mirror of
https://github.com/mihonapp/mihon.git
synced 2025-10-24 03:58:56 +02:00
Move GitHub Release/App Update logic to data (#9422)
* Move GitHub Release/App Update logic to data * Add tests for GetApplicationRelease * Review changes
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
package tachiyomi.domain.release.interactor
|
||||
|
||||
import tachiyomi.core.preference.Preference
|
||||
import tachiyomi.core.preference.PreferenceStore
|
||||
import tachiyomi.domain.release.model.Release
|
||||
import tachiyomi.domain.release.service.ReleaseService
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
|
||||
class GetApplicationRelease(
|
||||
private val service: ReleaseService,
|
||||
private val preferenceStore: PreferenceStore,
|
||||
) {
|
||||
|
||||
private val lastChecked: Preference<Long> by lazy {
|
||||
preferenceStore.getLong("last_app_check", 0)
|
||||
}
|
||||
|
||||
suspend fun await(arguments: Arguments): Result {
|
||||
val now = Instant.now()
|
||||
|
||||
// Limit checks to once every 3 days at most
|
||||
if (arguments.forceCheck.not() && now.isBefore(Instant.ofEpochMilli(lastChecked.get()).plus(3, ChronoUnit.DAYS))) {
|
||||
return Result.NoNewUpdate
|
||||
}
|
||||
|
||||
val release = service.latest(arguments.repository)
|
||||
|
||||
lastChecked.set(now.toEpochMilli())
|
||||
|
||||
// Check if latest version is different from current version
|
||||
val isNewVersion = isNewVersion(arguments.isPreview, arguments.commitCount, arguments.versionName, release.version)
|
||||
return when {
|
||||
isNewVersion && arguments.isThirdParty -> Result.ThirdPartyInstallation
|
||||
isNewVersion -> Result.NewUpdate(release)
|
||||
else -> Result.NoNewUpdate
|
||||
}
|
||||
}
|
||||
|
||||
private fun isNewVersion(isPreview: Boolean, commitCount: Int, versionName: String, versionTag: String): Boolean {
|
||||
// Removes prefixes like "r" or "v"
|
||||
val newVersion = versionTag.replace("[^\\d.]".toRegex(), "")
|
||||
return if (isPreview) {
|
||||
// Preview builds: based on releases in "tachiyomiorg/tachiyomi-preview" repo
|
||||
// tagged as something like "r1234"
|
||||
newVersion.toInt() > commitCount
|
||||
} else {
|
||||
// Release builds: based on releases in "tachiyomiorg/tachiyomi" repo
|
||||
// tagged as something like "v0.1.2"
|
||||
val oldVersion = versionName.replace("[^\\d.]".toRegex(), "")
|
||||
|
||||
val newSemVer = newVersion.split(".").map { it.toInt() }
|
||||
val oldSemVer = oldVersion.split(".").map { it.toInt() }
|
||||
|
||||
oldSemVer.mapIndexed { index, i ->
|
||||
if (newSemVer[index] > i) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
data class Arguments(
|
||||
val isPreview: Boolean,
|
||||
val isThirdParty: Boolean,
|
||||
val commitCount: Int,
|
||||
val versionName: String,
|
||||
val repository: String,
|
||||
val forceCheck: Boolean = false,
|
||||
)
|
||||
|
||||
sealed class Result {
|
||||
class NewUpdate(val release: Release) : Result()
|
||||
object NoNewUpdate : Result()
|
||||
object ThirdPartyInstallation : Result()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package tachiyomi.domain.release.model
|
||||
|
||||
import android.os.Build
|
||||
|
||||
/**
|
||||
* Contains information about the latest release.
|
||||
*/
|
||||
data class Release(
|
||||
val version: String,
|
||||
val info: String,
|
||||
val releaseLink: String,
|
||||
private val assets: List<String>,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Get download link of latest release from the assets.
|
||||
* @return download link of latest release.
|
||||
*/
|
||||
fun getDownloadLink(): String {
|
||||
val apkVariant = when (Build.SUPPORTED_ABIS[0]) {
|
||||
"arm64-v8a" -> "-arm64-v8a"
|
||||
"armeabi-v7a" -> "-armeabi-v7a"
|
||||
"x86" -> "-x86"
|
||||
"x86_64" -> "-x86_64"
|
||||
else -> ""
|
||||
}
|
||||
|
||||
return assets.find { it.contains("tachiyomi$apkVariant-") } ?: assets[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Assets class containing download url.
|
||||
*/
|
||||
data class Assets(val downloadLink: String)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package tachiyomi.domain.release.service
|
||||
|
||||
import tachiyomi.domain.release.model.Release
|
||||
|
||||
interface ReleaseService {
|
||||
|
||||
suspend fun latest(repository: String): Release
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package tachiyomi.domain.release.interactor
|
||||
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import tachiyomi.core.preference.Preference
|
||||
import tachiyomi.core.preference.PreferenceStore
|
||||
import tachiyomi.domain.release.model.Release
|
||||
import tachiyomi.domain.release.service.ReleaseService
|
||||
import java.time.Instant
|
||||
|
||||
class GetApplicationReleaseTest {
|
||||
|
||||
lateinit var getApplicationRelease: GetApplicationRelease
|
||||
lateinit var releaseService: ReleaseService
|
||||
lateinit var preference: Preference<Long>
|
||||
|
||||
@BeforeEach
|
||||
fun beforeEach() {
|
||||
val preferenceStore = mockk<PreferenceStore>()
|
||||
preference = mockk()
|
||||
every { preferenceStore.getLong(any(), any()) } returns preference
|
||||
releaseService = mockk()
|
||||
|
||||
getApplicationRelease = GetApplicationRelease(releaseService, preferenceStore)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `When has update but is third party expect third party installation`() = runTest {
|
||||
every { preference.get() } returns 0
|
||||
every { preference.set(any()) }.answers { }
|
||||
|
||||
coEvery { releaseService.latest(any()) } returns Release(
|
||||
"v2.0.0",
|
||||
"info",
|
||||
"http://example.com/release_link",
|
||||
listOf("http://example.com/assets"),
|
||||
)
|
||||
|
||||
val result = getApplicationRelease.await(
|
||||
GetApplicationRelease.Arguments(
|
||||
isPreview = false,
|
||||
isThirdParty = true,
|
||||
commitCount = 0,
|
||||
versionName = "v1.0.0",
|
||||
repository = "test",
|
||||
),
|
||||
)
|
||||
|
||||
result shouldBe GetApplicationRelease.Result.ThirdPartyInstallation
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `When has update but is preview expect new update`() = runTest {
|
||||
every { preference.get() } returns 0
|
||||
every { preference.set(any()) }.answers { }
|
||||
|
||||
val release = Release(
|
||||
"r2000",
|
||||
"info",
|
||||
"http://example.com/release_link",
|
||||
listOf("http://example.com/assets"),
|
||||
)
|
||||
|
||||
coEvery { releaseService.latest(any()) } returns release
|
||||
|
||||
val result = getApplicationRelease.await(
|
||||
GetApplicationRelease.Arguments(
|
||||
isPreview = true,
|
||||
isThirdParty = false,
|
||||
commitCount = 1000,
|
||||
versionName = "",
|
||||
repository = "test",
|
||||
),
|
||||
)
|
||||
|
||||
(result as GetApplicationRelease.Result.NewUpdate).release shouldBe GetApplicationRelease.Result.NewUpdate(release).release
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `When has update expect new update`() = runTest {
|
||||
every { preference.get() } returns 0
|
||||
every { preference.set(any()) }.answers { }
|
||||
|
||||
val release = Release(
|
||||
"v2.0.0",
|
||||
"info",
|
||||
"http://example.com/release_link",
|
||||
listOf("http://example.com/assets"),
|
||||
)
|
||||
|
||||
coEvery { releaseService.latest(any()) } returns release
|
||||
|
||||
val result = getApplicationRelease.await(
|
||||
GetApplicationRelease.Arguments(
|
||||
isPreview = false,
|
||||
isThirdParty = false,
|
||||
commitCount = 0,
|
||||
versionName = "v1.0.0",
|
||||
repository = "test",
|
||||
),
|
||||
)
|
||||
|
||||
(result as GetApplicationRelease.Result.NewUpdate).release shouldBe GetApplicationRelease.Result.NewUpdate(release).release
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `When has no update expect no new update`() = runTest {
|
||||
every { preference.get() } returns 0
|
||||
every { preference.set(any()) }.answers { }
|
||||
|
||||
val release = Release(
|
||||
"v1.0.0",
|
||||
"info",
|
||||
"http://example.com/release_link",
|
||||
listOf("http://example.com/assets"),
|
||||
)
|
||||
|
||||
coEvery { releaseService.latest(any()) } returns release
|
||||
|
||||
val result = getApplicationRelease.await(
|
||||
GetApplicationRelease.Arguments(
|
||||
isPreview = false,
|
||||
isThirdParty = false,
|
||||
commitCount = 0,
|
||||
versionName = "v2.0.0",
|
||||
repository = "test",
|
||||
),
|
||||
)
|
||||
|
||||
result shouldBe GetApplicationRelease.Result.NoNewUpdate
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `When now is before three days expect no new update`() = runTest {
|
||||
every { preference.get() } returns Instant.now().toEpochMilli()
|
||||
every { preference.set(any()) }.answers { }
|
||||
|
||||
val release = Release(
|
||||
"v1.0.0",
|
||||
"info",
|
||||
"http://example.com/release_link",
|
||||
listOf("http://example.com/assets"),
|
||||
)
|
||||
|
||||
coEvery { releaseService.latest(any()) } returns release
|
||||
|
||||
val result = getApplicationRelease.await(
|
||||
GetApplicationRelease.Arguments(
|
||||
isPreview = false,
|
||||
isThirdParty = false,
|
||||
commitCount = 0,
|
||||
versionName = "v2.0.0",
|
||||
repository = "test",
|
||||
),
|
||||
)
|
||||
|
||||
coVerify(exactly = 0) { releaseService.latest(any()) }
|
||||
result shouldBe GetApplicationRelease.Result.NoNewUpdate
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user