diff --git a/app/build.gradle b/app/build.gradle index 71d2696b3..d492fb5cb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -92,6 +92,4 @@ dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2' testImplementation 'junit:junit:4.13' - testImplementation 'androidx.test:core:1.2.0' - testImplementation 'org.mockito:mockito-core:2.23.0' } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/SourceConfig.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/SourceConfig.kt index 32a906d93..49995eae4 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/SourceConfig.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/SourceConfig.kt @@ -4,14 +4,26 @@ import android.content.Context import org.koitharu.kotatsu.R 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) - ?.takeUnless(String::isBlank) - ?: defaultValue + private val prefs = context.getSharedPreferences(source.name, Context.MODE_PRIVATE) + 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) + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/domain/MangaLoaderContext.kt b/app/src/main/java/org/koitharu/kotatsu/domain/MangaLoaderContext.kt index e2dc53753..583e8775b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/domain/MangaLoaderContext.kt +++ b/app/src/main/java/org/koitharu/kotatsu/domain/MangaLoaderContext.kt @@ -11,7 +11,7 @@ import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.prefs.SourceConfig import org.koitharu.kotatsu.utils.ext.await -class MangaLoaderContext : KoinComponent { +open class MangaLoaderContext : KoinComponent { private val okHttp by inject() @@ -43,5 +43,5 @@ class MangaLoaderContext : KoinComponent { return okHttp.newCall(request.build()).await() } - fun getSettings(source: MangaSource) = SourceConfig(get(), source) + open fun getSettings(source: MangaSource) = SourceConfig(get(), source) } \ No newline at end of file diff --git a/app/src/test/java/org/koitharu/kotatsu/parsers/RemoteRepositoryTest.kt b/app/src/test/java/org/koitharu/kotatsu/parsers/RemoteRepositoryTest.kt new file mode 100644 index 000000000..69dee4167 --- /dev/null +++ b/app/src/test/java/org/koitharu/kotatsu/parsers/RemoteRepositoryTest.kt @@ -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 { + object : MangaLoaderContext() { + override fun getSettings(source: MangaSource): SourceConfig { + return SourceConfigMock() + } + } + } + } + )) + } + } + + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun getProviders() = (MangaSource.values().toList() - MangaSource.LOCAL).toTypedArray() + } +} \ No newline at end of file diff --git a/app/src/test/java/org/koitharu/kotatsu/parsers/RepositoryTestEnvironment.kt b/app/src/test/java/org/koitharu/kotatsu/parsers/RepositoryTestEnvironment.kt deleted file mode 100644 index b2f71eca1..000000000 --- a/app/src/test/java/org/koitharu/kotatsu/parsers/RepositoryTestEnvironment.kt +++ /dev/null @@ -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() } -} \ No newline at end of file diff --git a/app/src/test/java/org/koitharu/kotatsu/parsers/SourceConfigMock.kt b/app/src/test/java/org/koitharu/kotatsu/parsers/SourceConfigMock.kt new file mode 100644 index 000000000..834b3a5f1 --- /dev/null +++ b/app/src/test/java/org/koitharu/kotatsu/parsers/SourceConfigMock.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/test/java/org/koitharu/kotatsu/parsers/repository/MintMangaTest.kt b/app/src/test/java/org/koitharu/kotatsu/parsers/repository/MintMangaTest.kt deleted file mode 100644 index bf0376dbd..000000000 --- a/app/src/test/java/org/koitharu/kotatsu/parsers/repository/MintMangaTest.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file diff --git a/app/src/test/java/org/koitharu/kotatsu/parsers/repository/ReadmangaRuTest.kt b/app/src/test/java/org/koitharu/kotatsu/parsers/repository/ReadmangaRuTest.kt deleted file mode 100644 index 0fdb7f0a9..000000000 --- a/app/src/test/java/org/koitharu/kotatsu/parsers/repository/ReadmangaRuTest.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file diff --git a/app/src/test/java/org/koitharu/kotatsu/parsers/repository/SelfMangaTest.kt b/app/src/test/java/org/koitharu/kotatsu/parsers/repository/SelfMangaTest.kt deleted file mode 100644 index 6a528053f..000000000 --- a/app/src/test/java/org/koitharu/kotatsu/parsers/repository/SelfMangaTest.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file diff --git a/app/src/test/java/org/koitharu/kotatsu/utils/AssertX.kt b/app/src/test/java/org/koitharu/kotatsu/utils/AssertX.kt index 10f597b29..bc9f60fbc 100644 --- a/app/src/test/java/org/koitharu/kotatsu/utils/AssertX.kt +++ b/app/src/test/java/org/koitharu/kotatsu/utils/AssertX.kt @@ -6,17 +6,23 @@ import java.net.URL object AssertX { - private val VALID_RESPONSE_CODES = arrayOf( - HttpURLConnection.HTTP_OK, - HttpURLConnection.HTTP_MOVED_PERM, - HttpURLConnection.HTTP_MOVED_TEMP - ) - - fun assertValidUrl(url: String) { + fun assertContentType(url: String, type: String, subtype: String? = null) { + Assert.assertFalse("URL is empty", url.isEmpty()) val cn = URL(url).openConnection() as HttpURLConnection + cn.requestMethod = "HEAD" cn.connect() - val code = cn.responseCode - Assert.assertTrue("Invalid response code $code", code in VALID_RESPONSE_CODES) + when (val code = cn.responseCode) { + 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") + } } } \ No newline at end of file