diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index be8d0d34a0..ebda14e825 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -5,6 +5,10 @@ on: - '**.md' - 'app/src/main/res/**/strings.xml' +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + permissions: contents: read @@ -21,7 +25,7 @@ jobs: uses: gradle/wrapper-validation-action@v1 - name: Dependency Review - uses: actions/dependency-review-action@v1 + uses: actions/dependency-review-action@v2 - name: Set up JDK 11 uses: actions/setup-java@v3 @@ -29,7 +33,7 @@ jobs: java-version: 11 distribution: adopt - - name: Build app + - name: Build app and run unit tests uses: gradle/gradle-command-action@v2 with: - arguments: assembleStandardRelease + arguments: assembleStandardRelease testStandardReleaseUnitTest \ No newline at end of file diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 843673ba06..192116fc87 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -6,18 +6,16 @@ on: tags: - v* +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + jobs: build: name: Build app runs-on: ubuntu-latest steps: - - name: Cancel previous runs - uses: styfle/cancel-workflow-action@0.9.1 - with: - access_token: ${{ github.token }} - all_but_latest: true - - name: Clone repo uses: actions/checkout@v3 @@ -30,10 +28,10 @@ jobs: java-version: 11 distribution: adopt - - name: Build app + - name: Build app and run unit tests uses: gradle/gradle-command-action@v2 with: - arguments: assembleStandardRelease + arguments: assembleStandardRelease testStandardReleaseUnitTest # Sign APK and create release for tags diff --git a/.github/workflows/cancel_pull_request.yml b/.github/workflows/cancel_pull_request.yml deleted file mode 100644 index 82d572614c..0000000000 --- a/.github/workflows/cancel_pull_request.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Cancel old pull request workflows - -on: - workflow_run: - workflows: ["PR build check"] - types: - - requested - -jobs: - cancel: - runs-on: ubuntu-latest - steps: - - uses: styfle/cancel-workflow-action@0.9.1 - with: - all_but_latest: true - workflow_id: ${{ github.event.workflow.id }} diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 809186baa4..4ab6582389 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,4 @@ +import org.gradle.api.tasks.testing.logging.TestLogEvent import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -242,16 +243,19 @@ dependencies { // Tests testImplementation(libs.junit) - testImplementation(libs.assertj.core) - testImplementation(libs.mockito.core) - - testImplementation(libs.bundles.robolectric) // For detecting memory leaks; see https://square.github.io/leakcanary/ // debugImplementation(libs.leakcanary.android) } tasks { + withType { + useJUnitPlatform() + testLogging { + events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) + } + } + // See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers) withType { kotlinOptions.freeCompilerArgs += listOf( diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterRecognition.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterRecognition.kt index dd44f42be8..f9dfb076e7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterRecognition.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterRecognition.kt @@ -46,8 +46,8 @@ object ChapterRecognition { // Get chapter title with lower case var name = chapter.name.lowercase() - // Remove comma's from chapter. - name = name.replace(',', '.') + // Remove comma's or hyphens. + name = name.replace(',', '.').replace('-', '.') // Remove unwanted white spaces. unwantedWhiteSpace.findAll(name).let { diff --git a/app/src/test/java/eu/kanade/tachiyomi/CustomRobolectricGradleTestRunner.kt b/app/src/test/java/eu/kanade/tachiyomi/CustomRobolectricGradleTestRunner.kt deleted file mode 100644 index 3b334be41b..0000000000 --- a/app/src/test/java/eu/kanade/tachiyomi/CustomRobolectricGradleTestRunner.kt +++ /dev/null @@ -1,12 +0,0 @@ -package eu.kanade.tachiyomi - -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config -import org.robolectric.manifest.AndroidManifest - -class CustomRobolectricGradleTestRunner(klass: Class<*>) : RobolectricTestRunner(klass) { - - override fun getAppManifest(config: Config): AndroidManifest { - return super.getAppManifest(config).apply { packageName = "eu.kanade.tachiyomi" } - } -} diff --git a/app/src/test/java/eu/kanade/tachiyomi/TestApp.kt b/app/src/test/java/eu/kanade/tachiyomi/TestApp.kt deleted file mode 100644 index 35a74ceb22..0000000000 --- a/app/src/test/java/eu/kanade/tachiyomi/TestApp.kt +++ /dev/null @@ -1,8 +0,0 @@ -package eu.kanade.tachiyomi - -open class TestApp : App() { - - override fun setupAcra() { - // Do nothing - } -} diff --git a/app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt b/app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt deleted file mode 100644 index 75fe1320c9..0000000000 --- a/app/src/test/java/eu/kanade/tachiyomi/data/backup/BackupTest.kt +++ /dev/null @@ -1,377 +0,0 @@ -package eu.kanade.tachiyomi.data.backup - -import android.app.Application -import android.content.Context -import android.os.Build -import eu.kanade.tachiyomi.BuildConfig -import eu.kanade.tachiyomi.CustomRobolectricGradleTestRunner -import eu.kanade.tachiyomi.data.backup.legacy.LegacyBackupManager -import eu.kanade.tachiyomi.data.backup.legacy.models.Backup -import eu.kanade.tachiyomi.data.backup.legacy.models.DHistory -import eu.kanade.tachiyomi.data.database.DatabaseHelper -import eu.kanade.tachiyomi.data.database.models.Category -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.ChapterImpl -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.MangaImpl -import eu.kanade.tachiyomi.data.database.models.Track -import eu.kanade.tachiyomi.data.database.models.TrackImpl -import eu.kanade.tachiyomi.source.SourceManager -import eu.kanade.tachiyomi.source.online.HttpSource -import eu.kanade.tachiyomi.ui.reader.setting.OrientationType -import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType -import kotlinx.coroutines.runBlocking -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.buildJsonObject -import org.assertj.core.api.Assertions.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.RETURNS_DEEP_STUBS -import org.mockito.Mockito.anyLong -import org.mockito.Mockito.mock -import org.mockito.Mockito.`when` -import org.robolectric.RuntimeEnvironment -import org.robolectric.annotation.Config -import rx.Observable -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.InjektModule -import uy.kohesive.injekt.api.InjektRegistrar -import uy.kohesive.injekt.api.addSingleton - -/** - * Test class for the [LegacyBackupManager]. - * Note that this does not include the backup create/restore services. - */ -@Config(constants = BuildConfig::class, sdk = [Build.VERSION_CODES.M]) -@RunWith(CustomRobolectricGradleTestRunner::class) -class BackupTest { - // Create root object - var root = Backup() - - // Create information object - var information = buildJsonObject {} - - lateinit var app: Application - lateinit var context: Context - lateinit var source: HttpSource - - lateinit var legacyBackupManager: LegacyBackupManager - - lateinit var db: DatabaseHelper - - @Before - fun setup() { - app = RuntimeEnvironment.application - context = app.applicationContext - legacyBackupManager = LegacyBackupManager(context, 2) - db = legacyBackupManager.databaseHelper - - // Mock the source manager - val module = object : InjektModule { - override fun InjektRegistrar.registerInjectables() { - addSingleton(mock(SourceManager::class.java, RETURNS_DEEP_STUBS)) - } - } - Injekt.importModule(module) - - source = mock(HttpSource::class.java) - `when`(legacyBackupManager.sourceManager.get(anyLong())).thenReturn(source) - } - - /** - * Test that checks if no crashes when no categories in library. - */ - @Test - fun testRestoreEmptyCategory() { - // Restore Json - legacyBackupManager.restoreCategories(root.categories ?: emptyList()) - - // Check if empty - val dbCats = db.getCategories().executeAsBlocking() - assertThat(dbCats).isEmpty() - } - - /** - * Test to check if single category gets restored - */ - @Test - fun testRestoreSingleCategory() { - // Create category and add to json - val category = addSingleCategory("category") - - // Restore Json - legacyBackupManager.restoreCategories(root.categories ?: emptyList()) - - // Check if successful - val dbCats = legacyBackupManager.databaseHelper.getCategories().executeAsBlocking() - assertThat(dbCats).hasSize(1) - assertThat(dbCats[0].name).isEqualTo(category.name) - } - - /** - * Test to check if multiple categories get restored. - */ - @Test - fun testRestoreMultipleCategories() { - // Create category and add to json - val category = addSingleCategory("category") - val category2 = addSingleCategory("category2") - val category3 = addSingleCategory("category3") - val category4 = addSingleCategory("category4") - val category5 = addSingleCategory("category5") - - // Insert category to test if no duplicates on restore. - db.insertCategory(category).executeAsBlocking() - - // Restore Json - legacyBackupManager.restoreCategories(root.categories ?: emptyList()) - - // Check if successful - val dbCats = legacyBackupManager.databaseHelper.getCategories().executeAsBlocking() - assertThat(dbCats).hasSize(5) - assertThat(dbCats[0].name).isEqualTo(category.name) - assertThat(dbCats[1].name).isEqualTo(category2.name) - assertThat(dbCats[2].name).isEqualTo(category3.name) - assertThat(dbCats[3].name).isEqualTo(category4.name) - assertThat(dbCats[4].name).isEqualTo(category5.name) - } - - /** - * Test if restore of manga is successful - */ - @Test - fun testRestoreManga() { - // Add manga to database - val manga = getSingleManga("One Piece") - manga.readingModeType = ReadingModeType.VERTICAL.flagValue - manga.orientationType = OrientationType.PORTRAIT.flagValue - manga.id = db.insertManga(manga).executeAsBlocking().insertedId() - - var favoriteManga = legacyBackupManager.databaseHelper.getFavoriteMangas().executeAsBlocking() - assertThat(favoriteManga).hasSize(1) - assertThat(favoriteManga[0].readingModeType).isEqualTo(ReadingModeType.VERTICAL.flagValue) - assertThat(favoriteManga[0].orientationType).isEqualTo(OrientationType.PORTRAIT.flagValue) - - // Change manga in database to default values - val dbManga = getSingleManga("One Piece") - dbManga.id = manga.id - db.insertManga(dbManga).executeAsBlocking() - - favoriteManga = legacyBackupManager.databaseHelper.getFavoriteMangas().executeAsBlocking() - assertThat(favoriteManga).hasSize(1) - assertThat(favoriteManga[0].readingModeType).isEqualTo(ReadingModeType.DEFAULT.flagValue) - assertThat(favoriteManga[0].orientationType).isEqualTo(OrientationType.DEFAULT.flagValue) - - // Restore local manga - legacyBackupManager.restoreMangaNoFetch(manga, dbManga) - - // Test if restore successful - favoriteManga = legacyBackupManager.databaseHelper.getFavoriteMangas().executeAsBlocking() - assertThat(favoriteManga).hasSize(1) - assertThat(favoriteManga[0].readingModeType).isEqualTo(ReadingModeType.VERTICAL.flagValue) - assertThat(favoriteManga[0].orientationType).isEqualTo(OrientationType.PORTRAIT.flagValue) - - // Clear database to test manga fetch - clearDatabase() - - // Test if successful - favoriteManga = legacyBackupManager.databaseHelper.getFavoriteMangas().executeAsBlocking() - assertThat(favoriteManga).hasSize(0) - - // Restore Json - // Create JSON from manga to test parser - val json = legacyBackupManager.parser.encodeToString(manga) - // Restore JSON from manga to test parser - val jsonManga = legacyBackupManager.parser.decodeFromString(json) - - // Restore manga with fetch observable - val networkManga = getSingleManga("One Piece") - networkManga.description = "This is a description" - `when`(source.fetchMangaDetails(jsonManga)).thenReturn(Observable.just(networkManga)) - - runBlocking { - legacyBackupManager.fetchManga(source, jsonManga) - - // Check if restore successful - val dbCats = legacyBackupManager.databaseHelper.getFavoriteMangas().executeAsBlocking() - assertThat(dbCats).hasSize(1) - assertThat(dbCats[0].readingModeType).isEqualTo(ReadingModeType.VERTICAL.flagValue) - assertThat(dbCats[0].orientationType).isEqualTo(OrientationType.PORTRAIT.flagValue) - assertThat(dbCats[0].description).isEqualTo("This is a description") - } - } - - /** - * Test if chapter restore is successful - */ - @Test - fun testRestoreChapters() { - // Insert manga - val manga = getSingleManga("One Piece") - manga.id = legacyBackupManager.databaseHelper.insertManga(manga).executeAsBlocking().insertedId() - - // Create restore list - val chapters = mutableListOf() - for (i in 1..8) { - val chapter = getSingleChapter("Chapter $i") - chapter.read = true - chapters.add(chapter) - } - - // Check parser - val chaptersJson = legacyBackupManager.parser.encodeToString(chapters) - val restoredChapters = legacyBackupManager.parser.decodeFromString>(chaptersJson) - - // Fetch chapters from upstream - // Create list - val chaptersRemote = mutableListOf() - (1..10).mapTo(chaptersRemote) { getSingleChapter("Chapter $it") } - `when`(source.fetchChapterList(manga)).thenReturn(Observable.just(chaptersRemote)) - - runBlocking { - legacyBackupManager.restoreChapters(source, manga, restoredChapters) - - val dbCats = legacyBackupManager.databaseHelper.getChapters(manga).executeAsBlocking() - assertThat(dbCats).hasSize(10) - assertThat(dbCats[0].read).isEqualTo(true) - } - } - - /** - * Test to check if history restore works - */ - @Test - fun restoreHistoryForManga() { - val manga = getSingleManga("One Piece") - manga.id = legacyBackupManager.databaseHelper.insertManga(manga).executeAsBlocking().insertedId() - - // Create chapter - val chapter = getSingleChapter("Chapter 1") - chapter.manga_id = manga.id - chapter.read = true - chapter.id = legacyBackupManager.databaseHelper.insertChapter(chapter).executeAsBlocking().insertedId() - - val historyJson = getSingleHistory(chapter) - - val historyList = mutableListOf() - historyList.add(historyJson) - - // Check parser - val historyListJson = legacyBackupManager.parser.encodeToString(historyList) - val history = legacyBackupManager.parser.decodeFromString>(historyListJson) - - // Restore categories - legacyBackupManager.restoreHistoryForManga(history) - - val historyDB = legacyBackupManager.databaseHelper.getHistoryByMangaId(manga.id!!).executeAsBlocking() - assertThat(historyDB).hasSize(1) - assertThat(historyDB[0].last_read).isEqualTo(1000) - } - - /** - * Test to check if tracking restore works - */ - @Test - fun restoreTrackForManga() { - // Create mangas - val manga = getSingleManga("One Piece") - val manga2 = getSingleManga("Bleach") - manga.id = legacyBackupManager.databaseHelper.insertManga(manga).executeAsBlocking().insertedId() - manga2.id = legacyBackupManager.databaseHelper.insertManga(manga2).executeAsBlocking().insertedId() - - // Create track and add it to database - // This tests duplicate errors. - val track = getSingleTrack(manga) - track.last_chapter_read = 5F - legacyBackupManager.databaseHelper.insertTrack(track).executeAsBlocking() - var trackDB = legacyBackupManager.databaseHelper.getTracks(manga).executeAsBlocking() - assertThat(trackDB).hasSize(1) - assertThat(trackDB[0].last_chapter_read).isEqualTo(5) - track.last_chapter_read = 7F - - // Create track for different manga to test track not in database - val track2 = getSingleTrack(manga2) - track2.last_chapter_read = 10F - - // Check parser and restore already in database - var trackList = listOf(track) - // Check parser - var trackListJson = legacyBackupManager.parser.encodeToString(trackList) - var trackListRestore = legacyBackupManager.parser.decodeFromString>(trackListJson) - legacyBackupManager.restoreTrackForManga(manga, trackListRestore) - - // Assert if restore works. - trackDB = legacyBackupManager.databaseHelper.getTracks(manga).executeAsBlocking() - assertThat(trackDB).hasSize(1) - assertThat(trackDB[0].last_chapter_read).isEqualTo(7) - - // Check parser and restore already in database with lower chapter_read - track.last_chapter_read = 5F - trackList = listOf(track) - legacyBackupManager.restoreTrackForManga(manga, trackList) - - // Assert if restore works. - trackDB = legacyBackupManager.databaseHelper.getTracks(manga).executeAsBlocking() - assertThat(trackDB).hasSize(1) - assertThat(trackDB[0].last_chapter_read).isEqualTo(7) - - // Check parser and restore, track not in database - trackList = listOf(track2) - - // Check parser - trackListJson = legacyBackupManager.parser.encodeToString(trackList) - trackListRestore = legacyBackupManager.parser.decodeFromString>(trackListJson) - legacyBackupManager.restoreTrackForManga(manga2, trackListRestore) - - // Assert if restore works. - trackDB = legacyBackupManager.databaseHelper.getTracks(manga2).executeAsBlocking() - assertThat(trackDB).hasSize(1) - assertThat(trackDB[0].last_chapter_read).isEqualTo(10) - } - - private fun clearJson() { - root = Backup() - information = buildJsonObject {} - } - - private fun addSingleCategory(name: String): Category { - val category = Category.create(name) - root.categories = listOf(category) - return category - } - - private fun clearDatabase() { - db.deleteMangas().executeAsBlocking() - db.deleteHistory().executeAsBlocking() - } - - private fun getSingleHistory(chapter: Chapter): DHistory { - return DHistory(chapter.url, 1000) - } - - private fun getSingleTrack(manga: Manga): TrackImpl { - val track = TrackImpl() - track.title = manga.title - track.manga_id = manga.id!! - track.sync_id = 1 - return track - } - - private fun getSingleManga(title: String): MangaImpl { - val manga = MangaImpl() - manga.source = 1 - manga.title = title - manga.url = "/manga/$title" - manga.favorite = true - return manga - } - - private fun getSingleChapter(name: String): ChapterImpl { - val chapter = ChapterImpl() - chapter.name = name - chapter.url = "/read-online/$name-page-1.html" - return chapter - } -} diff --git a/app/src/test/java/eu/kanade/tachiyomi/data/database/CategoryTest.kt b/app/src/test/java/eu/kanade/tachiyomi/data/database/CategoryTest.kt deleted file mode 100644 index c24baa1c63..0000000000 --- a/app/src/test/java/eu/kanade/tachiyomi/data/database/CategoryTest.kt +++ /dev/null @@ -1,109 +0,0 @@ -package eu.kanade.tachiyomi.data.database - -import android.os.Build -import eu.kanade.tachiyomi.BuildConfig -import eu.kanade.tachiyomi.CustomRobolectricGradleTestRunner -import eu.kanade.tachiyomi.data.database.models.CategoryImpl -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.MangaCategory -import org.assertj.core.api.Assertions.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RuntimeEnvironment -import org.robolectric.annotation.Config - -@Config(constants = BuildConfig::class, sdk = [Build.VERSION_CODES.M]) -@RunWith(CustomRobolectricGradleTestRunner::class) -class CategoryTest { - - lateinit var db: DatabaseHelper - - @Before - fun setup() { - val app = RuntimeEnvironment.application - db = DatabaseHelper(app) - - // Create 5 manga - createManga("a") - createManga("b") - createManga("c") - createManga("d") - createManga("e") - } - - @Test - fun testHasCategories() { - // Create 2 categories - createCategory("Reading") - createCategory("Hold") - - val categories = db.getCategories().executeAsBlocking() - assertThat(categories).hasSize(2) - } - - @Test - fun testHasLibraryMangas() { - val mangas = db.getLibraryMangas().executeAsBlocking() - assertThat(mangas).hasSize(5) - } - - @Test - fun testHasCorrectFavorites() { - val m = Manga.create(0) - m.title = "title" - m.author = "" - m.artist = "" - m.thumbnail_url = "" - m.genre = "a list of genres" - m.description = "long description" - m.url = "url to manga" - m.favorite = false - db.insertManga(m).executeAsBlocking() - val mangas = db.getLibraryMangas().executeAsBlocking() - assertThat(mangas).hasSize(5) - } - - @Test - fun testMangaInCategory() { - // Create 2 categories - createCategory("Reading") - createCategory("Hold") - - // It should not have 0 as id - val c = db.getCategories().executeAsBlocking()[0] - assertThat(c.id).isNotZero - - // Add a manga to a category - val m = db.getLibraryMangas().executeAsBlocking()[0] - val mc = MangaCategory.create(m, c) - db.insertMangaCategory(mc).executeAsBlocking() - - // Get mangas from library and assert manga category is the same - val mangas = db.getLibraryMangas().executeAsBlocking() - for (manga in mangas) { - if (manga.id == m.id) { - assertThat(manga.category).isEqualTo(c.id) - } - } - } - - private fun createManga(title: String) { - val m = Manga.create(0) - m.title = title - m.author = "" - m.artist = "" - m.thumbnail_url = "" - m.genre = "a list of genres" - m.description = "long description" - m.url = "url to manga" - m.favorite = true - db.insertManga(m).executeAsBlocking() - } - - private fun createCategory(name: String) { - val c = CategoryImpl() - c.name = name - db.insertCategory(c).executeAsBlocking() - } -} diff --git a/app/src/test/java/eu/kanade/tachiyomi/data/database/ChapterRecognitionTest.kt b/app/src/test/java/eu/kanade/tachiyomi/data/database/ChapterRecognitionTest.kt deleted file mode 100644 index 33a0f2d176..0000000000 --- a/app/src/test/java/eu/kanade/tachiyomi/data/database/ChapterRecognitionTest.kt +++ /dev/null @@ -1,497 +0,0 @@ -package eu.kanade.tachiyomi.data.database - -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.util.chapter.ChapterRecognition -import org.assertj.core.api.Assertions.assertThat -import org.junit.Before -import org.junit.Test - -class ChapterRecognitionTest { - /** - * The manga containing manga title - */ - lateinit var manga: Manga - - /** - * The chapter containing chapter name - */ - lateinit var chapter: Chapter - - /** - * Set chapter title - * @param name name of chapter - * @return chapter object - */ - private fun createChapter(name: String): Chapter { - chapter = Chapter.create() - chapter.name = name - return chapter - } - - /** - * Set manga title - * @param title title of manga - * @return manga object - */ - private fun createManga(title: String): Manga { - manga.title = title - return manga - } - - /** - * Called before test - */ - @Before - fun setup() { - manga = Manga.create(0).apply { title = "random" } - chapter = Chapter.create() - } - - /** - * Ch.xx base case - */ - @Test - fun ChCaseBase() { - createManga("Mokushiroku Alice") - - createChapter("Mokushiroku Alice Vol.1 Ch.4: Misrepresentation") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(4f) - } - - /** - * Ch. xx base case but space after period - */ - @Test - fun ChCaseBase2() { - createManga("Mokushiroku Alice") - - createChapter("Mokushiroku Alice Vol. 1 Ch. 4: Misrepresentation") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(4f) - } - - /** - * Ch.xx.x base case - */ - @Test - fun ChCaseDecimal() { - createManga("Mokushiroku Alice") - - createChapter("Mokushiroku Alice Vol.1 Ch.4.1: Misrepresentation") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(4.1f) - - createChapter("Mokushiroku Alice Vol.1 Ch.4.4: Misrepresentation") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(4.4f) - } - - /** - * Ch.xx.a base case - */ - @Test - fun ChCaseAlpha() { - createManga("Mokushiroku Alice") - - createChapter("Mokushiroku Alice Vol.1 Ch.4.a: Misrepresentation") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(4.1f) - - createChapter("Mokushiroku Alice Vol.1 Ch.4.b: Misrepresentation") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(4.2f) - - createChapter("Mokushiroku Alice Vol.1 Ch.4.extra: Misrepresentation") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(4.99f) - } - - /** - * Name containing one number base case - */ - @Test - fun OneNumberCaseBase() { - createManga("Bleach") - - createChapter("Bleach 567 Down With Snowwhite") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(567f) - } - - /** - * Name containing one number and decimal case - */ - @Test - fun OneNumberCaseDecimal() { - createManga("Bleach") - - createChapter("Bleach 567.1 Down With Snowwhite") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(567.1f) - - createChapter("Bleach 567.4 Down With Snowwhite") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(567.4f) - } - - /** - * Name containing one number and alpha case - */ - @Test - fun OneNumberCaseAlpha() { - createManga("Bleach") - - createChapter("Bleach 567.a Down With Snowwhite") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(567.1f) - - createChapter("Bleach 567.b Down With Snowwhite") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(567.2f) - - createChapter("Bleach 567.extra Down With Snowwhite") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(567.99f) - } - - /** - * Chapter containing manga title and number base case - */ - @Test - fun MangaTitleCaseBase() { - createManga("Solanin") - - createChapter("Solanin 028 Vol. 2") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28f) - } - - /** - * Chapter containing manga title and number decimal case - */ - @Test - fun MangaTitleCaseDecimal() { - createManga("Solanin") - - createChapter("Solanin 028.1 Vol. 2") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28.1f) - - createChapter("Solanin 028.4 Vol. 2") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28.4f) - } - - /** - * Chapter containing manga title and number alpha case - */ - @Test - fun MangaTitleCaseAlpha() { - createManga("Solanin") - - createChapter("Solanin 028.a Vol. 2") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28.1f) - - createChapter("Solanin 028.b Vol. 2") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28.2f) - - createChapter("Solanin 028.extra Vol. 2") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28.99f) - } - - /** - * Extreme base case - */ - @Test - fun ExtremeCaseBase() { - createManga("Onepunch-Man") - - createChapter("Onepunch-Man Punch Ver002 028") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28f) - } - - /** - * Extreme base case decimal - */ - @Test - fun ExtremeCaseDecimal() { - createManga("Onepunch-Man") - - createChapter("Onepunch-Man Punch Ver002 028.1") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28.1f) - - createChapter("Onepunch-Man Punch Ver002 028.4") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28.4f) - } - - /** - * Extreme base case alpha - */ - @Test - fun ExtremeCaseAlpha() { - createManga("Onepunch-Man") - - createChapter("Onepunch-Man Punch Ver002 028.a") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28.1f) - - createChapter("Onepunch-Man Punch Ver002 028.b") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28.2f) - - createChapter("Onepunch-Man Punch Ver002 028.extra") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(28.99f) - } - - /** - * Chapter containing .v2 - */ - @Test - fun dotV2Case() { - createChapter("Vol.1 Ch.5v.2: Alones") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(5f) - } - - /** - * Check for case with number in manga title - */ - @Test - fun numberInMangaTitleCase() { - createManga("Ayame 14") - createChapter("Ayame 14 1 - The summer of 14") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(1f) - } - - /** - * Case with space between ch. x - */ - @Test - fun spaceAfterChapterCase() { - createManga("Mokushiroku Alice") - createChapter("Mokushiroku Alice Vol.1 Ch. 4: Misrepresentation") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(4f) - } - - /** - * Chapter containing mar(ch) - */ - @Test - fun marchInChapterCase() { - createManga("Ayame 14") - createChapter("Vol.1 Ch.1: March 25 (First Day Cohabiting)") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(1f) - } - - /** - * Chapter containing range - */ - @Test - fun rangeInChapterCase() { - createChapter("Ch.191-200 Read Online") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(191f) - } - - /** - * Chapter containing multiple zeros - */ - @Test - fun multipleZerosCase() { - createChapter("Vol.001 Ch.003: Kaguya Doesn't Know Much") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(3f) - } - - /** - * Chapter with version before number - */ - @Test - fun chapterBeforeNumberCase() { - createManga("Onepunch-Man") - createChapter("Onepunch-Man Punch Ver002 086 : Creeping Darkness [3]") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(86f) - } - - /** - * Case with version attached to chapter number - */ - @Test - fun vAttachedToChapterCase() { - createManga("Ansatsu Kyoushitsu") - createChapter("Ansatsu Kyoushitsu 011v002: Assembly Time") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(11f) - } - - /** - * Case where the chapter title contains the chapter - * But wait it's not actual the chapter number. - */ - @Test - fun NumberAfterMangaTitleWithChapterInChapterTitleCase() { - createChapter("Tokyo ESP 027: Part 002: Chapter 001") - createManga("Tokyo ESP") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(027f) - } - - /** - * unParsable chapter - */ - @Test - fun unParsableCase() { - createChapter("Foo") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(-1f) - } - - /** - * chapter with time in title - */ - @Test - fun timeChapterCase() { - createChapter("Fairy Tail 404: 00:00") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(404f) - } - - /** - * chapter with alpha without dot - */ - @Test - fun alphaWithoutDotCase() { - createChapter("Asu No Yoichi 19a") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(19.1f) - } - - /** - * Chapter title containing extra and vol - */ - @Test - fun chapterContainingExtraCase() { - createManga("Fairy Tail") - - createChapter("Fairy Tail 404.extravol002") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(404.99f) - - createChapter("Fairy Tail 404 extravol002") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(404.99f) - - createChapter("Fairy Tail 404.evol002") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(404.5f) - } - - /** - * Chapter title containing omake (japanese extra) and vol - */ - @Test - fun chapterContainingOmakeCase() { - createManga("Fairy Tail") - - createChapter("Fairy Tail 404.omakevol002") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(404.98f) - - createChapter("Fairy Tail 404 omakevol002") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(404.98f) - - createChapter("Fairy Tail 404.ovol002") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(404.15f) - } - - /** - * Chapter title containing special and vol - */ - @Test - fun chapterContainingSpecialCase() { - createManga("Fairy Tail") - - createChapter("Fairy Tail 404.specialvol002") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(404.97f) - - createChapter("Fairy Tail 404 specialvol002") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(404.97f) - - createChapter("Fairy Tail 404.svol002") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(404.19f) - } - - /** - * Chapter title containing comma's - */ - @Test - fun chapterContainingCommasCase() { - createManga("One Piece") - - createChapter("One Piece 300,a") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(300.1f) - - createChapter("One Piece Ch,123,extra") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(123.99f) - - createChapter("One Piece the sunny, goes swimming 024,005") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(24.005f) - } - - /** - * Test for chapters containing season - */ - @Test - fun chapterContainingSeasonCase() { - createManga("D.I.C.E") - - createChapter("D.I.C.E[Season 001] Ep. 007") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(7f) - } - - /** - * Test for chapters in format sx - chapter xx - */ - @Test - fun chapterContainingSeasonCase2() { - createManga("The Gamer") - - createChapter("S3 - Chapter 20") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(20f) - } - - /** - * Test for chapters ending with s - */ - @Test - fun chaptersEndingWithS() { - createManga("One Outs") - - createChapter("One Outs 001") - ChapterRecognition.parseChapterNumber(chapter, manga) - assertThat(chapter.chapter_number).isEqualTo(1f) - } -} diff --git a/app/src/test/java/eu/kanade/tachiyomi/data/library/LibraryUpdateServiceTest.kt b/app/src/test/java/eu/kanade/tachiyomi/data/library/LibraryUpdateServiceTest.kt deleted file mode 100644 index bd96644c41..0000000000 --- a/app/src/test/java/eu/kanade/tachiyomi/data/library/LibraryUpdateServiceTest.kt +++ /dev/null @@ -1,136 +0,0 @@ -package eu.kanade.tachiyomi.data.library - -import android.app.Application -import android.content.Context -import android.content.Intent -import android.os.Build -import eu.kanade.tachiyomi.BuildConfig -import eu.kanade.tachiyomi.CustomRobolectricGradleTestRunner -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.LibraryManga -import eu.kanade.tachiyomi.source.SourceManager -import eu.kanade.tachiyomi.source.online.HttpSource -import kotlinx.coroutines.runBlocking -import org.assertj.core.api.Assertions.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Matchers.anyLong -import org.mockito.Mockito.RETURNS_DEEP_STUBS -import org.mockito.Mockito.mock -import org.mockito.Mockito.`when` -import org.robolectric.Robolectric -import org.robolectric.RuntimeEnvironment -import org.robolectric.annotation.Config -import rx.Observable -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.InjektModule -import uy.kohesive.injekt.api.InjektRegistrar -import uy.kohesive.injekt.api.addSingleton - -@Config(constants = BuildConfig::class, sdk = [Build.VERSION_CODES.M]) -@RunWith(CustomRobolectricGradleTestRunner::class) -class LibraryUpdateServiceTest { - - lateinit var app: Application - lateinit var context: Context - lateinit var service: LibraryUpdateService - lateinit var source: HttpSource - - @Before - fun setup() { - app = RuntimeEnvironment.application - context = app.applicationContext - - // Mock the source manager - val module = object : InjektModule { - override fun InjektRegistrar.registerInjectables() { - addSingleton(mock(SourceManager::class.java, RETURNS_DEEP_STUBS)) - } - } - Injekt.importModule(module) - - service = Robolectric.setupService(LibraryUpdateService::class.java) - source = mock(HttpSource::class.java) - `when`(service.sourceManager.get(anyLong())).thenReturn(source) - } - - @Test - fun testLifecycle() { - // Smoke test - Robolectric.buildService(LibraryUpdateService::class.java) - .attach() - .create() - .startCommand(0, 0) - .destroy() - .get() - } - - @Test - fun testUpdateManga() { - val manga = createManga("/manga1")[0] - manga.id = 1L - service.db.insertManga(manga).executeAsBlocking() - - val sourceChapters = createChapters("/chapter1", "/chapter2") - - `when`(source.fetchChapterList(manga)).thenReturn(Observable.just(sourceChapters)) - - runBlocking { - service.updateManga(manga) - - assertThat(service.db.getChapters(manga).executeAsBlocking()).hasSize(2) - } - } - - @Test - fun testContinuesUpdatingWhenAMangaFails() { - var favManga = createManga("/manga1", "/manga2", "/manga3") - service.db.insertMangas(favManga).executeAsBlocking() - favManga = service.db.getLibraryMangas().executeAsBlocking() - - val chapters = createChapters("/chapter1", "/chapter2") - val chapters3 = createChapters("/achapter1", "/achapter2") - - // One of the updates will fail - `when`(source.fetchChapterList(favManga[0])).thenReturn(Observable.just(chapters)) - `when`(source.fetchChapterList(favManga[1])).thenReturn(Observable.error(Exception())) - `when`(source.fetchChapterList(favManga[2])).thenReturn(Observable.just(chapters3)) - - val intent = Intent() - val categoryId = intent.getIntExtra(LibraryUpdateService.KEY_CATEGORY, -1) - val target = LibraryUpdateService.Target.CHAPTERS - runBlocking { - service.addMangaToQueue(categoryId, target) - service.updateChapterList() - - // There are 3 network attempts and 2 insertions (1 request failed) - assertThat(service.db.getChapters(favManga[0]).executeAsBlocking()).hasSize(2) - assertThat(service.db.getChapters(favManga[1]).executeAsBlocking()).hasSize(0) - assertThat(service.db.getChapters(favManga[2]).executeAsBlocking()).hasSize(2) - } - } - - private fun createChapters(vararg urls: String): List { - val list = mutableListOf() - for (url in urls) { - val c = Chapter.create() - c.url = url - c.name = url.substring(1) - list.add(c) - } - return list - } - - private fun createManga(vararg urls: String): List { - val list = mutableListOf() - for (url in urls) { - val m = LibraryManga() - m.url = url - m.title = url.substring(1) - m.favorite = true - list.add(m) - } - return list - } -} diff --git a/app/src/test/java/eu/kanade/tachiyomi/util/chapter/ChapterRecognitionTest.kt b/app/src/test/java/eu/kanade/tachiyomi/util/chapter/ChapterRecognitionTest.kt new file mode 100644 index 0000000000..1887ff4da7 --- /dev/null +++ b/app/src/test/java/eu/kanade/tachiyomi/util/chapter/ChapterRecognitionTest.kt @@ -0,0 +1,275 @@ +package eu.kanade.tachiyomi.util.chapter + +import eu.kanade.tachiyomi.data.database.models.Chapter +import eu.kanade.tachiyomi.data.database.models.Manga +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode + +@Execution(ExecutionMode.CONCURRENT) +class ChapterRecognitionTest { + + @Test + fun `Basic Ch prefix`() { + val mangaTitle = "Mokushiroku Alice" + + assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4: Misrepresentation", 4f) + } + + @Test + fun `Basic Ch prefix with space after period`() { + val mangaTitle = "Mokushiroku Alice" + + assertChapter(mangaTitle, "Mokushiroku Alice Vol. 1 Ch. 4: Misrepresentation", 4f) + } + + @Test + fun `Basic Ch prefix with decimal`() { + val mangaTitle = "Mokushiroku Alice" + + assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4.1: Misrepresentation", 4.1f) + assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4.4: Misrepresentation", 4.4f) + } + + @Test + fun `Basic Ch prefix with alpha postfix`() { + val mangaTitle = "Mokushiroku Alice" + + assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4.a: Misrepresentation", 4.1f) + assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4.b: Misrepresentation", 4.2f) + assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch.4.extra: Misrepresentation", 4.99f) + } + + @Test + fun `Name containing one number`() { + val mangaTitle = "Bleach" + + assertChapter(mangaTitle, "Bleach 567 Down With Snowwhite", 567f) + } + + @Test + fun `Name containing one number and decimal`() { + val mangaTitle = "Bleach" + + assertChapter(mangaTitle, "Bleach 567.1 Down With Snowwhite", 567.1f) + assertChapter(mangaTitle, "Bleach 567.4 Down With Snowwhite", 567.4f) + } + + @Test + fun `Name containing one number and alpha`() { + val mangaTitle = "Bleach" + + assertChapter(mangaTitle, "Bleach 567.a Down With Snowwhite", 567.1f) + assertChapter(mangaTitle, "Bleach 567.b Down With Snowwhite", 567.2f) + assertChapter(mangaTitle, "Bleach 567.extra Down With Snowwhite", 567.99f) + } + + @Test + fun `Chapter containing manga title and number`() { + val mangaTitle = "Solanin" + + assertChapter(mangaTitle, "Solanin 028 Vol. 2", 28f) + } + + @Test + fun `Chapter containing manga title and number decimal`() { + val mangaTitle = "Solanin" + + assertChapter(mangaTitle, "Solanin 028.1 Vol. 2", 28.1f) + assertChapter(mangaTitle, "Solanin 028.4 Vol. 2", 28.4f) + } + + @Test + fun `Chapter containing manga title and number alpha`() { + val mangaTitle = "Solanin" + + assertChapter(mangaTitle, "Solanin 028.a Vol. 2", 28.1f) + assertChapter(mangaTitle, "Solanin 028.b Vol. 2", 28.2f) + assertChapter(mangaTitle, "Solanin 028.extra Vol. 2", 28.99f) + } + + @Test + fun `Extreme case`() { + val mangaTitle = "Onepunch-Man" + + assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028", 28f) + } + + @Test + fun `Extreme case with decimal`() { + val mangaTitle = "Onepunch-Man" + + assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028.1", 28.1f) + assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028.4", 28.4f) + } + + @Test + fun `Extreme case with alpha`() { + val mangaTitle = "Onepunch-Man" + + assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028.a", 28.1f) + assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028.b", 28.2f) + assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 028.extra", 28.99f) + } + + @Test + fun `Chapter containing dot v2`() { + val mangaTitle = "random" + + assertChapter(mangaTitle, "Vol.1 Ch.5v.2: Alones", 5f) + } + + @Test + fun `Number in manga title`() { + val mangaTitle = "Ayame 14" + + assertChapter(mangaTitle, "Ayame 14 1 - The summer of 14", 1f) + } + + @Test + fun `Space between ch x`() { + val mangaTitle = "Mokushiroku Alice" + + assertChapter(mangaTitle, "Mokushiroku Alice Vol.1 Ch. 4: Misrepresentation", 4f) + } + + @Test + fun `Chapter title with ch substring`() { + val mangaTitle = "Ayame 14" + + assertChapter(mangaTitle, "Vol.1 Ch.1: March 25 (First Day Cohabiting)", 1f) + } + + @Test + fun `Chapter containing multiple zeros`() { + val mangaTitle = "random" + + assertChapter(mangaTitle, "Vol.001 Ch.003: Kaguya Doesn't Know Much", 3f) + } + + @Test + fun `Chapter with version before number`() { + val mangaTitle = "Onepunch-Man" + + assertChapter(mangaTitle, "Onepunch-Man Punch Ver002 086 : Creeping Darkness [3]", 86f) + } + + @Test + fun `Version attached to chapter number`() { + val mangaTitle = "Ansatsu Kyoushitsu" + + assertChapter(mangaTitle, "Ansatsu Kyoushitsu 011v002: Assembly Time", 11f) + } + + /** + * Case where the chapter title contains the chapter + * But wait it's not actual the chapter number. + */ + @Test + fun `Number after manga title with chapter in chapter title case`() { + val mangaTitle = "Tokyo ESP" + + assertChapter(mangaTitle, "Tokyo ESP 027: Part 002: Chapter 001", 027f) + } + + @Test + fun `Unparseable chapter`() { + val mangaTitle = "random" + + assertChapter(mangaTitle, "Foo", -1f) + } + + @Test + fun `Chapter with time in title`() { + val mangaTitle = "random" + + assertChapter(mangaTitle, "Fairy Tail 404: 00:00", 404f) + } + + @Test + fun `Chapter with alpha without dot`() { + val mangaTitle = "random" + + assertChapter(mangaTitle, "Asu No Yoichi 19a", 19.1f) + } + + @Test + fun `Chapter title containing extra and vol`() { + val mangaTitle = "Fairy Tail" + + assertChapter(mangaTitle, "Fairy Tail 404.extravol002", 404.99f) + assertChapter(mangaTitle, "Fairy Tail 404 extravol002", 404.99f) + assertChapter(mangaTitle, "Fairy Tail 404.evol002", 404.5f) + } + + @Test + fun `Chapter title containing omake (japanese extra) and vol`() { + val mangaTitle = "Fairy Tail" + + assertChapter(mangaTitle, "Fairy Tail 404.omakevol002", 404.98f) + assertChapter(mangaTitle, "Fairy Tail 404 omakevol002", 404.98f) + assertChapter(mangaTitle, "Fairy Tail 404.ovol002", 404.15f) + } + + @Test + fun `Chapter title containing special and vol`() { + val mangaTitle = "Fairy Tail" + + assertChapter(mangaTitle, "Fairy Tail 404.specialvol002", 404.97f) + assertChapter(mangaTitle, "Fairy Tail 404 specialvol002", 404.97f) + assertChapter(mangaTitle, "Fairy Tail 404.svol002", 404.19f) + } + + @Test + fun `Chapter title containing commas`() { + val mangaTitle = "One Piece" + + assertChapter(mangaTitle, "One Piece 300,a", 300.1f) + assertChapter(mangaTitle, "One Piece Ch,123,extra", 123.99f) + assertChapter(mangaTitle, "One Piece the sunny, goes swimming 024,005", 24.005f) + } + + @Test + fun `Chapter title containing hyphens`() { + val mangaTitle = "Solo Leveling" + + assertChapter(mangaTitle, "ch 122-a", 122.1f) + assertChapter(mangaTitle, "Solo Leveling Ch.123-extra", 123.99f) + assertChapter(mangaTitle, "Solo Leveling, 024-005", 24.005f) + assertChapter(mangaTitle, "Ch.191-200 Read Online", 191.200f) + } + + @Test + fun `Chapters containing season`() { + assertChapter("D.I.C.E", "D.I.C.E[Season 001] Ep. 007", 7f) + } + + @Test + fun `Chapters in format sx - chapter xx`() { + assertChapter("The Gamer", "S3 - Chapter 20", 20f) + } + + @Test + fun `Chapters ending with s`() { + assertChapter("One Outs", "One Outs 001", 1f) + } + + private fun assertChapter(mangaTitle: String, name: String, expected: Float) { + val chapter = createChapter(name) + ChapterRecognition.parseChapterNumber(chapter, createManga(mangaTitle)) + assertEquals(expected, chapter.chapter_number) + } + + private fun createManga(title: String): Manga { + val manga = Manga.create(0) + manga.title = title + return manga + } + + private fun createChapter(name: String): Chapter { + val chapter = Chapter.create() + chapter.name = name + return chapter + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ac3459b93f..417351534a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,6 @@ coil_version = "2.0.0-rc03" conductor_version = "3.1.5" flowbinding_version = "1.2.0" shizuku_version = "12.1.0" -robolectric_version = "3.1.4" [libraries] android-shortcut-gradle = "com.github.zellius:android-shortcut-gradle-plugin:0.1.2" @@ -87,12 +86,7 @@ aboutlibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibr shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku_version" } shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizuku_version" } -junit = "junit:junit:4.13.2" -assertj-core = "org.assertj:assertj-core:3.16.1" -mockito-core = "org.mockito:mockito-core:1.10.19" - -robolectric-core = { module = "org.robolectric:robolectric", version.ref = "robolectric_version" } -robolectric-playservices = { module = "org.robolectric:shadows-play-services", version.ref = "robolectric_version" } +junit = "org.junit.jupiter:junit-jupiter:5.9.0" leakcanary-android = "com.squareup.leakcanary:leakcanary-android:2.7" @@ -106,7 +100,6 @@ coil = ["coil-core","coil-gif",] flowbinding = ["flowbinding-android","flowbinding-appcompat","flowbinding-recyclerview","flowbinding-swiperefreshlayout","flowbinding-viewpager"] conductor = ["conductor-core","conductor-viewpager","conductor-support-preference"] shizuku = ["shizuku-api","shizuku-provider"] -robolectric = ["robolectric-core","robolectric-playservices"] [plugins] kotlinter = { id = "org.jmailen.kotlinter", version = "3.10.0"}