mangadex_at_home/src/test/kotlin/mdnet/cache/ImageStorageTest.kt

195 lines
6.3 KiB
Kotlin

/*
Mangadex@Home
Copyright (c) 2020, MangaDex Network
This file is part of MangaDex@Home.
MangaDex@Home is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
MangaDex@Home is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this MangaDex@Home. If not, see <http://www.gnu.org/licenses/>.
*/
package mdnet.cache
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.assertions.timing.eventually
import io.kotest.core.spec.IsolationMode
import io.kotest.core.spec.style.FreeSpec
import io.kotest.engine.spec.tempdir
import io.kotest.engine.spec.tempfile
import io.kotest.matchers.booleans.shouldBeTrue
import io.kotest.matchers.longs.shouldBeGreaterThan
import io.kotest.matchers.longs.shouldBeZero
import io.kotest.matchers.nulls.shouldBeNull
import io.kotest.matchers.nulls.shouldNotBeNull
import io.kotest.matchers.shouldBe
import org.apache.commons.io.IOUtils
import org.ktorm.database.Database
import kotlin.time.ExperimentalTime
import kotlin.time.minutes
class ImageStorageTest : FreeSpec() {
override fun isolationMode() = IsolationMode.InstancePerTest
init {
val imageStorage = ImageStorage(
maxSize = 5,
cacheDirectory = tempdir().toPath(),
database = Database.connect("jdbc:h2:${tempfile()}"),
autoPrune = false,
)
val testMeta = ImageMetadata("a", "a", 123)
"storeImage()" - {
"should throw exception when length too short" {
for (i in listOf("", "a", "aa")) {
shouldThrow<IllegalArgumentException> {
imageStorage.storeImage(i, testMeta)
}
}
}
"when writer committed" - {
val writer = imageStorage.storeImage("test", testMeta)
writer.shouldNotBeNull()
writer.stream.write(ByteArray(12))
writer.commit(12).shouldBeTrue()
"should not update size until calculated" {
imageStorage.size.shouldBeZero()
}
"should update size when calculated" {
imageStorage.calculateSize()
imageStorage.size.shouldBeGreaterThan(0)
}
}
"when writer aborted" - {
val writer = imageStorage.storeImage("test", testMeta)
writer.shouldNotBeNull()
writer.stream.write(ByteArray(12))
writer.abort()
"should not update size" {
imageStorage.size.shouldBeZero()
}
}
}
"loadImage()" - {
"should load committed data" - {
val data = byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val writer = imageStorage.storeImage("test", testMeta)
writer.shouldNotBeNull()
writer.stream.write(data)
writer.commit(data.size).shouldBeTrue()
val image = imageStorage.loadImage("test")
image.shouldNotBeNull()
image.data.shouldBe(testMeta)
IOUtils.toByteArray(image.stream).shouldBe(data)
}
"should not load aborted data" {
val data = byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val writer = imageStorage.storeImage("test", testMeta)
writer.shouldNotBeNull()
writer.stream.write(data)
writer.abort()
val image = imageStorage.loadImage("test")
image.shouldBeNull()
}
}
"pruneImage()" - {
"should prune if insufficient size" {
val writer = imageStorage.storeImage("test", testMeta)
writer.shouldNotBeNull()
writer.stream.write(ByteArray(12))
writer.commit(12).shouldBeTrue()
imageStorage.calculateSize()
imageStorage.size.shouldBeGreaterThan(0)
imageStorage.pruneImages()
imageStorage.calculateSize()
imageStorage.size.shouldBeZero()
}
"should not prune if enough size" {
imageStorage.maxSize = 10000
val writer = imageStorage.storeImage("test", testMeta)
writer.shouldNotBeNull()
writer.stream.write(ByteArray(12))
writer.commit(12).shouldBeTrue()
imageStorage.calculateSize()
imageStorage.size.shouldBeGreaterThan(0)
imageStorage.pruneImages()
imageStorage.calculateSize()
imageStorage.size.shouldBeGreaterThan(0)
}
}
}
}
@ExperimentalTime
class ImageStorageSlowTest : FreeSpec() {
override fun isolationMode() = IsolationMode.InstancePerTest
init {
val imageStorage = ImageStorage(
maxSize = 4097,
cacheDirectory = tempdir().toPath(),
database = Database.connect("jdbc:h2:${tempfile()}"),
)
"autoPrune" - {
"should update size eventually" {
val writer = imageStorage.storeImage("test", ImageMetadata("a", "a", 4096))
writer.shouldNotBeNull()
writer.stream.write(ByteArray(4096))
writer.commit(4096).shouldBeTrue()
eventually(5.minutes) {
imageStorage.size.shouldBeGreaterThan(0)
}
}
"should prune if insufficient size eventually" {
imageStorage.maxSize = 10000
val writer = imageStorage.storeImage("test", ImageMetadata("a", "a", 123))
writer.shouldNotBeNull()
writer.stream.write(ByteArray(8192))
writer.commit(8192).shouldBeTrue()
eventually(5.minutes) {
imageStorage.size.shouldBeZero()
}
}
}
}
}