Add initial testing suit
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
package ir.armor.tachidesk
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import ir.armor.tachidesk.impl.Extension.installExtension
|
||||
import ir.armor.tachidesk.impl.Extension.uninstallExtension
|
||||
import ir.armor.tachidesk.impl.Extension.updateExtension
|
||||
import ir.armor.tachidesk.impl.ExtensionsList.getExtensionList
|
||||
import ir.armor.tachidesk.impl.Source.getSourceList
|
||||
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||
import ir.armor.tachidesk.impl.util.awaitSingle
|
||||
import ir.armor.tachidesk.model.dataclass.ExtensionDataClass
|
||||
import ir.armor.tachidesk.server.applicationSetup
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import mu.KotlinLogging
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import rx.Observable
|
||||
import java.io.File
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class TestExtensions {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
private lateinit var extensions: List<ExtensionDataClass>
|
||||
private lateinit var sources: List<HttpSource>
|
||||
|
||||
private val mangaToFetch = mutableListOf<Pair<HttpSource, SManga>>()
|
||||
private val failedToFetch = mutableListOf<Pair<HttpSource, Exception>>()
|
||||
private val mangaFailedToFetch = mutableListOf<Triple<HttpSource, SManga, Exception>>()
|
||||
private val chaptersToFetch = mutableListOf<Triple<HttpSource, SManga, SChapter>>()
|
||||
private val chaptersFailedToFetch = mutableListOf<Triple<HttpSource, SManga, Throwable>>()
|
||||
private val chaptersPageListFailedToFetch = mutableListOf<Triple<HttpSource, Pair<SManga, SChapter>, Exception>>()
|
||||
|
||||
@BeforeAll
|
||||
fun setup() {
|
||||
val dataRoot = File("tmp/TestDesk").absolutePath
|
||||
applicationSetup(dataRoot, loadConfigs(dataRoot))
|
||||
setLoggingEnabled(false)
|
||||
runBlocking {
|
||||
extensions = getExtensionList()
|
||||
extensions.forEach {
|
||||
when {
|
||||
it.obsolete -> {
|
||||
uninstallExtension(it.pkgName)
|
||||
}
|
||||
it.hasUpdate -> {
|
||||
updateExtension(it.pkgName)
|
||||
}
|
||||
else -> {
|
||||
installExtension(it.pkgName)
|
||||
}
|
||||
}
|
||||
}
|
||||
sources = getSourceList().map { getHttpSource(it.id) }
|
||||
}
|
||||
setLoggingEnabled(true)
|
||||
File("tmp/TestDesk/sources.txt").writeText(sources.joinToString("\n") { "${it.name} - ${it.lang.toUpperCase()} - ${it.id}" })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun runTest() {
|
||||
runBlocking(Dispatchers.Default) {
|
||||
val semaphore = Semaphore(10)
|
||||
sources.mapIndexed { index, source ->
|
||||
async {
|
||||
semaphore.withPermit {
|
||||
logger.info { "$index - Now fetching popular manga from $source" }
|
||||
try {
|
||||
mangaToFetch += source to (source.fetchPopularManga(1)
|
||||
.awaitSingleRepeat().mangas.firstOrNull()
|
||||
?: throw Exception("Source returned no manga"))
|
||||
} catch (e: Exception) {
|
||||
logger.warn { "Failed to fetch popular manga from $source: ${e.message}" }
|
||||
failedToFetch += source to e
|
||||
}
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
File("tmp/TestDesk/failedToFetch.txt").writeText(
|
||||
failedToFetch.joinToString("\n") { (source, exception) ->
|
||||
"${source.name} (${source.lang.toUpperCase()}, ${source.id}):" +
|
||||
" ${exception.message}"
|
||||
}
|
||||
)
|
||||
logger.info { "Now fetching manga info from ${mangaToFetch.size} sources" }
|
||||
|
||||
mangaToFetch.mapIndexed { index, (source, manga) ->
|
||||
async {
|
||||
semaphore.withPermit {
|
||||
logger.info { "$index - Now fetching manga from $source" }
|
||||
try {
|
||||
manga.copyFrom(source.fetchMangaDetails(manga).awaitSingleRepeat())
|
||||
manga.initialized = true
|
||||
} catch (e: Exception) {
|
||||
logger.warn {
|
||||
"Failed to fetch manga info from $source for ${manga.title} (${source.mangaDetailsRequest(manga).url}): ${e.message}"
|
||||
}
|
||||
mangaFailedToFetch += Triple(source, manga, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
File("tmp/TestDesk/MangaFailedToFetch.txt").writeText(
|
||||
mangaFailedToFetch.joinToString("\n") { (source, manga, exception) ->
|
||||
"${source.name} (${source.lang}, ${source.id}):" +
|
||||
" ${manga.title} (${source.mangaDetailsRequest(manga).url}):" +
|
||||
" ${exception.message}"
|
||||
}
|
||||
)
|
||||
logger.info { "Now fetching manga chapters from ${mangaToFetch.size} sources" }
|
||||
|
||||
mangaToFetch.filter { it.second.initialized }.mapIndexed { index, (source, manga) ->
|
||||
async {
|
||||
semaphore.withPermit {
|
||||
logger.info { "$index - Now fetching manga chapters from $source" }
|
||||
try {
|
||||
chaptersToFetch += Triple(
|
||||
source,
|
||||
manga,
|
||||
source.fetchChapterList(manga).awaitSingleRepeat().firstOrNull() ?: throw Exception("Source returned no chapters")
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logger.warn {
|
||||
"Failed to fetch manga chapters from $source for ${manga.title} (${source.mangaDetailsRequest(manga).url}): ${e.message}"
|
||||
}
|
||||
chaptersFailedToFetch += Triple(source, manga, e)
|
||||
} catch (e: NoClassDefFoundError) {
|
||||
logger.warn {
|
||||
"Failed to fetch manga chapters from $source for ${manga.title} (${source.mangaDetailsRequest(manga).url}): ${e.message}"
|
||||
}
|
||||
chaptersFailedToFetch += Triple(source, manga, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
|
||||
File("tmp/TestDesk/ChaptersFailedToFetch.txt").writeText(
|
||||
chaptersFailedToFetch.joinToString("\n") { (source, manga, exception) ->
|
||||
"${source.name} (${source.lang}, ${source.id}):" +
|
||||
" ${manga.title} (${source.mangaDetailsRequest(manga).url}):" +
|
||||
" ${exception.message}"
|
||||
}
|
||||
)
|
||||
|
||||
chaptersToFetch.mapIndexed { index, (source, manga, chapter) ->
|
||||
async {
|
||||
semaphore.withPermit {
|
||||
logger.info { "$index - Now fetching page list from $source" }
|
||||
try {
|
||||
source.fetchPageList(chapter).awaitSingleRepeat()
|
||||
} catch (e: Exception) {
|
||||
logger.warn {
|
||||
"Failed to fetch manga info from $source for ${manga.title} (${source.mangaDetailsRequest(manga).url}): ${e.message}"
|
||||
}
|
||||
chaptersPageListFailedToFetch += Triple(source, manga to chapter, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
|
||||
File("tmp/TestDesk/ChapterPageListFailedToFetch.txt").writeText(
|
||||
chaptersPageListFailedToFetch.joinToString("\n") { (source, manga, exception) ->
|
||||
"${source.name} (${source.lang}, ${source.id}):" +
|
||||
" ${manga.first.title} (${source.mangaDetailsRequest(manga.first).url}):" +
|
||||
" ${manga.second.name} (${manga.second.url}): ${exception.message}"
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T> Observable<T>.awaitSingleRepeat(): T {
|
||||
for (i in 1..2) {
|
||||
try {
|
||||
return awaitSingle()
|
||||
} catch (e: Exception) {}
|
||||
}
|
||||
return awaitSingle()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package ir.armor.tachidesk
|
||||
|
||||
import ch.qos.logback.classic.Level
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigRenderOptions
|
||||
import mu.KotlinLogging
|
||||
import org.slf4j.Logger
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Load configs
|
||||
*/
|
||||
fun loadConfigs(dataRoot: String): Config {
|
||||
val logger = KotlinLogging.logger {}
|
||||
//Load reference configs
|
||||
val compatConfig = ConfigFactory.parseResources("compat-reference.conf")
|
||||
val serverConfig = ConfigFactory.parseResources("server-reference.conf")
|
||||
|
||||
//Load user config
|
||||
val userConfig =
|
||||
File(dataRoot, "server.conf").let {
|
||||
ConfigFactory.parseFile(it)
|
||||
}
|
||||
|
||||
val config = ConfigFactory.empty()
|
||||
.withFallback(userConfig)
|
||||
.withFallback(compatConfig)
|
||||
.withFallback(serverConfig)
|
||||
.resolve()
|
||||
|
||||
logger.debug {
|
||||
"Loaded config:\n" + config.root().render(ConfigRenderOptions.concise().setFormatted(true))
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
fun setLoggingEnabled(enabled: Boolean = true) {
|
||||
val logger = (KotlinLogging.logger(Logger.ROOT_LOGGER_NAME).underlyingLogger as ch.qos.logback.classic.Logger)
|
||||
logger.level = if (enabled) {
|
||||
Level.DEBUG
|
||||
} else Level.ERROR
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# Server ip and port bindings
|
||||
server.ip = "0.0.0.0"
|
||||
server.port = 4567
|
||||
|
||||
# Socks5 proxy
|
||||
server.socksProxy = false
|
||||
server.socksProxyHost = ""
|
||||
server.socksProxyPort = ""
|
||||
|
||||
# misc
|
||||
server.debugLogsEnabled = true
|
||||
server.systemTrayEnabled = false
|
||||
server.initialOpenInBrowserEnabled = true
|
||||
Reference in New Issue
Block a user