/* 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 . */ 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 { 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() } } } } }