Manga parsers unit tests

This commit is contained in:
Koitharu
2020-03-15 15:19:32 +02:00
parent c765e03b28
commit f0b3400103
10 changed files with 157 additions and 254 deletions

View File

@@ -92,6 +92,4 @@ dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13'
testImplementation 'androidx.test:core:1.2.0'
testImplementation 'org.mockito:mockito-core:2.23.0'
} }

View File

@@ -4,14 +4,26 @@ import android.content.Context
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSource
class SourceConfig(context: Context, source: MangaSource) { interface SourceConfig {
private val prefs = context.getSharedPreferences(source.name, Context.MODE_PRIVATE) fun getDomain(defaultValue: String): String
private val keyDomain = context.getString(R.string.key_parser_domain) private class PrefSourceConfig(context: Context, source: MangaSource) : SourceConfig {
fun getDomain(defaultValue: String) = prefs.getString(keyDomain, defaultValue) private val prefs = context.getSharedPreferences(source.name, Context.MODE_PRIVATE)
?.takeUnless(String::isBlank)
?: defaultValue
private val keyDomain = context.getString(R.string.key_parser_domain)
override fun getDomain(defaultValue: String) = prefs.getString(keyDomain, defaultValue)
?.takeUnless(String::isBlank)
?: defaultValue
}
companion object {
@JvmStatic
operator fun invoke(context: Context, source: MangaSource): SourceConfig =
PrefSourceConfig(context, source)
}
} }

View File

@@ -11,7 +11,7 @@ import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.prefs.SourceConfig import org.koitharu.kotatsu.core.prefs.SourceConfig
import org.koitharu.kotatsu.utils.ext.await import org.koitharu.kotatsu.utils.ext.await
class MangaLoaderContext : KoinComponent { open class MangaLoaderContext : KoinComponent {
private val okHttp by inject<OkHttpClient>() private val okHttp by inject<OkHttpClient>()
@@ -43,5 +43,5 @@ class MangaLoaderContext : KoinComponent {
return okHttp.newCall(request.build()).await() return okHttp.newCall(request.build()).await()
} }
fun getSettings(source: MangaSource) = SourceConfig(get(), source) open fun getSettings(source: MangaSource) = SourceConfig(get(), source)
} }

View File

@@ -0,0 +1,114 @@
package org.koitharu.kotatsu.parsers
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import org.junit.Assert
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.koin.core.context.startKoin
import org.koin.dsl.module
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.prefs.SourceConfig
import org.koitharu.kotatsu.domain.MangaLoaderContext
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.utils.AssertX
import java.util.concurrent.TimeUnit
@RunWith(Parameterized::class)
class RemoteRepositoryTest(source: MangaSource) {
private val repo = MangaProviderFactory.create(source)
@Test
fun getList() {
val list = runBlocking { repo.getList(60) }
Assert.assertFalse(list.isEmpty())
val item = list.random()
AssertX.assertContentType(item.coverUrl, "image")
AssertX.assertContentType(item.url, "text", "html")
Assert.assertFalse(item.title.isBlank())
}
@Test
fun search() {
val list = runBlocking { repo.getList(0, query = "tail") }
Assert.assertFalse(list.isEmpty())
val item = list.random()
AssertX.assertContentType(item.coverUrl, "image")
AssertX.assertContentType(item.url, "text", "html")
Assert.assertFalse(item.title.isBlank())
}
@Test
fun getTags() {
val tags = runBlocking { repo.getTags() }
Assert.assertFalse(tags.isEmpty())
val tag = tags.random()
Assert.assertFalse(tag.key.isBlank())
Assert.assertFalse(tag.title.isBlank())
val list = runBlocking { repo.getList(0, tag = tag) }
Assert.assertFalse(list.isEmpty())
val item = list.random()
AssertX.assertContentType(item.coverUrl, "image")
AssertX.assertContentType(item.url, "text", "html")
Assert.assertFalse(item.title.isBlank())
}
@Test
fun getDetails() {
val manga = runBlocking { repo.getList(0) }.random()
val details = runBlocking { repo.getDetails(manga) }
Assert.assertFalse(details.chapters.isNullOrEmpty())
Assert.assertFalse(details.description.isNullOrEmpty())
val chapter = details.chapters!!.random()
Assert.assertFalse(chapter.name.isBlank())
AssertX.assertContentType(chapter.url, "text", "html")
}
@Test
fun getPages() {
val manga = runBlocking { repo.getList(0) }.random()
val details = runBlocking { repo.getDetails(manga) }
val pages = runBlocking { repo.getPages(details.chapters!!.random()) }
Assert.assertFalse(pages.isEmpty())
val page = pages.random()
val fullUrl = runBlocking { repo.getPageFullUrl(page) }
AssertX.assertContentType(fullUrl, "image")
}
companion object {
@JvmStatic
@BeforeClass
fun initialize() {
startKoin {
modules(listOf(
module {
factory {
OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.build()
}
},
module {
single<MangaLoaderContext> {
object : MangaLoaderContext() {
override fun getSettings(source: MangaSource): SourceConfig {
return SourceConfigMock()
}
}
}
}
))
}
}
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun getProviders() = (MangaSource.values().toList() - MangaSource.LOCAL).toTypedArray()
}
}

