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

Commit 56b41efd authored by Abhishek Aggarwal's avatar Abhishek Aggarwal Committed by Jonathan Klee
Browse files

fix(download): emit stable progress snapshots

parent f9600fa1
Loading
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ class DownloadProgressTracker @Inject constructor(
        appInstall: AppInstall,
        progress: DownloadProgress
    ): Int {
        val downloadIds = appInstall.downloadIdMap.keys
        val downloadIds = appInstall.downloadIdMap.keys.toSet()
        if (downloadIds.isEmpty()) {
            return 0
        }
@@ -85,12 +85,13 @@ class DownloadProgressTracker @Inject constructor(
        application?.let { app ->
            val appDownload = appManager.getDownloadList()
                .singleOrNull { it.id.contentEquals(app._id) }
            val downloadIds = appDownload?.downloadIdMap?.keys?.toSet().orEmpty()
            val downloadingMap = progress.totalSizeBytes.filter { item ->
                appDownload?.downloadIdMap?.keys?.contains(item.key) == true
                downloadIds.contains(item.key)
            }
            val totalSizeBytes = downloadingMap.values.sum()
            val downloadedSoFar = progress.bytesDownloadedSoFar.filter { item ->
                appDownload?.downloadIdMap?.keys?.contains(item.key) == true
                downloadIds.contains(item.key)
            }.values.sum()

            return Pair(totalSizeBytes, downloadedSoFar)
+9 −1
Original line number Diff line number Diff line
@@ -23,4 +23,12 @@ data class DownloadProgress(
    var bytesDownloadedSoFar: MutableMap<Long, Long> = mutableMapOf(),
    var status: MutableMap<Long, Boolean> = mutableMapOf(),
    var downloadId: Long = -1
) {
    fun snapshot(): DownloadProgress {
        return copy(
            totalSizeBytes = totalSizeBytes.toMutableMap(),
            bytesDownloadedSoFar = bytesDownloadedSoFar.toMutableMap(),
            status = status.toMutableMap(),
        )
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ class DownloadProgressLD @Inject constructor(
            }
            ?: run {
                Timber.w("DownloadManager returned null cursor for ids $downloadingIds")
                postValue(downloadProgress)
                postValue(downloadProgress.snapshot())
            }
    }

@@ -96,7 +96,7 @@ class DownloadProgressLD @Inject constructor(
        downloadingIds: List<Long>
    ) {
        if (!cursor.moveToFirst()) {
            postValue(downloadProgress)
            postValue(downloadProgress.snapshot())
            return
        }

@@ -130,7 +130,7 @@ class DownloadProgressLD @Inject constructor(
            }
        } while (cursor.moveToNext())

        postValue(downloadProgress)
        postValue(downloadProgress.snapshot())
    }

    override fun onInactive() {
+60 −12
Original line number Diff line number Diff line
@@ -39,19 +39,12 @@ class DownloadProgressLDTest {

    @Test
    fun processCursor_updatesProgressForSuccessfulDownload() {
        val cursor: Cursor = mock()
        whenever(cursor.moveToFirst()).thenReturn(true)
        whenever(cursor.moveToNext()).thenReturn(false)
        whenever(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID)).thenReturn(0)
        whenever(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)).thenReturn(1)
        whenever(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)).thenReturn(2)
        whenever(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)).thenReturn(
            3
        val cursor = progressCursor(
            id = 10L,
            status = DownloadManager.STATUS_SUCCESSFUL,
            totalSizeBytes = 200L,
            bytesDownloadedSoFar = 50L,
        )
        whenever(cursor.getLong(0)).thenReturn(10L)
        whenever(cursor.getInt(1)).thenReturn(DownloadManager.STATUS_SUCCESSFUL)
        whenever(cursor.getLong(2)).thenReturn(200L)
        whenever(cursor.getLong(3)).thenReturn(50L)

        var emitted: DownloadProgress? = null
        downloadProgressLD.observeForever { emitted = it }
@@ -63,4 +56,59 @@ class DownloadProgressLDTest {
        assertThat(emitted?.totalSizeBytes?.get(10L)).isEqualTo(200L)
        assertThat(emitted?.status?.get(10L)).isTrue()
    }

    @Test
    fun processCursor_emitsSnapshotThatIsNotMutatedByLaterProgressUpdates() {
        var emitted: DownloadProgress? = null
        downloadProgressLD.observeForever { emitted = it }

        processCursor.invoke(
            downloadProgressLD,
            progressCursor(
                id = 10L,
                status = DownloadManager.STATUS_RUNNING,
                totalSizeBytes = 200L,
                bytesDownloadedSoFar = 50L,
            ),
            listOf(10L),
        )
        val firstEmission = requireNotNull(emitted)

        processCursor.invoke(
            downloadProgressLD,
            progressCursor(
                id = 10L,
                status = DownloadManager.STATUS_RUNNING,
                totalSizeBytes = 200L,
                bytesDownloadedSoFar = 100L,
            ),
            listOf(10L),
        )

        assertThat(firstEmission.bytesDownloadedSoFar[10L]).isEqualTo(50L)
        assertThat(emitted).isNotSameInstanceAs(firstEmission)
        assertThat(emitted?.bytesDownloadedSoFar?.get(10L)).isEqualTo(100L)
    }

    private fun progressCursor(
        id: Long,
        status: Int,
        totalSizeBytes: Long,
        bytesDownloadedSoFar: Long,
    ): Cursor {
        val cursor: Cursor = mock()
        whenever(cursor.moveToFirst()).thenReturn(true)
        whenever(cursor.moveToNext()).thenReturn(false)
        whenever(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID)).thenReturn(0)
        whenever(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)).thenReturn(1)
        whenever(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)).thenReturn(2)
        whenever(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)).thenReturn(
            3
        )
        whenever(cursor.getLong(0)).thenReturn(id)
        whenever(cursor.getInt(1)).thenReturn(status)
        whenever(cursor.getLong(2)).thenReturn(totalSizeBytes)
        whenever(cursor.getLong(3)).thenReturn(bytesDownloadedSoFar)
        return cursor
    }
}