From 54e69a49faabc925aed6073ec55ec952656ddd89 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 31 Mar 2026 09:34:46 -0300 Subject: [PATCH] Use more generic PackageVersion in AppInstallManager --- .../org/fdroid/install/AppInstallManager.kt | 100 ++++++------ .../kotlin/org/fdroid/install/InstallState.kt | 4 +- .../fdroid/install/SessionInstallManager.kt | 14 +- .../org/fdroid/install/UnarchiveWorker.kt | 13 +- .../org/fdroid/ui/details/AppDetailsItem.kt | 6 +- .../fdroid/ui/details/AppDetailsViewModel.kt | 9 +- .../org/fdroid/ui/utils/PreviewUtils.kt | 4 + .../org/fdroid/updates/UpdateInstaller.kt | 13 +- .../fdroid/install/AppInstallManagerTest.kt | 26 +-- .../install/SessionInstallManagerTest.kt | 13 +- .../org/fdroid/updates/UpdateInstallerTest.kt | 153 ++++++++++-------- 11 files changed, 190 insertions(+), 165 deletions(-) diff --git a/app/src/main/kotlin/org/fdroid/install/AppInstallManager.kt b/app/src/main/kotlin/org/fdroid/install/AppInstallManager.kt index 3f176ddae..96aae5f43 100644 --- a/app/src/main/kotlin/org/fdroid/install/AppInstallManager.kt +++ b/app/src/main/kotlin/org/fdroid/install/AppInstallManager.kt @@ -31,13 +31,13 @@ import mu.KotlinLogging import org.fdroid.LocaleChooser.getBestLocale import org.fdroid.NotificationManager import org.fdroid.database.AppMetadata -import org.fdroid.database.AppVersion import org.fdroid.database.Repository import org.fdroid.download.DownloaderFactory import org.fdroid.download.getUri import org.fdroid.history.HistoryManager import org.fdroid.history.InstallEvent import org.fdroid.history.UninstallEvent +import org.fdroid.index.v2.PackageVersion import org.fdroid.utils.IoDispatcher @Singleton @@ -81,18 +81,17 @@ constructor( else -> null } // track app state for in progress apps - val appState = - appStateCategory?.let { - // all states that get a category above must be InstallStateWithInfo - state as InstallStateWithInfo - AppState( - packageName = packageName, - category = it, - name = state.name, - installVersionName = state.versionName, - currentVersionName = state.currentVersionName, - ) - } + val appState = appStateCategory?.let { + // all states that get a category above must be InstallStateWithInfo + state as InstallStateWithInfo + AppState( + packageName = packageName, + category = it, + name = state.name, + installVersionName = state.versionName, + currentVersionName = state.currentVersionName, + ) + } if (appState != null) appStates.add(appState) } return InstallNotificationState( @@ -116,19 +115,20 @@ constructor( */ @UiThread suspend fun install( + packageName: String, appMetadata: AppMetadata?, - version: AppVersion, + version: PackageVersion, currentVersionName: String?, repo: Repository?, iconModel: Any?, canAskPreApprovalNow: Boolean, ): InstallState { if (appMetadata == null || repo == null) { - log.error { "Can't install app without metadata for ${version.packageName}" } + log.error { "Can't install app without metadata for $packageName" } val error = InstallState.Error( - msg = "App ${version.packageName} no longer in DB.", - name = version.packageName, + msg = "App $packageName no longer in DB.", + name = packageName, versionName = version.versionName, currentVersionName = currentVersionName, lastUpdated = version.added, @@ -136,27 +136,25 @@ constructor( ) // Write the terminal state so any prior Waiting state is cleared and the // service stop logic in onStatesUpdated() has a chance to run. - updateAppState(version.packageName, error) + updateAppState(packageName, error) return error } - val packageName = appMetadata.packageName val currentState = apps.value[packageName] if (currentState?.showProgress == true && currentState !is InstallState.Waiting) { log.warn { "Attempted to install $packageName with install in progress: $currentState" } return currentState } currentCoroutineContext().ensureActive() - val job = - scope.async { - startInstall( - appMetadata = appMetadata, - version = version, - currentVersionName = currentVersionName, - repo = repo, - iconModel = iconModel, - canAskPreApprovalNow = canAskPreApprovalNow, - ) - } + val job = scope.async { + startInstall( + appMetadata = appMetadata, + version = version, + currentVersionName = currentVersionName, + repo = repo, + iconModel = iconModel, + canAskPreApprovalNow = canAskPreApprovalNow, + ) + } // keep track of this job, in case we want to cancel it return trackJob(packageName, job) } @@ -212,7 +210,7 @@ constructor( @WorkerThread private suspend fun startInstall( appMetadata: AppMetadata, - version: AppVersion, + version: PackageVersion, currentVersionName: String?, repo: Repository, iconModel: Any?, @@ -256,7 +254,14 @@ constructor( ) } as InstallState.PreApproved - downloadAndInstall(newState, version, currentVersionName, repo, iconModel) + downloadAndInstall( + state = newState, + packageName = appMetadata.packageName, + version = version, + currentVersionName = currentVersionName, + repo = repo, + iconModel = iconModel, + ) } is PreApprovalResult.UserConfirmationRequired -> { InstallState.PreApprovalConfirmationNeeded( @@ -295,16 +300,16 @@ constructor( updateAppState(packageName, result) return if (result is InstallState.PreApproved) { // move us off the UiThread, so we can download/install this app now - val job = - scope.async { - downloadAndInstall( - state = result, - version = installState.version, - currentVersionName = installState.currentVersionName, - repo = installState.repo, - iconModel = installState.iconModel, - ) - } + val job = scope.async { + downloadAndInstall( + state = result, + packageName = packageName, + version = installState.version, + currentVersionName = installState.currentVersionName, + repo = installState.repo, + iconModel = installState.iconModel, + ) + } // suspend/wait for this job and track it in case we want to cancel it trackJob(packageName, job) } else result @@ -313,7 +318,8 @@ constructor( @WorkerThread private suspend fun downloadAndInstall( state: InstallState.PreApproved, - version: AppVersion, + packageName: String, + version: PackageVersion, currentVersionName: String?, repo: Repository, iconModel: Any?, @@ -328,7 +334,7 @@ constructor( val now = System.currentTimeMillis() downloader.setListener { bytesRead, totalBytes -> coroutineContext.ensureActive() - updateAndGetAppState(version.packageName) { + updateAndGetAppState(packageName) { InstallState.Downloading( name = it.name, versionName = it.versionName, @@ -359,7 +365,7 @@ constructor( } currentCoroutineContext().ensureActive() val newState = - updateAndGetAppState(version.packageName) { + updateAndGetAppState(packageName) { InstallState.Installing( name = it.name, versionName = it.versionName, @@ -368,12 +374,12 @@ constructor( iconModel = it.iconModel, ) } - val result = sessionInstallManager.install(sessionId, version.packageName, newState, file) + val result = sessionInstallManager.install(sessionId, packageName, newState, file) log.debug { "Install result: $result" } return if (result is InstallState.PreApproved && result.result is PreApprovalResult.Error) { // if pre-approval failed (e.g. due to app label mismatch), // then try to install again, this time not using the pre-approved session - sessionInstallManager.install(null, version.packageName, newState, file) + sessionInstallManager.install(null, packageName, newState, file) } else { result } diff --git a/app/src/main/kotlin/org/fdroid/install/InstallState.kt b/app/src/main/kotlin/org/fdroid/install/InstallState.kt index 45fa6aa3a..30bec9a72 100644 --- a/app/src/main/kotlin/org/fdroid/install/InstallState.kt +++ b/app/src/main/kotlin/org/fdroid/install/InstallState.kt @@ -1,8 +1,8 @@ package org.fdroid.install import android.app.PendingIntent -import org.fdroid.database.AppVersion import org.fdroid.database.Repository +import org.fdroid.index.v2.PackageVersion sealed class InstallState(val showProgress: Boolean) { data object Unknown : InstallState(false) @@ -30,7 +30,7 @@ sealed class InstallState(val showProgress: Boolean) { data class PreApprovalConfirmationNeeded( private val state: InstallStateWithInfo, - val version: AppVersion, + val version: PackageVersion, val repo: Repository, override val sessionId: Int, override val creationTimeMillis: Long = System.currentTimeMillis(), diff --git a/app/src/main/kotlin/org/fdroid/install/SessionInstallManager.kt b/app/src/main/kotlin/org/fdroid/install/SessionInstallManager.kt index 3c57d4030..dfe3856ba 100644 --- a/app/src/main/kotlin/org/fdroid/install/SessionInstallManager.kt +++ b/app/src/main/kotlin/org/fdroid/install/SessionInstallManager.kt @@ -34,7 +34,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine import mu.KotlinLogging import org.fdroid.LocaleChooser.getBestLocale import org.fdroid.database.AppMetadata -import org.fdroid.database.AppVersion +import org.fdroid.index.v2.PackageVersion import org.fdroid.ui.utils.isAppInForeground import org.fdroid.utils.IoDispatcher @@ -93,13 +93,13 @@ constructor( app: AppMetadata, iconGetter: suspend () -> Bitmap?, isUpdate: Boolean, - version: AppVersion, + version: PackageVersion, canRequestUserConfirmationNow: Boolean, ): PreApprovalResult { return if (!context.isAppInForeground()) { log.info { "App not in foreground, pre-approval for ${app.packageName} not supported." } PreApprovalResult.NotSupported - } else if (isUpdate && canDoAutoUpdate(version)) { + } else if (isUpdate && canDoAutoUpdate(app.packageName, version)) { // should not be needed, so we say not supported log.info { "Can do auto-update pre-approval for ${app.packageName} not needed." } PreApprovalResult.NotSupported @@ -368,17 +368,17 @@ constructor( return params } - private fun canDoAutoUpdate(version: AppVersion): Boolean { + private fun canDoAutoUpdate(packageName: String, version: PackageVersion): Boolean { if (SDK_INT < 31) return false - val targetSdkVersion = version.manifest.targetSdkVersion ?: return false + val targetSdkVersion = version.packageManifest.targetSdkVersion ?: return false // docs: // https://developer.android.com/reference/android/content/pm/PackageInstaller.SessionParams#setRequireUserAction(int) return if (isAutoUpdateSupported(targetSdkVersion)) { val ourPackageName = context.packageName - if (ourPackageName == version.packageName) return true + if (ourPackageName == packageName) return true val sourceInfo = try { - context.packageManager.getInstallSourceInfo(version.packageName) + context.packageManager.getInstallSourceInfo(packageName) } catch (e: Exception) { log.error(e) { "Could not get package info: " } return false diff --git a/app/src/main/kotlin/org/fdroid/install/UnarchiveWorker.kt b/app/src/main/kotlin/org/fdroid/install/UnarchiveWorker.kt index 9cb8a85c5..95ba23dec 100644 --- a/app/src/main/kotlin/org/fdroid/install/UnarchiveWorker.kt +++ b/app/src/main/kotlin/org/fdroid/install/UnarchiveWorker.kt @@ -83,12 +83,13 @@ constructor( // TODO we could do better error handling, e.g. when metadata or repo are null // or install state is an error, also maybe show a Toast to user on error appInstallManager.install( - appMetadata = db.getAppDao().getApp(version.repoId, packageName)?.metadata, - version = version, - currentVersionName = null, - repo = repoManager.getRepository(version.repoId), - iconModel = null, - canAskPreApprovalNow = true, + packageName = packageName, + appMetadata = db.getAppDao().getApp(version.repoId, packageName)?.metadata, + version = version, + currentVersionName = null, + repo = repoManager.getRepository(version.repoId), + iconModel = null, + canAskPreApprovalNow = true, ) Result.success() } diff --git a/app/src/main/kotlin/org/fdroid/ui/details/AppDetailsItem.kt b/app/src/main/kotlin/org/fdroid/ui/details/AppDetailsItem.kt index 787109839..e8465073d 100644 --- a/app/src/main/kotlin/org/fdroid/ui/details/AppDetailsItem.kt +++ b/app/src/main/kotlin/org/fdroid/ui/details/AppDetailsItem.kt @@ -53,7 +53,7 @@ data class AppDetailsItem( */ val installedSigner: String? = null, /** The currently suggested version for installation. */ - val suggestedVersion: AppVersion? = null, + val suggestedVersion: PackageVersion? = null, /** * Similar to [suggestedVersion], but doesn't obey [appPrefs] for ignoring versions. This is * useful for (un-)ignoring this version. @@ -225,8 +225,8 @@ data class AppDetailsItem( val bitcoinUri = app.bitcoin?.let { "bitcoin:$it" } } -class AppDetailsActions( - val installAction: (AppMetadata, AppVersion, Any?) -> Unit, +data class AppDetailsActions( + val installAction: (AppMetadata, PackageVersion, Any?) -> Unit, val requestUserConfirmation: (InstallState.UserConfirmationNeeded) -> Unit, /** * A workaround for Android 10, 11, 12 and 13 where tapping outside the confirmation dialog diff --git a/app/src/main/kotlin/org/fdroid/ui/details/AppDetailsViewModel.kt b/app/src/main/kotlin/org/fdroid/ui/details/AppDetailsViewModel.kt index 7c5e37d3a..01ae0b543 100644 --- a/app/src/main/kotlin/org/fdroid/ui/details/AppDetailsViewModel.kt +++ b/app/src/main/kotlin/org/fdroid/ui/details/AppDetailsViewModel.kt @@ -31,13 +31,13 @@ import kotlinx.coroutines.withContext import mu.KotlinLogging import org.fdroid.UpdateChecker import org.fdroid.database.AppMetadata -import org.fdroid.database.AppVersion import org.fdroid.database.FDroidDatabase import org.fdroid.download.DownloadRequest import org.fdroid.download.NetworkMonitor import org.fdroid.getCacheKey import org.fdroid.index.RELEASE_CHANNEL_BETA import org.fdroid.index.RepoManager +import org.fdroid.index.v2.PackageVersion import org.fdroid.install.AppInstallManager import org.fdroid.install.InstallState import org.fdroid.repo.RepoPreLoader @@ -116,14 +116,15 @@ constructor( } @UiThread - fun install(appMetadata: AppMetadata, version: AppVersion, iconModel: Any?) { + fun install(appMetadata: AppMetadata, version: PackageVersion, iconModel: Any?) { scope.launch(Dispatchers.Main) { val result = appInstallManager.install( + packageName = packageName, appMetadata = appMetadata, version = version, - currentVersionName = packageInfoFlow.value?.packageInfo?.versionName, - repo = repoManager.getRepository(version.repoId) ?: return@launch, // TODO + currentVersionName = packageInfoFlow.value?.packageInfo?.versionName, // TODO + repo = repoManager.getRepository(appMetadata.repoId) ?: return@launch, iconModel = iconModel, canAskPreApprovalNow = true, ) diff --git a/app/src/main/kotlin/org/fdroid/ui/utils/PreviewUtils.kt b/app/src/main/kotlin/org/fdroid/ui/utils/PreviewUtils.kt index 968605bb6..50095e104 100644 --- a/app/src/main/kotlin/org/fdroid/ui/utils/PreviewUtils.kt +++ b/app/src/main/kotlin/org/fdroid/ui/utils/PreviewUtils.kt @@ -13,6 +13,7 @@ import org.fdroid.database.Repository import org.fdroid.download.Mirror import org.fdroid.download.NetworkState import org.fdroid.index.IndexFormatVersion +import org.fdroid.index.v2.FileV1 import org.fdroid.index.v2.PackageManifest import org.fdroid.index.v2.PackageVersion import org.fdroid.index.v2.SignerV2 @@ -81,6 +82,7 @@ val testVersion1 = object : PackageVersion { override val versionCode: Long = 42 override val versionName: String = "42.23.0-alpha1337-33d2252b90" + override val file: FileV1 = FileV1("foo/bar", "abcd", 23) override val added: Long = System.currentTimeMillis() - DAYS.toMillis(4) override val size: Long = 1024 * 1024 * 42 override val signer: SignerV2 = @@ -100,6 +102,7 @@ val testVersion2 = object : PackageVersion { override val versionCode: Long = 23 override val versionName: String = "23.42.0" + override val file: FileV1 = FileV1("foo/bar", "abcd", 23) override val added: Long = System.currentTimeMillis() - DAYS.toMillis(4) override val size: Long = 1024 * 1024 * 23 override val signer: SignerV2 = @@ -252,6 +255,7 @@ fun getPreviewVersion(versionName: String, size: Long? = null) = object : PackageVersion { override val versionCode: Long = 23 override val versionName: String = versionName + override val file: FileV1 = FileV1("foo/bar", "abcd", 23) override val added: Long = System.currentTimeMillis() - DAYS.toMillis(3) override val size: Long? = size override val signer: SignerV2? = null diff --git a/app/src/main/kotlin/org/fdroid/updates/UpdateInstaller.kt b/app/src/main/kotlin/org/fdroid/updates/UpdateInstaller.kt index 85e957551..fa9b2d5af 100644 --- a/app/src/main/kotlin/org/fdroid/updates/UpdateInstaller.kt +++ b/app/src/main/kotlin/org/fdroid/updates/UpdateInstaller.kt @@ -114,12 +114,13 @@ constructor( private suspend fun updateApp(update: AppUpdateItem, canAskPreApprovalNow: Boolean) { val app = db.getAppDao().getApp(update.repoId, update.packageName) appInstallManager.install( - appMetadata = app?.metadata, - version = update.update as AppVersion, - currentVersionName = update.installedVersionName, - repo = repoManager.getRepository(update.repoId), - iconModel = update.iconModel, - canAskPreApprovalNow = canAskPreApprovalNow, + packageName = update.packageName, + appMetadata = app?.metadata, + version = update.update as AppVersion, + currentVersionName = update.installedVersionName, + repo = repoManager.getRepository(update.repoId), + iconModel = update.iconModel, + canAskPreApprovalNow = canAskPreApprovalNow, ) } } diff --git a/app/src/test/java/org/fdroid/install/AppInstallManagerTest.kt b/app/src/test/java/org/fdroid/install/AppInstallManagerTest.kt index 59202b07f..8c4461bc7 100644 --- a/app/src/test/java/org/fdroid/install/AppInstallManagerTest.kt +++ b/app/src/test/java/org/fdroid/install/AppInstallManagerTest.kt @@ -624,12 +624,13 @@ internal class AppInstallManagerTest { val installJob = installScope.async { appInstallManager.install( - appMetadata = appMetadata, - version = version, - currentVersionName = installedVersionName, - repo = repo, - iconModel = null, - canAskPreApprovalNow = false, + packageName = packageName, + appMetadata = appMetadata, + version = version, + currentVersionName = installedVersionName, + repo = repo, + iconModel = null, + canAskPreApprovalNow = false, ) } @@ -648,12 +649,13 @@ internal class AppInstallManagerTest { canAskPreApprovalNow: Boolean = false, ): InstallState { return appInstallManager.install( - appMetadata = appMetadata, - version = version, - currentVersionName = currentVersionName, - repo = repo, - iconModel = iconModel, - canAskPreApprovalNow = canAskPreApprovalNow, + packageName = packageName, + appMetadata = appMetadata, + version = version, + currentVersionName = currentVersionName, + repo = repo, + iconModel = iconModel, + canAskPreApprovalNow = canAskPreApprovalNow, ) } } diff --git a/app/src/test/java/org/fdroid/install/SessionInstallManagerTest.kt b/app/src/test/java/org/fdroid/install/SessionInstallManagerTest.kt index a173e98eb..ccc838d93 100644 --- a/app/src/test/java/org/fdroid/install/SessionInstallManagerTest.kt +++ b/app/src/test/java/org/fdroid/install/SessionInstallManagerTest.kt @@ -34,6 +34,7 @@ import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.runBlocking import org.fdroid.database.AppMetadata import org.fdroid.database.AppVersion +import org.fdroid.index.v2.PackageVersion import org.fdroid.ui.utils.isAppInForeground import org.junit.After import org.junit.Before @@ -73,7 +74,7 @@ internal class SessionInstallManagerTest { name = mapOf("en-US" to "Example App"), isCompatible = true, ) - private val appVersion: AppVersion = mockk(relaxed = true) + private val appVersion: PackageVersion = mockk(relaxed = true) private val installingState = InstallState.Installing( @@ -213,14 +214,13 @@ internal class SessionInstallManagerTest { ) assertIs(notForegroundResult) - // in foreground + update that can auto-update -> NotSupported + // in foreground + update that can auto-update, we say NotSupported, because not needed every { context.isAppInForeground() } returns true - every { appVersion.packageName } returns packageName - every { appVersion.manifest.targetSdkVersion } returns 34 + every { appVersion.packageManifest.targetSdkVersion } returns 42 val sourceInfo: InstallSourceInfo = mockk(relaxed = true) every { sourceInfo.installingPackageName } returns context.packageName if (SDK_INT >= 34) { - every { sourceInfo.updateOwnerPackageName } returns null + every { sourceInfo.updateOwnerPackageName } returns context.packageName } every { packageManager.getInstallSourceInfo(packageName) } returns sourceInfo @@ -237,8 +237,7 @@ internal class SessionInstallManagerTest { // isUpdate = true but not our package, and we are not the update owner -> NotSupported, // because canDoAutoUpdate() returns false when getInstallSourceInfo() throws every { context.isAppInForeground() } returns true - every { appVersion.packageName } returns packageName - every { appVersion.manifest.targetSdkVersion } returns 34 + every { appVersion.packageManifest.targetSdkVersion } returns 34 every { packageManager.getInstallSourceInfo(packageName) } throws SecurityException("nope") val installSourceError = diff --git a/app/src/test/java/org/fdroid/updates/UpdateInstallerTest.kt b/app/src/test/java/org/fdroid/updates/UpdateInstallerTest.kt index 93079c57c..31a3c78a5 100644 --- a/app/src/test/java/org/fdroid/updates/UpdateInstallerTest.kt +++ b/app/src/test/java/org/fdroid/updates/UpdateInstallerTest.kt @@ -62,23 +62,25 @@ internal class UpdateInstallerTest { } @Test - fun `updateAll returns early when updates list is empty`() = - testScope.runTest { - val installer = createUpdateInstaller() + fun `updateAll returns early when updates list is empty`() = testScope.runTest { + val installer = createUpdateInstaller() - installer.updateAll(emptyList(), canAskPreApprovalNow = false) - advanceUntilIdle() + installer.updateAll(emptyList(), canAskPreApprovalNow = false) + advanceUntilIdle() - coVerify(exactly = 0) { appInstallManager.install(any(), any(), any(), any(), any(), any()) } - verify(exactly = 0) { appInstallManager.setWaitingState(any(), any(), any(), any(), any()) } + coVerify(exactly = 0) { + appInstallManager.install(any(), any(), any(), any(), any(), any(), any()) } + verify(exactly = 0) { appInstallManager.setWaitingState(any(), any(), any(), any(), any()) } + } @Test fun `updateAll preApproval is true for single app and forced false for multiple`() = testScope.runTest { every { repoManager.getRepository(1L) } returns makeRepository() - coEvery { appInstallManager.install(any(), any(), any(), any(), any(), any()) } returns - mockk() + coEvery { + appInstallManager.install(any(), any(), any(), any(), any(), any(), any()) + } returns mockk() // single app + canAsk=true -> canAskPreApprovalNow=true val ver = makeAppVersion(versionName = "5.0", versionCode = 50) @@ -98,6 +100,7 @@ internal class UpdateInstallerTest { coVerify(exactly = 1) { appInstallManager.install( + packageName = "com.example.app", appMetadata = match { metadata -> metadata.packageName == "com.example.app" && metadata.repoId == 1L @@ -121,6 +124,7 @@ internal class UpdateInstallerTest { coVerify(exactly = 2) { appInstallManager.install( + packageName = any(), appMetadata = any(), version = any(), currentVersionName = any(), @@ -132,70 +136,72 @@ internal class UpdateInstallerTest { } @Test - fun `updateAll updates own app last and sets waiting state`() = - testScope.runTest { - val otherPkg = "com.example.other" - every { repoManager.getRepository(1L) } returns makeRepository() - coEvery { appInstallManager.install(any(), any(), any(), any(), any(), any()) } returns - mockk() + fun `updateAll updates own app last and sets waiting state`() = testScope.runTest { + val otherPkg = "com.example.other" + every { repoManager.getRepository(1L) } returns makeRepository() + coEvery { + appInstallManager.install(any(), any(), any(), any(), any(), any(), any()) + } returns mockk() - val ownVersion = - makeAppVersion(packageName = OWN_PACKAGE_NAME, versionName = "3.0", added = 9999L) - every { appDao.getApp(1L, OWN_PACKAGE_NAME) } returns makeApp(packageName = OWN_PACKAGE_NAME) - every { appDao.getApp(1L, otherPkg) } returns makeApp(packageName = otherPkg) - every { - appInstallManager.setWaitingState( + val ownVersion = + makeAppVersion(packageName = OWN_PACKAGE_NAME, versionName = "3.0", added = 9999L) + every { appDao.getApp(1L, OWN_PACKAGE_NAME) } returns makeApp(packageName = OWN_PACKAGE_NAME) + every { appDao.getApp(1L, otherPkg) } returns makeApp(packageName = otherPkg) + every { + appInstallManager.setWaitingState( + packageName = OWN_PACKAGE_NAME, + name = any(), + versionName = any(), + currentVersionName = any(), + lastUpdated = any(), + ) + } just runs + + val updates = + listOf( + makeAppUpdateItem( packageName = OWN_PACKAGE_NAME, - name = any(), - versionName = any(), - currentVersionName = any(), - lastUpdated = any(), - ) - } just runs + installedVersionName = "2.0", + update = ownVersion, + ), + makeAppUpdateItem(packageName = otherPkg), + ) - val updates = - listOf( - makeAppUpdateItem( - packageName = OWN_PACKAGE_NAME, - installedVersionName = "2.0", - update = ownVersion, - ), - makeAppUpdateItem(packageName = otherPkg), - ) + createUpdateInstaller().updateAll(updates, canAskPreApprovalNow = false) + advanceUntilIdle() - createUpdateInstaller().updateAll(updates, canAskPreApprovalNow = false) - advanceUntilIdle() - - verify(exactly = 1) { - appInstallManager.setWaitingState( - packageName = OWN_PACKAGE_NAME, - name = any(), - versionName = "3.0", - currentVersionName = "2.0", - lastUpdated = 9999L, - ) - } - - coVerifyOrder { - appInstallManager.install( - appMetadata = match { metadata -> metadata.packageName == otherPkg }, - version = any(), - currentVersionName = any(), - repo = any(), - iconModel = any(), - canAskPreApprovalNow = any(), - ) - appInstallManager.install( - appMetadata = match { metadata -> metadata.packageName == OWN_PACKAGE_NAME }, - version = any(), - currentVersionName = any(), - repo = any(), - iconModel = any(), - canAskPreApprovalNow = any(), - ) - } + verify(exactly = 1) { + appInstallManager.setWaitingState( + packageName = OWN_PACKAGE_NAME, + name = any(), + versionName = "3.0", + currentVersionName = "2.0", + lastUpdated = 9999L, + ) } + coVerifyOrder { + appInstallManager.install( + packageName = otherPkg, + appMetadata = match { metadata -> metadata.packageName == otherPkg }, + version = any(), + currentVersionName = any(), + repo = any(), + iconModel = any(), + canAskPreApprovalNow = any(), + ) + appInstallManager.install( + packageName = OWN_PACKAGE_NAME, + appMetadata = match { metadata -> metadata.packageName == OWN_PACKAGE_NAME }, + version = any(), + currentVersionName = any(), + repo = any(), + iconModel = any(), + canAskPreApprovalNow = any(), + ) + } + } + @Test fun `updateApp continues with null values if app missing in DB or repo missing`() = testScope.runTest { @@ -208,22 +214,27 @@ internal class UpdateInstallerTest { update = ver, ) ) - coEvery { appInstallManager.install(any(), any(), any(), any(), any(), any()) } returns - mockk() + coEvery { + appInstallManager.install(any(), any(), any(), any(), any(), any(), any()) + } returns mockk() // repo is null every { repoManager.getRepository(1L) } returns null every { appDao.getApp(1L, "com.example.app") } returns makeApp() createUpdateInstaller().updateAll(updates, canAskPreApprovalNow = false) advanceUntilIdle() - coVerify(exactly = 1) { appInstallManager.install(any(), any(), any(), null, any(), any()) } + coVerify(exactly = 1) { + appInstallManager.install(any(), any(), any(), any(), null, any(), any()) + } // app is null every { repoManager.getRepository(1L) } returns makeRepository() every { appDao.getApp(1L, "com.example.app") } returns null createUpdateInstaller().updateAll(updates, canAskPreApprovalNow = false) advanceUntilIdle() - coVerify(exactly = 1) { appInstallManager.install(null, any(), any(), any(), any(), any()) } + coVerify(exactly = 1) { + appInstallManager.install(any(), null, any(), any(), any(), any(), any()) + } } private fun makeAppUpdateItem(