diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 302ee49c2b..f09c788020 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -73,16 +73,12 @@ jobs: # PROD - name: Build release build if: startsWith(env.VERSION_TAG, 'v') - uses: gradle/gradle-command-action@v2 - with: - arguments: assembleStandardRelease + run: ./gradlew assembleStandardRelease testStandardReleaseUnitTest # NIGHTLY - name: Build nightly build if: startsWith(env.VERSION_TAG, 'r') - uses: gradle/gradle-command-action@v2 - with: - arguments: assembleStandardNightly + run: ./gradlew assembleStandardNightly testStandardNightlyUnitTest - name: Sign APK if: env.VERSION_TAG != '' diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1595125308..48affbb09b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -305,6 +305,10 @@ dependencies { // Android Chart implementation(libs.mpandroidchart) + + // Tests + testImplementation(libs.bundles.test) + androidTestImplementation(libs.junit.android) } tasks { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt index f6c6412566..b7538efe54 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.updater import android.content.Context import android.os.Build +import androidx.annotation.VisibleForTesting import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.network.GET @@ -11,15 +12,16 @@ import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.util.system.localeContext import eu.kanade.tachiyomi.util.system.withIOContext import kotlinx.serialization.json.Json -import uy.kohesive.injekt.injectLazy +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import java.util.Date import java.util.concurrent.TimeUnit -class AppUpdateChecker { - - private val json: Json by injectLazy() - private val networkService: NetworkHelper by injectLazy() - private val preferences: PreferencesHelper by injectLazy() +class AppUpdateChecker( + private val json: Json = Injekt.get(), + private val networkService: NetworkHelper = Injekt.get(), + private val preferences: PreferencesHelper = Injekt.get(), +) { suspend fun checkForUpdate(context: Context, isUserPrompt: Boolean = false, doExtrasAfterNewUpdate: Boolean = true): AppUpdateResult { // Limit checks to once a day at most @@ -84,7 +86,8 @@ class AppUpdateChecker { } } - private fun isNewVersion(versionTag: String, currentVersion: String = BuildConfig.VERSION_NAME): Boolean { + @VisibleForTesting + fun isNewVersion(versionTag: String, currentVersion: String = BuildConfig.VERSION_NAME, isNightly: Boolean = BuildConfig.NIGHTLY): Boolean { // Removes prefixes like "r" or "v" val newVersion = versionTag.replace("[^\\d.-]".toRegex(), "") val oldVersion = currentVersion.replace("[^\\d.-]".toRegex(), "") @@ -94,15 +97,11 @@ class AppUpdateChecker { val isNewVersionNightly = newSemVer.size == 1 val oldSemVer = oldPreReleaseVer.first().split(".").map { it.toInt() } - // Nightly doesn't use semver - if (!BuildConfig.NIGHTLY) { - oldSemVer.mapIndexed { index, i -> - // Keeping this nightly check for backwards compat - if (!isNewVersionNightly && newSemVer.getOrElse(index) { i } > i) { - return true - } else if (newSemVer.getOrElse(index) { i } < i) { - return false - } + oldSemVer.mapIndexed { index, i -> + if (!isNewVersionNightly && !isNightly && newSemVer.getOrElse(index) { i } > i) { + return true + } else if (newSemVer.getOrElse(index) { i } < i) { + return false } } // For cases of extreme patch versions (new: 1.2.3.1 vs old: 1.2.3, return true) @@ -118,12 +117,12 @@ class AppUpdateChecker { oldPreReleaseVer.getOrNull(1)?.replace("[^\\d.-]".toRegex(), "")?.toIntOrNull() when { // For prod, don't bother with betas (current: 1.2.3 vs new: 1.2.3-b1) - oldPreVersion == null -> false + oldPreVersion == null && !isNightly -> false // For betas, always use prod builds (current: 1.2.3-b1 vs new: 1.2.3) // For nightly, don't use prod builds - newPreVersion == null -> !BuildConfig.NIGHTLY + newPreVersion == null -> !isNightly // For nightly, higher beta ver is newer (current: 1.2.3-b1 vs new: 1.2.3-b2 or r2) - else -> (oldPreVersion < newPreVersion) + else -> (oldPreVersion ?: 0) < newPreVersion } } } diff --git a/app/src/test/java/eu/kanade/tachiyomi/data/updater/AppUpdateCheckerTest.kt b/app/src/test/java/eu/kanade/tachiyomi/data/updater/AppUpdateCheckerTest.kt new file mode 100644 index 0000000000..0d58a48a48 --- /dev/null +++ b/app/src/test/java/eu/kanade/tachiyomi/data/updater/AppUpdateCheckerTest.kt @@ -0,0 +1,75 @@ +package eu.kanade.tachiyomi.data.updater + +import android.content.Context +import eu.kanade.tachiyomi.core.preference.PreferenceStore +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.network.NetworkHelper +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.impl.annotations.SpyK +import io.mockk.mockk +import kotlinx.serialization.json.Json +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test + +class AppUpdateCheckerTest { + lateinit var appUpdateChecker: AppUpdateChecker + + @Before + fun setup() { + val json = mockk() + val network = mockk() + val preferences = mockk() + + appUpdateChecker = AppUpdateChecker(json, network, preferences) + } + + @Test + fun `Check new nightly version (Tachi format)`() { + assertTrue(isNewVersion("1.2.3-r2", "1.2.3-r1", true)) // tachi format + assertTrue(isNewVersion("1.2.4-r1", "1.2.3", true)) // Unlikely to happened, but we should try anyway + } + + @Test + fun `Check new nightly version (Yokai format)`() { + assertTrue(isNewVersion("r2", "1.2.3-r1", true)) // yokai format + } + + @Test + fun `Nightly shouldn't get Prod build`() { + assertFalse(isNewVersion("1.2.3", "1.2.3-r2", true)) + assertFalse(isNewVersion("1.2.4", "1.2.3-r2", true)) + assertFalse(isNewVersion("1.2.4", "1.2.3", true)) + } + + @Test + fun `Beta should get Prod build`() { + assertTrue(isNewVersion("1.2.4", "1.2.3-r2", false)) + assertTrue(isNewVersion("1.2.3", "1.2.3-r2", false)) + } + + @Test + fun `Prod should get latest Prod build`() { + assertTrue(isNewVersion("1.2.4", "1.2.3", false)) + } + + @Test + fun `Prod should get latest Prod build (Check for Betas)`() { + assertTrue(isNewVersion("1.2.4-r1", "1.2.3", false)) + } + + @Test + fun `Latest version check`() { + assertFalse(isNewVersion("1.2.3", "1.2.3", false)) + assertFalse(isNewVersion("1.2.3-r1", "1.2.3-r1", false)) + + assertFalse(isNewVersion("1.2.3-r1", "1.2.3-r1", true)) + assertFalse(isNewVersion("r1", "1.2.4-r1", true)) + } + + private fun isNewVersion(newVersion: String, currentVersion: String, isNightly: Boolean): Boolean { + return appUpdateChecker.isNewVersion(newVersion, currentVersion, isNightly) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d4fdaf9222..e3f031f0b3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -68,7 +68,13 @@ firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics-ktx" unifile = { module = "com.github.tachiyomiorg:unifile", version = "17bec43" } viewstatepageradapter = { module = "com.nightlynexus.viewstatepageradapter:viewstatepageradapter", version = "1.1.0" } viewtooltip = { module = "com.github.florent37:viewtooltip", version = "1.2.2" } +junit = { module = "junit:junit", version = "4.13.2" } +junit-android = { module = "androidx.test.ext:junit", version = "1.1.5" } +mockk = { module = "io.mockk:mockk", version = "1.13.11" } [plugins] kotlinter = { id = "org.jmailen.kotlinter", version = "4.1.1" } gradle-versions = { id = "com.github.ben-manes.versions", version = "0.42.0" } + +[bundles] +test = [ "junit", "mockk" ]