mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-04 11:24:35 -05:00
* Non-Extension Index changes for 1.6 * Changelog * Minor fixes * Implement extension store * Test build fix * Docs * Simplify fetching manga and chapters * Use EMPTY JsonObject * Update docs/Configuring-Suwayomi‐Server.md Co-authored-by: Constantin Piber <59023762+cpiber@users.noreply.github.com> * Improve Fetch Extension Store * Fixes * Simplify deprecated isNsfw in SourceQuery * Simplify ContentRating in Source.kt * Simplify isNsfw in SourceType * No magic numbers for ContentRating, improves safety for future versions of extension api * Fix SearchTest * Lint * Lint * Optimize imports and fix unchecked cast warning * Proper extension store queries * Optimize import fixes * Add ContentRatingFilter * Improve extension store sync * fix: re-sync (#2121) * Lint * Add ExtenionStores to the fetchExtensions result since its possible for the stores to change. * Use a single version of ContentRating * Exclude ServerConfig.extensionStores from GraphQL * Use syncDbToPrefs in ExtensionStoreMutation * Optimize Imports * Update server/server-config/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt Co-authored-by: Constantin Piber <59023762+cpiber@users.noreply.github.com> * Remove replaceWith and add specific description for GQL APIs * Include OkHttp ZSTD * Update to latest Mihon extension lib * Fix latest Mihon Extension Lib * Lint * Optimize imports * Lint * Review fixes * Add a index to extesnion table store url * Lint --------- Co-authored-by: Constantin Piber <59023762+cpiber@users.noreply.github.com>
196 lines
8.3 KiB
Kotlin
196 lines
8.3 KiB
Kotlin
package masstest
|
|
|
|
/*
|
|
* Copyright (C) Contributors to the Suwayomi project
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
import android.os.Looper
|
|
import eu.kanade.tachiyomi.source.model.SChapter
|
|
import eu.kanade.tachiyomi.source.model.SManga
|
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
|
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 org.junit.jupiter.api.AfterAll
|
|
import org.junit.jupiter.api.BeforeAll
|
|
import org.junit.jupiter.api.Test
|
|
import org.junit.jupiter.api.TestInstance
|
|
import org.koin.core.context.stopKoin
|
|
import suwayomi.tachidesk.manga.impl.Source.getSourceList
|
|
import suwayomi.tachidesk.manga.impl.extension.Extension.installExtension
|
|
import suwayomi.tachidesk.manga.impl.extension.Extension.uninstallExtension
|
|
import suwayomi.tachidesk.manga.impl.extension.Extension.updateExtension
|
|
import suwayomi.tachidesk.manga.impl.extension.ExtensionsList.getExtensionList
|
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrNull
|
|
import suwayomi.tachidesk.manga.model.dataclass.ExtensionDataClass
|
|
import suwayomi.tachidesk.server.applicationSetup
|
|
import suwayomi.tachidesk.server.settings.SettingsRegistry
|
|
import suwayomi.tachidesk.test.BASE_PATH
|
|
import suwayomi.tachidesk.test.setLoggingEnabled
|
|
import xyz.nulldev.ts.config.CONFIG_PREFIX
|
|
import java.io.File
|
|
import java.util.concurrent.atomic.AtomicInteger
|
|
|
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
|
class TestExtensionCompatibility {
|
|
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 chaptersPageListFailedToFetch = mutableListOf<Triple<HttpSource, Pair<SManga, SChapter>, Exception>>()
|
|
|
|
@BeforeAll
|
|
fun setup() {
|
|
val dataRoot = File(BASE_PATH).absolutePath
|
|
System.setProperty("$CONFIG_PREFIX.server.rootDir", dataRoot)
|
|
Looper.clearMainLooperForTest()
|
|
SettingsRegistry.clear()
|
|
applicationSetup()
|
|
setLoggingEnabled(false)
|
|
|
|
runBlocking {
|
|
extensions = getExtensionList()
|
|
extensions.forEach {
|
|
when {
|
|
it.obsolete -> {
|
|
uninstallExtension(it.pkgName)
|
|
}
|
|
|
|
it.hasUpdate -> {
|
|
updateExtension(it.pkgName)
|
|
}
|
|
|
|
else -> {
|
|
uninstallExtension(it.pkgName)
|
|
installExtension(it.pkgName)
|
|
}
|
|
}
|
|
}
|
|
sources =
|
|
getSourceList()
|
|
.filter {
|
|
// filter local source
|
|
it.id.toLong() != 0L
|
|
}.map { getCatalogueSourceOrNull(it.id.toLong())!! as HttpSource }
|
|
}
|
|
setLoggingEnabled(true)
|
|
File("$BASE_PATH/sources.txt").writeText(sources.joinToString("\n") { "${it.name} - ${it.lang.uppercase()} - ${it.id}" })
|
|
}
|
|
|
|
@AfterAll
|
|
fun teardown() {
|
|
stopKoin()
|
|
}
|
|
|
|
@Test
|
|
fun runTest() {
|
|
runBlocking(Dispatchers.Default) {
|
|
val semaphore = Semaphore(10)
|
|
val popularCount = AtomicInteger(1)
|
|
sources
|
|
.map { source ->
|
|
async {
|
|
semaphore.withPermit {
|
|
logger.info { "${popularCount.getAndIncrement()} - Now fetching popular manga from $source" }
|
|
try {
|
|
mangaToFetch += source to (
|
|
repeat { source.getPopularManga(1) }
|
|
.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("$BASE_PATH/failedToFetch.txt").writeText(
|
|
failedToFetch.joinToString("\n") { (source, exception) ->
|
|
"${source.name} (${source.lang.uppercase()}, ${source.id}):" +
|
|
" ${exception.message}"
|
|
},
|
|
)
|
|
logger.info { "Now fetching manga info from ${mangaToFetch.size} sources" }
|
|
|
|
val mangaCount = AtomicInteger(1)
|
|
mangaToFetch
|
|
.map { (source, manga) ->
|
|
async {
|
|
semaphore.withPermit {
|
|
logger.info { "${mangaCount.getAndIncrement()} - Now fetching manga from $source" }
|
|
try {
|
|
repeat { source.getMangaUpdate(manga, emptyList(), true, true) }
|
|
} catch (e: Exception) {
|
|
logger.warn {
|
|
"Failed to fetch manga info and chapters from $source for ${manga.title} (${source.mangaDetailsRequest(
|
|
manga,
|
|
).url}): ${e.message}"
|
|
}
|
|
mangaFailedToFetch += Triple(source, manga, e)
|
|
}
|
|
}
|
|
}
|
|
}.awaitAll()
|
|
File("$BASE_PATH/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" }
|
|
|
|
val pageListCount = AtomicInteger(1)
|
|
chaptersToFetch
|
|
.map { (source, manga, chapter) ->
|
|
async {
|
|
semaphore.withPermit {
|
|
logger.info { "${pageListCount.getAndIncrement()} - Now fetching page list from $source" }
|
|
try {
|
|
repeat { source.getPageList(chapter) }
|
|
} 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("$BASE_PATH/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> repeat(block: suspend () -> T): T {
|
|
for (i in 1..2) {
|
|
try {
|
|
return block()
|
|
} catch (e: Exception) {
|
|
}
|
|
}
|
|
return block()
|
|
}
|
|
}
|