View File

@@ -1,37 +0,0 @@
package org.koitharu.kotatsu.parsers
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import org.junit.BeforeClass
import org.koin.core.context.startKoin
import org.koin.dsl.module
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.domain.MangaLoaderContext
abstract class RepositoryTestEnvironment {
lateinit var repository: MangaRepository
@BeforeClass
fun initialize(source: MangaSource) {
startKoin {
module {
factory {
OkHttpClient()
}
single {
MangaLoaderContext()
}
}
}
val constructor = source.cls.getConstructor(MangaLoaderContext::class.java)
repository = constructor.newInstance(MangaLoaderContext())
}
fun getMangaList() = runBlocking { repository.getList(2) }
fun getMangaItem() = runBlocking { repository.getDetails(repository.getList(5).last()) }
fun getTags() = runBlocking { repository.getTags() }
}

View File

@@ -0,0 +1,8 @@
package org.koitharu.kotatsu.parsers
import org.koitharu.kotatsu.core.prefs.SourceConfig
class SourceConfigMock : SourceConfig {
override fun getDomain(defaultValue: String) = defaultValue
}

View File

@@ -1,66 +0,0 @@
package org.koitharu.kotatsu.parsers.repository
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.parsers.MangaParserTest
import org.koitharu.kotatsu.parsers.RepositoryTestEnvironment
import org.koitharu.kotatsu.utils.AssertX
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
class MintMangaTest : MangaParserTest {
@Test
override fun testMangaList() {
val list = getMangaList()
Assert.assertTrue(list.size == 70)
val item = list[40]
Assert.assertTrue(item.title.isNotEmpty())
Assert.assertTrue(item.rating in 0f..1f)
AssertX.assertValidUrl(item.url)
AssertX.assertValidUrl(item.coverUrl)
Assert.assertEquals(item.source, MangaSource.MINTMANGA)
}
@Test
override fun testMangaDetails() {
val manga = getMangaItem()
Assert.assertNotNull(manga.largeCoverUrl)
AssertX.assertValidUrl(manga.largeCoverUrl!!)
Assert.assertNotNull(manga.chapters)
val chapter = manga.chapters!!.last()
Assert.assertEquals(chapter.source, MangaSource.MINTMANGA)
AssertX.assertValidUrl(chapter.url)
}
@Test
override fun testMangaPages() {
val chapter = getMangaItem().chapters!!.first()
val pages = runBlocking { repository.getPages(chapter) }
Assert.assertFalse(pages.isEmpty())
Assert.assertEquals(pages.first().source, MangaSource.MINTMANGA)
AssertX.assertValidUrl(runBlocking { repository.getPageFullUrl(pages.first()) })
AssertX.assertValidUrl(runBlocking { repository.getPageFullUrl(pages.last()) })
}
@Test
override fun testTags() {
val tags = getTags()
Assert.assertFalse(tags.isEmpty())
val tag = tags.first()
Assert.assertFalse(tag.title.isBlank())
Assert.assertEquals(tag.source, MangaSource.MINTMANGA)
AssertX.assertValidUrl("https://mintmanga.live/list/genre/${tag.key}")
}
companion object : RepositoryTestEnvironment() {
@JvmStatic
@BeforeClass
fun setUp() = initialize(MangaSource.MINTMANGA)
}
}

View File

