Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Verified Commit c41932f3 authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

test(update): cover serialized update queueing

Add migration and queueing regressions for persisted update requests, serialized dispatch, and update-only completion handling.
parent 67df168a
Loading
Loading
Loading
Loading
+157 −0
Original line number Diff line number Diff line
@@ -88,6 +88,97 @@ class AppInstallDatabaseTest {
        }
    }

    @Test
    fun migration7To8_opensVersion7DatabaseAndAddsIsUpdateRequestColumn() {
        val legacyDatabaseName = trackDatabase("app_install_test_${System.nanoTime()}")
        createVersion7Database(legacyDatabaseName)

        val migratedDatabase = Room.databaseBuilder(
            context,
            AppInstallDatabase::class.java,
            legacyDatabaseName
        ).addMigrations(AppInstallDatabase.migration7To8)
            .allowMainThreadQueries()
            .build()

        try {
            migratedDatabase.openHelper.writableDatabase
                .query("PRAGMA table_info(`FusedDownload`)")
                .use { cursor ->
                    var foundIsUpdateRequestColumn = false
                    while (cursor.moveToNext()) {
                        if (cursor.getString(cursor.getColumnIndexOrThrow("name")) != "isUpdateRequest") {
                            continue
                        }

                        foundIsUpdateRequestColumn = true
                        assertThat(cursor.getString(cursor.getColumnIndexOrThrow("type")))
                            .isEqualTo("INTEGER")
                        assertThat(cursor.getInt(cursor.getColumnIndexOrThrow("notnull")))
                            .isEqualTo(1)
                        assertThat(cursor.getString(cursor.getColumnIndexOrThrow("dflt_value")))
                            .isEqualTo("0")
                    }

                    assertThat(foundIsUpdateRequestColumn).isTrue()
                }
        } finally {
            migratedDatabase.close()
        }
    }

    @Test
    fun migration7To8_keepsExistingNonUpdateRowsAsNotUpdateRequests() {
        val legacyDatabaseName = trackDatabase("app_install_test_${System.nanoTime()}")
        createVersion7Database(legacyDatabaseName, orgStatus = "AWAITING")

        val migratedDatabase = Room.databaseBuilder(
            context,
            AppInstallDatabase::class.java,
            legacyDatabaseName
        ).addMigrations(AppInstallDatabase.migration7To8)
            .allowMainThreadQueries()
            .build()

        try {
            migratedDatabase.openHelper.writableDatabase
                .query("SELECT `isUpdateRequest` FROM `FusedDownload` WHERE `id` = 'legacy-id'")
                .use { cursor ->
                    assertThat(cursor.moveToFirst()).isTrue()
                    assertThat(cursor.getInt(cursor.getColumnIndexOrThrow("isUpdateRequest")))
                        .isEqualTo(0)
                }
        } finally {
            migratedDatabase.close()
        }
    }

    @Test
    fun migration7To8_backfillsQueuedUpdateRowsAsUpdateRequests() {
        val legacyDatabaseName = trackDatabase("app_install_test_${System.nanoTime()}")
        createVersion7Database(legacyDatabaseName, orgStatus = "UPDATABLE")

        val migratedDatabase = Room.databaseBuilder(
            context,
            AppInstallDatabase::class.java,
            legacyDatabaseName
        ).addMigrations(AppInstallDatabase.migration7To8)
            .allowMainThreadQueries()
            .build()

        try {
            migratedDatabase.openHelper.writableDatabase
                .query("SELECT `isUpdateRequest` FROM `FusedDownload` WHERE `id` = 'legacy-id'")
                .use { cursor ->
                    assertThat(cursor.moveToFirst()).isTrue()
                    assertThat(cursor.getInt(cursor.getColumnIndexOrThrow("isUpdateRequest")))
                        .isEqualTo(1)
                }
        } finally {
            migratedDatabase.close()
        }
    }

    private fun createVersion6Database(databaseName: String) {
        context.deleteDatabase(databaseName)
        val databaseFile = context.getDatabasePath(databaseName)
@@ -130,6 +221,50 @@ class AppInstallDatabaseTest {
        }
    }

    private fun createVersion7Database(databaseName: String, orgStatus: String = "AWAITING") {
        context.deleteDatabase(databaseName)
        val databaseFile = context.getDatabasePath(databaseName)
        databaseFile.parentFile?.mkdirs()

        val database = SQLiteDatabase.openOrCreateDatabase(databaseFile, null)
        try {
            database.execSQL(VERSION_7_CREATE_TABLE_SQL)
            database.execSQL(
                """
                INSERT INTO `FusedDownload` (
                    `id`, `source`, `status`, `name`, `packageName`, `downloadURLList`,
                    `downloadIdMap`, `orgStatus`, `type`, `iconImageUrl`, `versionCode`,
                    `offerType`, `isFree`, `appSize`, `files`, `signature`, `contentRating`,
                    `sharedLibs`
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """.trimIndent(),
                arrayOf<Any>(
                    "legacy-id",
                    "PLAY_STORE",
                    "AWAITING",
                    "Legacy App",
                    "com.example.legacy",
                    "[]",
                    "{}",
                    orgStatus,
                    "NATIVE",
                    "icon.png",
                    123L,
                    0,
                    1,
                    2048L,
                    "[]",
                    "legacy-signature",
                    "{}",
                    "[]"
                )
            )
            database.version = 7
        } finally {
            database.close()
        }
    }

    private fun trackDatabase(name: String): String {
        databasesToDelete += name
        return name
@@ -156,5 +291,27 @@ class AppInstallDatabaseTest {
                "`signature` TEXT NOT NULL, " +
                "`contentRating` TEXT NOT NULL, " +
                "PRIMARY KEY(`id`))"

        private const val VERSION_7_CREATE_TABLE_SQL =
            "CREATE TABLE IF NOT EXISTS `FusedDownload` (" +
                "`id` TEXT NOT NULL, " +
                "`source` TEXT NOT NULL, " +
                "`status` TEXT NOT NULL, " +
                "`name` TEXT NOT NULL, " +
                "`packageName` TEXT NOT NULL, " +
                "`downloadURLList` TEXT NOT NULL, " +
                "`downloadIdMap` TEXT NOT NULL, " +
                "`orgStatus` TEXT NOT NULL, " +
                "`type` TEXT NOT NULL, " +
                "`iconImageUrl` TEXT NOT NULL, " +
                "`versionCode` INTEGER NOT NULL, " +
                "`offerType` INTEGER NOT NULL, " +
                "`isFree` INTEGER NOT NULL, " +
                "`appSize` INTEGER NOT NULL, " +
                "`files` TEXT NOT NULL, " +
                "`signature` TEXT NOT NULL, " +
                "`contentRating` TEXT NOT NULL, " +
                "`sharedLibs` TEXT NOT NULL, " +
                "PRIMARY KEY(`id`))"
    }
}
+58 −1
Original line number Diff line number Diff line
@@ -79,7 +79,7 @@ class AppInstallationFacadeTest {
    }

    @Test
    fun initAppInstall_computesUpdateFlagAndDelegates() = runTest {
    fun initAppInstall_marksUpdatableAppsAsUpdateRequests() = runTest {
        val application = Application(
            _id = "123",
            source = Source.PLAY_STORE,
@@ -106,6 +106,63 @@ class AppInstallationFacadeTest {
        coVerify { installationEnqueuer.enqueue(appInstall, true, application.isSystemApp) }
    }

    @Test
    fun initAppInstall_marksExplicitUpdatesAsUpdateRequests() = runTest {
        val application = Application(
            _id = "123",
            source = Source.PLAY_STORE,
            status = Status.INSTALLED,
            name = "Example",
            package_name = "com.example.app",
            type = Type.NATIVE,
            dependentLibraries = listOf(SharedLib(packageName = "com.example.lib", versionCode = 1L))
        )
        val appInstall = AppInstall(id = "123", packageName = "com.example.app")
        coEvery { installationRequest.create(application, isUpdateRequest = true) } returns appInstall
        coEvery {
            installationEnqueuer.enqueue(
                appInstall,
                true,
                application.isSystemApp
            )
        } returns true

        val result = appInstallationFacade.initAppInstall(application, isAnUpdate = true)

        assertTrue(result)
        coVerify { installationRequest.create(application, isUpdateRequest = true) }
        coVerify { installationEnqueuer.enqueue(appInstall, true, application.isSystemApp) }
    }

    @Test
    fun initAppInstall_doesNotMarkInstalledAppsAsUpdateRequestsFromInstallState() = runTest {
        val application = Application(
            _id = "123",
            source = Source.PLAY_STORE,
            status = Status.INSTALLED,
            name = "Example",
            package_name = "com.example.app",
            type = Type.NATIVE,
            dependentLibraries = listOf(SharedLib(packageName = "com.example.lib", versionCode = 1L))
        )
        val appInstall = AppInstall(id = "123", packageName = "com.example.app")
        coEvery { installationRequest.create(application, isUpdateRequest = false) } returns appInstall
        coEvery {
            installationEnqueuer.enqueue(
                appInstall,
                false,
                application.isSystemApp
            )
        } returns true

        val result = appInstallationFacade.initAppInstall(application)

        assertTrue(result)
        coVerify { installationRequest.create(application, isUpdateRequest = false) }
        coVerify { installationEnqueuer.enqueue(appInstall, false, application.isSystemApp) }
        coVerify(exactly = 0) { appManager.isAppInstalled(any()) }
    }

    @Test
    fun enqueueAppForInstallation_delegatesResult() = runTest {
        val appInstall = AppInstall(id = "123", packageName = "com.example.app")
+11 −0
Original line number Diff line number Diff line
@@ -91,6 +91,17 @@ class InstallationCompletionHandlerTest {
        verify(exactly = 0) { UpdatesNotifier.showNotification(any(), any<String>(), any<String>()) }
    }

    @Test
    fun onInstallFinished_ignoresNonUpdateRequestsEvenWhenUpdateWorkIsTrue() = runTest {
        val appInstall = AppInstall(id = "123", packageName = "com.example.app")

        handler.onInstallFinished(appInstall, true)

        verify(exactly = 0) { appInstallationManager.getInstallationStatus(any()) }
        assertTrue(UpdatesDao.successfulUpdatedApps.isEmpty())
        verify(exactly = 0) { UpdatesNotifier.showNotification(any(), any<String>(), any<String>()) }
    }

    @Test
    fun onInstallFinished_tracksInstalledUpdates() = runTest {
        val appInstall = AppInstall(id = "123", packageName = "com.example.app", isUpdateRequest = true)
+14 −7
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import foundation.e.apps.data.install.core.helper.DevicePreconditions
import foundation.e.apps.data.install.core.helper.DownloadUrlRefresher
import foundation.e.apps.data.install.core.helper.PreEnqueueChecker
import foundation.e.apps.data.install.notification.StorageNotificationManager
import foundation.e.apps.data.install.workmanager.InstallWorkManager
import foundation.e.apps.data.installation.model.AppInstall
import foundation.e.apps.data.installation.model.InstallationSource
import foundation.e.apps.data.installation.model.InstallationType
@@ -219,6 +220,8 @@ class InstallationEnqueuerTest {
    fun enqueue_marksUpdateAwaitingWhenUpdateAndChecksPass() = runTest {
        val appInstall = createPwaInstall()

        mockkObject(InstallWorkManager)
        try {
            coEvery { appManager.addDownload(appInstall) } returns true
            coEvery { ageLimiter.allow(appInstall) } returns true
            every { context.isNetworkAvailable() } returns true
@@ -228,6 +231,10 @@ class InstallationEnqueuerTest {

            assertTrue(result)
            coVerify { appManager.updateAwaiting(appInstall) }
            verify(exactly = 0) { InstallWorkManager.enqueueWork(any(), any(), any()) }
        } finally {
            unmockkObject(InstallWorkManager)
        }
    }

    @Test
+3 −1
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ class InstallationRequestTest {
            originalSize = 2048L
        )

        val appInstall = installationRequest.create(application)
        val appInstall = installationRequest.create(application, isUpdateRequest = true)

        assertEquals("123", appInstall.id)
        assertEquals(InstallationSource.PLAY_STORE, appInstall.source)
@@ -68,6 +68,8 @@ class InstallationRequestTest {
        assertEquals(1, appInstall.offerType)
        assertEquals(false, appInstall.isFree)
        assertEquals(2048L, appInstall.appSize)
        assertEquals(Status.AWAITING, appInstall.orgStatus)
        assertTrue(appInstall.isUpdateRequest)
    }

    @Test