195 lines
6.3 KiB
Kotlin
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()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|