@@ -1,66 +0,0 @@
package org.koitharu.kotatsu.parsers.repository
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.parsers.MangaParserTest
import org.koitharu.kotatsu.parsers.RepositoryTestEnvironment
import org.koitharu.kotatsu.utils.AssertX
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
class ReadmangaRuTest : MangaParserTest {
@Test
override fun testMangaList() {
val list = getMangaList()
Assert.assertTrue(list.size == 70)
val item = list[40]
Assert.assertTrue(item.title.isNotEmpty())
Assert.assertTrue(item.rating in 0f..1f)
AssertX.assertValidUrl(item.url)
AssertX.assertValidUrl(item.coverUrl)
Assert.assertEquals(item.source, MangaSource.READMANGA_RU)
}
@Test
override fun testMangaDetails() {
val manga = getMangaItem()
Assert.assertNotNull(manga.largeCoverUrl)
AssertX.assertValidUrl(manga.largeCoverUrl!!)
Assert.assertNotNull(manga.chapters)
val chapter = manga.chapters!!.last()
Assert.assertEquals(chapter.source, MangaSource.READMANGA_RU)
AssertX.assertValidUrl(chapter.url)
}
@Test
override fun testMangaPages() {
val chapter = getMangaItem().chapters!!.first()
val pages = runBlocking { repository.getPages(chapter) }
Assert.assertFalse(pages.isEmpty())
Assert.assertEquals(pages.first().source, MangaSource.READMANGA_RU)
AssertX.assertValidUrl(runBlocking { repository.getPageFullUrl(pages.first()) })
AssertX.assertValidUrl(runBlocking { repository.getPageFullUrl(pages.last()) })
}
@Test
override fun testTags() {
val tags = getTags()
Assert.assertFalse(tags.isEmpty())
val tag = tags.first()
Assert.assertFalse(tag.title.isBlank())
Assert.assertEquals(tag.source, MangaSource.READMANGA_RU)
AssertX.assertValidUrl("https://readmanga.me/list/genre/${tag.key}")
}
companion object : RepositoryTestEnvironment() {
@JvmStatic
@BeforeClass
fun setUp() = initialize(MangaSource.READMANGA_RU)
}
}

View File

@@ -1,66 +0,0 @@
package org.koitharu.kotatsu.parsers.repository
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.parsers.MangaParserTest
import org.koitharu.kotatsu.parsers.RepositoryTestEnvironment
import org.koitharu.kotatsu.utils.AssertX
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
class SelfMangaTest : MangaParserTest {
@Test
override fun testMangaList() {
val list = getMangaList()
Assert.assertTrue(list.size == 70)
val item = list[40]
Assert.assertTrue(item.title.isNotEmpty())
Assert.assertTrue(item.rating in 0f..1f)
AssertX.assertValidUrl(item.url)
AssertX.assertValidUrl(item.coverUrl)
Assert.assertEquals(item.source, MangaSource.SELFMANGA)
}
@Test
override fun testMangaDetails() {
val manga = getMangaItem()
Assert.assertNotNull(manga.largeCoverUrl)
AssertX.assertValidUrl(manga.largeCoverUrl!!)
Assert.assertNotNull(manga.chapters)
val chapter = manga.chapters!!.last()
Assert.assertEquals(chapter.source, MangaSource.SELFMANGA)
AssertX.assertValidUrl(chapter.url)
}
@Test
override fun testMangaPages() {
val chapter = getMangaItem().chapters!!.first()
val pages = runBlocking { repository.getPages(chapter) }
Assert.assertFalse(pages.isEmpty())
Assert.assertEquals(pages.first().source, MangaSource.SELFMANGA)
AssertX.assertValidUrl(runBlocking { repository.getPageFullUrl(pages.first()) })
AssertX.assertValidUrl(runBlocking { repository.getPageFullUrl(pages.last()) })
}
@Test
override fun testTags() {
val tags = getTags()
Assert.assertFalse(tags.isEmpty())
val tag = tags.first()
Assert.assertFalse(tag.title.isBlank())
Assert.assertEquals(tag.source, MangaSource.SELFMANGA)
AssertX.assertValidUrl("https://selfmanga.ru/list/genre/${tag.key}")
}
companion object : RepositoryTestEnvironment() {
@JvmStatic
@BeforeClass
fun setUp() = initialize(MangaSource.SELFMANGA)
}
}

View File

@@ -6,17 +6,23 @@ import java.net.URL
object AssertX { object AssertX {
private val VALID_RESPONSE_CODES = arrayOf( fun assertContentType(url: String, type: String, subtype: String? = null) {
HttpURLConnection.HTTP_OK, Assert.assertFalse("URL is empty", url.isEmpty())
HttpURLConnection.HTTP_MOVED_PERM,
HttpURLConnection.HTTP_MOVED_TEMP
)
fun assertValidUrl(url: String) {
val cn = URL(url).openConnection() as HttpURLConnection val cn = URL(url).openConnection() as HttpURLConnection
cn.requestMethod = "HEAD"
cn.connect() cn.connect()
val code = cn.responseCode when (val code = cn.responseCode) {
Assert.assertTrue("Invalid response code $code", code in VALID_RESPONSE_CODES) HttpURLConnection.HTTP_MOVED_PERM,
HttpURLConnection.HTTP_MOVED_TEMP -> assertContentType(cn.getHeaderField("Location"), type, subtype)
HttpURLConnection.HTTP_OK -> {
val ct = cn.contentType.substringBeforeLast(';').split("/")
Assert.assertEquals(type, ct.first())
if (subtype != null) {
Assert.assertEquals(subtype, ct.last())
}
}
else -> Assert.fail("Invalid response code $code")
}
} }
} }