Add current metadata to RestoreCoordinator state
so we know which backup version we need to expect during restore
This commit is contained in:
parent
bcb245531c
commit
5523e57fe7
8 changed files with 115 additions and 69 deletions
|
@ -3,7 +3,7 @@ package com.stevesoltys.seedvault.restore
|
||||||
import com.stevesoltys.seedvault.metadata.BackupMetadata
|
import com.stevesoltys.seedvault.metadata.BackupMetadata
|
||||||
import com.stevesoltys.seedvault.metadata.PackageMetadataMap
|
import com.stevesoltys.seedvault.metadata.PackageMetadataMap
|
||||||
|
|
||||||
data class RestorableBackup(private val backupMetadata: BackupMetadata) {
|
data class RestorableBackup(val backupMetadata: BackupMetadata) {
|
||||||
|
|
||||||
val name: String
|
val name: String
|
||||||
get() = backupMetadata.deviceName
|
get() = backupMetadata.deviceName
|
||||||
|
|
|
@ -148,6 +148,7 @@ internal class RestoreViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRestorableBackupClicked(restorableBackup: RestorableBackup) {
|
override fun onRestorableBackupClicked(restorableBackup: RestorableBackup) {
|
||||||
|
restoreCoordinator.beforeStartRestore(restorableBackup.backupMetadata)
|
||||||
mChosenRestorableBackup.value = restorableBackup
|
mChosenRestorableBackup.value = restorableBackup
|
||||||
mDisplayFragment.setEvent(RESTORE_APPS)
|
mDisplayFragment.setEvent(RESTORE_APPS)
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,8 +204,8 @@ class ConfigurableBackupTransport internal constructor(private val context: Cont
|
||||||
return restoreCoordinator.getCurrentRestoreSet()
|
return restoreCoordinator.getCurrentRestoreSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startRestore(token: Long, packages: Array<PackageInfo>): Int {
|
override fun startRestore(token: Long, packages: Array<PackageInfo>): Int = runBlocking {
|
||||||
return restoreCoordinator.startRestore(token, packages)
|
restoreCoordinator.startRestore(token, packages)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNextFullRestoreDataChunk(socket: ParcelFileDescriptor): Int = runBlocking {
|
override fun getNextFullRestoreDataChunk(socket: ParcelFileDescriptor): Int = runBlocking {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.stevesoltys.seedvault.transport.restore
|
||||||
|
|
||||||
import android.app.backup.BackupTransport.TRANSPORT_ERROR
|
import android.app.backup.BackupTransport.TRANSPORT_ERROR
|
||||||
import android.app.backup.BackupTransport.TRANSPORT_OK
|
import android.app.backup.BackupTransport.TRANSPORT_OK
|
||||||
import android.app.backup.IBackupManager
|
|
||||||
import android.app.backup.RestoreDescription
|
import android.app.backup.RestoreDescription
|
||||||
import android.app.backup.RestoreDescription.NO_MORE_PACKAGES
|
import android.app.backup.RestoreDescription.NO_MORE_PACKAGES
|
||||||
import android.app.backup.RestoreDescription.TYPE_FULL_STREAM
|
import android.app.backup.RestoreDescription.TYPE_FULL_STREAM
|
||||||
|
@ -12,7 +11,6 @@ import android.content.Context
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.collection.LongSparseArray
|
|
||||||
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
|
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
|
||||||
import com.stevesoltys.seedvault.R
|
import com.stevesoltys.seedvault.R
|
||||||
import com.stevesoltys.seedvault.header.UnsupportedVersionException
|
import com.stevesoltys.seedvault.header.UnsupportedVersionException
|
||||||
|
@ -31,7 +29,8 @@ private data class RestoreCoordinatorState(
|
||||||
/**
|
/**
|
||||||
* Optional [PackageInfo] for single package restore, to reduce data needed to read for @pm@
|
* Optional [PackageInfo] for single package restore, to reduce data needed to read for @pm@
|
||||||
*/
|
*/
|
||||||
val pmPackageInfo: PackageInfo?
|
val pmPackageInfo: PackageInfo?,
|
||||||
|
val backupMetadata: BackupMetadata
|
||||||
) {
|
) {
|
||||||
var currentPackage: String? = null
|
var currentPackage: String? = null
|
||||||
}
|
}
|
||||||
|
@ -51,7 +50,7 @@ internal class RestoreCoordinator(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private var state: RestoreCoordinatorState? = null
|
private var state: RestoreCoordinatorState? = null
|
||||||
private var backupMetadata: LongSparseArray<BackupMetadata>? = null
|
private var backupMetadata: BackupMetadata? = null
|
||||||
private val failedPackages = ArrayList<String>()
|
private val failedPackages = ArrayList<String>()
|
||||||
|
|
||||||
suspend fun getAvailableMetadata(): Map<Long, BackupMetadata>? {
|
suspend fun getAvailableMetadata(): Map<Long, BackupMetadata>? {
|
||||||
|
@ -113,19 +112,27 @@ internal class RestoreCoordinator(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this before starting the restore as an optimization to prevent re-fetching metadata.
|
||||||
|
*/
|
||||||
|
fun beforeStartRestore(backupMetadata: BackupMetadata) {
|
||||||
|
this.backupMetadata = backupMetadata
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start restoring application data from backup.
|
* Start restoring application data from backup.
|
||||||
* After calling this function,
|
* After calling this function,
|
||||||
* there will be alternate calls to [nextRestorePackage] and [getRestoreData]
|
* there will be alternate calls to [nextRestorePackage] and [getRestoreData]
|
||||||
* to walk through the actual application data.
|
* to walk through the actual application data.
|
||||||
*
|
*
|
||||||
* @param token A backup token as returned by [getAvailableRestoreSets] or [getCurrentRestoreSet].
|
* @param token A backup token as returned by [getAvailableRestoreSets]
|
||||||
|
* or [getCurrentRestoreSet].
|
||||||
* @param packages List of applications to restore (if data is available).
|
* @param packages List of applications to restore (if data is available).
|
||||||
* Application data will be restored in the order given.
|
* Application data will be restored in the order given.
|
||||||
* @return One of [TRANSPORT_OK] (OK so far, call [nextRestorePackage])
|
* @return One of [TRANSPORT_OK] (OK so far, call [nextRestorePackage])
|
||||||
* or [TRANSPORT_ERROR] (an error occurred, the restore should be aborted and rescheduled).
|
* or [TRANSPORT_ERROR] (an error occurred, the restore should be aborted and rescheduled).
|
||||||
*/
|
*/
|
||||||
fun startRestore(token: Long, packages: Array<out PackageInfo>): Int {
|
suspend fun startRestore(token: Long, packages: Array<out PackageInfo>): Int {
|
||||||
check(state == null) { "Started new restore with existing state: $state" }
|
check(state == null) { "Started new restore with existing state: $state" }
|
||||||
Log.i(TAG, "Start restore with ${packages.map { info -> info.packageName }}")
|
Log.i(TAG, "Start restore with ${packages.map { info -> info.packageName }}")
|
||||||
|
|
||||||
|
@ -151,7 +158,13 @@ internal class RestoreCoordinator(
|
||||||
packages[1]
|
packages[1]
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
state = RestoreCoordinatorState(token, packages.iterator(), pmPackageInfo)
|
val metadata = if (backupMetadata?.token == token) {
|
||||||
|
backupMetadata!! // if token matches, backupMetadata is non-null
|
||||||
|
} else {
|
||||||
|
getAvailableMetadata()?.get(token) ?: return TRANSPORT_ERROR
|
||||||
|
}
|
||||||
|
state = RestoreCoordinatorState(token, packages.iterator(), pmPackageInfo, metadata)
|
||||||
|
backupMetadata = null
|
||||||
failedPackages.clear()
|
failedPackages.clear()
|
||||||
return TRANSPORT_OK
|
return TRANSPORT_OK
|
||||||
}
|
}
|
||||||
|
@ -269,18 +282,6 @@ internal class RestoreCoordinator(
|
||||||
state = null
|
state = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this after calling [IBackupManager.getAvailableRestoreTokenForUser]
|
|
||||||
* to retrieve additional [BackupMetadata] that is not available in [RestoreSet].
|
|
||||||
*
|
|
||||||
* It will also clear the saved metadata, so that subsequent calls will return null.
|
|
||||||
*/
|
|
||||||
fun getAndClearBackupMetadata(): LongSparseArray<BackupMetadata>? {
|
|
||||||
val result = backupMetadata
|
|
||||||
backupMetadata = null
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isFailedPackage(packageName: String) = packageName in failedPackages
|
fun isFailedPackage(packageName: String) = packageName in failedPackages
|
||||||
|
|
||||||
// TODO this is plugin specific, needs to be factored out when supporting different plugins
|
// TODO this is plugin specific, needs to be factored out when supporting different plugins
|
||||||
|
|
|
@ -181,6 +181,7 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
||||||
assertEquals(TRANSPORT_OK, backup.finishBackup())
|
assertEquals(TRANSPORT_OK, backup.finishBackup())
|
||||||
|
|
||||||
// start restore
|
// start restore
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
assertEquals(TRANSPORT_OK, restore.startRestore(token, arrayOf(packageInfo)))
|
assertEquals(TRANSPORT_OK, restore.startRestore(token, arrayOf(packageInfo)))
|
||||||
|
|
||||||
// find data for K/V backup
|
// find data for K/V backup
|
||||||
|
@ -251,6 +252,7 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
||||||
assertEquals(TRANSPORT_OK, backup.finishBackup())
|
assertEquals(TRANSPORT_OK, backup.finishBackup())
|
||||||
|
|
||||||
// start restore
|
// start restore
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
assertEquals(TRANSPORT_OK, restore.startRestore(token, arrayOf(packageInfo)))
|
assertEquals(TRANSPORT_OK, restore.startRestore(token, arrayOf(packageInfo)))
|
||||||
|
|
||||||
// find data for K/V backup
|
// find data for K/V backup
|
||||||
|
@ -311,6 +313,7 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
||||||
assertEquals(TRANSPORT_OK, backup.finishBackup())
|
assertEquals(TRANSPORT_OK, backup.finishBackup())
|
||||||
|
|
||||||
// start restore
|
// start restore
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
assertEquals(TRANSPORT_OK, restore.startRestore(token, arrayOf(packageInfo)))
|
assertEquals(TRANSPORT_OK, restore.startRestore(token, arrayOf(packageInfo)))
|
||||||
|
|
||||||
// find data only for full backup
|
// find data only for full backup
|
||||||
|
|
|
@ -10,6 +10,8 @@ import android.util.Log
|
||||||
import com.stevesoltys.seedvault.Clock
|
import com.stevesoltys.seedvault.Clock
|
||||||
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
|
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
|
||||||
import com.stevesoltys.seedvault.crypto.Crypto
|
import com.stevesoltys.seedvault.crypto.Crypto
|
||||||
|
import com.stevesoltys.seedvault.getRandomString
|
||||||
|
import com.stevesoltys.seedvault.metadata.BackupMetadata
|
||||||
import com.stevesoltys.seedvault.metadata.MetadataManager
|
import com.stevesoltys.seedvault.metadata.MetadataManager
|
||||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
@ -41,6 +43,12 @@ internal abstract class TransportTest {
|
||||||
protected val pmPackageInfo = PackageInfo().apply {
|
protected val pmPackageInfo = PackageInfo().apply {
|
||||||
packageName = MAGIC_PACKAGE_MANAGER
|
packageName = MAGIC_PACKAGE_MANAGER
|
||||||
}
|
}
|
||||||
|
protected val metadata = BackupMetadata(
|
||||||
|
token = token,
|
||||||
|
androidVersion = Random.nextInt(),
|
||||||
|
androidIncremental = getRandomString(),
|
||||||
|
deviceName = getRandomString()
|
||||||
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
mockkStatic(Log::class)
|
mockkStatic(Log::class)
|
||||||
|
|
|
@ -10,7 +10,6 @@ import android.content.pm.PackageInfo
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import com.stevesoltys.seedvault.coAssertThrows
|
import com.stevesoltys.seedvault.coAssertThrows
|
||||||
import com.stevesoltys.seedvault.getRandomString
|
import com.stevesoltys.seedvault.getRandomString
|
||||||
import com.stevesoltys.seedvault.metadata.BackupMetadata
|
|
||||||
import com.stevesoltys.seedvault.metadata.EncryptedBackupMetadata
|
import com.stevesoltys.seedvault.metadata.EncryptedBackupMetadata
|
||||||
import com.stevesoltys.seedvault.metadata.MetadataReader
|
import com.stevesoltys.seedvault.metadata.MetadataReader
|
||||||
import com.stevesoltys.seedvault.metadata.PackageMetadata
|
import com.stevesoltys.seedvault.metadata.PackageMetadata
|
||||||
|
@ -26,7 +25,6 @@ import io.mockk.verify
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||||
import org.junit.jupiter.api.Assertions.assertNull
|
|
||||||
import org.junit.jupiter.api.Assertions.assertThrows
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
import org.junit.jupiter.api.Assertions.fail
|
import org.junit.jupiter.api.Assertions.fail
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
@ -69,18 +67,13 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `getAvailableRestoreSets() builds set from plugin response`() = runBlocking {
|
fun `getAvailableRestoreSets() builds set from plugin response`() = runBlocking {
|
||||||
val encryptedMetadata = EncryptedBackupMetadata(token, inputStream)
|
val encryptedMetadata = EncryptedBackupMetadata(token, inputStream)
|
||||||
val metadata = BackupMetadata(
|
|
||||||
token = token,
|
|
||||||
androidVersion = Random.nextInt(),
|
|
||||||
androidIncremental = getRandomString(),
|
|
||||||
deviceName = getRandomString()
|
|
||||||
)
|
|
||||||
|
|
||||||
coEvery { plugin.getAvailableBackups() } returns sequenceOf(
|
coEvery { plugin.getAvailableBackups() } returns sequenceOf(
|
||||||
encryptedMetadata,
|
encryptedMetadata,
|
||||||
encryptedMetadata
|
EncryptedBackupMetadata(token + 1, inputStream)
|
||||||
)
|
)
|
||||||
every { metadataReader.readMetadata(inputStream, token) } returns metadata
|
every { metadataReader.readMetadata(inputStream, token) } returns metadata
|
||||||
|
every { metadataReader.readMetadata(inputStream, token + 1) } returns metadata
|
||||||
every { inputStream.close() } just Runs
|
every { inputStream.close() } just Runs
|
||||||
|
|
||||||
val sets = restore.getAvailableRestoreSets() ?: fail()
|
val sets = restore.getAvailableRestoreSets() ?: fail()
|
||||||
|
@ -97,68 +90,102 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `startRestore() returns OK`() {
|
fun `startRestore() returns OK`() = runBlocking {
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
assertEquals(TRANSPORT_OK, restore.startRestore(token, packageInfoArray))
|
assertEquals(TRANSPORT_OK, restore.startRestore(token, packageInfoArray))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `startRestore() can not be called twice`() {
|
fun `startRestore() fetches metadata if missing`() = runBlocking {
|
||||||
|
coEvery { plugin.getAvailableBackups() } returns sequenceOf(
|
||||||
|
EncryptedBackupMetadata(token, inputStream),
|
||||||
|
EncryptedBackupMetadata(token + 1, inputStream)
|
||||||
|
)
|
||||||
|
every { metadataReader.readMetadata(inputStream, token) } returns metadata
|
||||||
|
every { metadataReader.readMetadata(inputStream, token + 1) } returns metadata
|
||||||
|
every { inputStream.close() } just Runs
|
||||||
|
|
||||||
|
assertEquals(TRANSPORT_OK, restore.startRestore(token, packageInfoArray))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `startRestore() errors if metadata is not matching token`() = runBlocking {
|
||||||
|
coEvery { plugin.getAvailableBackups() } returns sequenceOf(
|
||||||
|
EncryptedBackupMetadata(token + 42, inputStream)
|
||||||
|
)
|
||||||
|
every { metadataReader.readMetadata(inputStream, token + 42) } returns metadata
|
||||||
|
every { inputStream.close() } just Runs
|
||||||
|
|
||||||
|
assertEquals(TRANSPORT_ERROR, restore.startRestore(token, packageInfoArray))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `startRestore() can not be called twice`() = runBlocking {
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
assertEquals(TRANSPORT_OK, restore.startRestore(token, packageInfoArray))
|
assertEquals(TRANSPORT_OK, restore.startRestore(token, packageInfoArray))
|
||||||
assertThrows(IllegalStateException::class.javaObjectType) {
|
assertThrows(IllegalStateException::class.javaObjectType) {
|
||||||
restore.startRestore(token, packageInfoArray)
|
runBlocking {
|
||||||
|
restore.startRestore(token, packageInfoArray)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `startRestore() can be be called again after restore finished`() {
|
fun `startRestore() can be be called again after restore finished`() = runBlocking {
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
assertEquals(TRANSPORT_OK, restore.startRestore(token, packageInfoArray))
|
assertEquals(TRANSPORT_OK, restore.startRestore(token, packageInfoArray))
|
||||||
|
|
||||||
every { full.hasState() } returns false
|
every { full.hasState() } returns false
|
||||||
restore.finishRestore()
|
restore.finishRestore()
|
||||||
|
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
assertEquals(TRANSPORT_OK, restore.startRestore(token, packageInfoArray))
|
assertEquals(TRANSPORT_OK, restore.startRestore(token, packageInfoArray))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `startRestore() optimized auto-restore with removed storage shows notification`() {
|
fun `startRestore() optimized auto-restore with removed storage shows notification`() =
|
||||||
every { settingsManager.getStorage() } returns storage
|
runBlocking {
|
||||||
every { storage.isUnavailableUsb(context) } returns true
|
every { settingsManager.getStorage() } returns storage
|
||||||
every { metadataManager.getPackageMetadata(packageName) } returns PackageMetadata(42L)
|
every { storage.isUnavailableUsb(context) } returns true
|
||||||
every { storage.name } returns storageName
|
every { metadataManager.getPackageMetadata(packageName) } returns PackageMetadata(42L)
|
||||||
every {
|
every { storage.name } returns storageName
|
||||||
notificationManager.onRemovableStorageNotAvailableForRestore(
|
every {
|
||||||
packageName,
|
notificationManager.onRemovableStorageNotAvailableForRestore(
|
||||||
storageName
|
packageName,
|
||||||
)
|
storageName
|
||||||
} just Runs
|
)
|
||||||
|
} just Runs
|
||||||
|
|
||||||
assertEquals(TRANSPORT_ERROR, restore.startRestore(token, pmPackageInfoArray))
|
assertEquals(TRANSPORT_ERROR, restore.startRestore(token, pmPackageInfoArray))
|
||||||
|
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
notificationManager.onRemovableStorageNotAvailableForRestore(
|
notificationManager.onRemovableStorageNotAvailableForRestore(
|
||||||
packageName,
|
packageName,
|
||||||
storageName
|
storageName
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `startRestore() optimized auto-restore with available storage shows no notification`() {
|
fun `startRestore() optimized auto-restore with available storage shows no notification`() =
|
||||||
every { settingsManager.getStorage() } returns storage
|
runBlocking {
|
||||||
every { storage.isUnavailableUsb(context) } returns false
|
every { settingsManager.getStorage() } returns storage
|
||||||
|
every { storage.isUnavailableUsb(context) } returns false
|
||||||
|
|
||||||
assertEquals(TRANSPORT_OK, restore.startRestore(token, pmPackageInfoArray))
|
restore.beforeStartRestore(metadata)
|
||||||
|
assertEquals(TRANSPORT_OK, restore.startRestore(token, pmPackageInfoArray))
|
||||||
|
|
||||||
verify(exactly = 0) {
|
verify(exactly = 0) {
|
||||||
notificationManager.onRemovableStorageNotAvailableForRestore(
|
notificationManager.onRemovableStorageNotAvailableForRestore(
|
||||||
packageName,
|
packageName,
|
||||||
storageName
|
storageName
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `startRestore() with removed storage shows no notification`() {
|
fun `startRestore() with removed storage shows no notification`() = runBlocking {
|
||||||
every { settingsManager.getStorage() } returns storage
|
every { settingsManager.getStorage() } returns storage
|
||||||
every { storage.isUnavailableUsb(context) } returns true
|
every { storage.isUnavailableUsb(context) } returns true
|
||||||
every { metadataManager.getPackageMetadata(packageName) } returns null
|
every { metadataManager.getPackageMetadata(packageName) } returns null
|
||||||
|
@ -182,6 +209,7 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `nextRestorePackage() returns KV description and takes precedence`() = runBlocking {
|
fun `nextRestorePackage() returns KV description and takes precedence`() = runBlocking {
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
restore.startRestore(token, packageInfoArray)
|
restore.startRestore(token, packageInfoArray)
|
||||||
|
|
||||||
coEvery { kv.hasDataForPackage(token, packageInfo) } returns true
|
coEvery { kv.hasDataForPackage(token, packageInfo) } returns true
|
||||||
|
@ -193,6 +221,7 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `nextRestorePackage() returns full description if no KV data found`() = runBlocking {
|
fun `nextRestorePackage() returns full description if no KV data found`() = runBlocking {
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
restore.startRestore(token, packageInfoArray)
|
restore.startRestore(token, packageInfoArray)
|
||||||
|
|
||||||
coEvery { kv.hasDataForPackage(token, packageInfo) } returns false
|
coEvery { kv.hasDataForPackage(token, packageInfo) } returns false
|
||||||
|
@ -205,6 +234,7 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `nextRestorePackage() returns NO_MORE_PACKAGES if data found`() = runBlocking {
|
fun `nextRestorePackage() returns NO_MORE_PACKAGES if data found`() = runBlocking {
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
restore.startRestore(token, packageInfoArray)
|
restore.startRestore(token, packageInfoArray)
|
||||||
|
|
||||||
coEvery { kv.hasDataForPackage(token, packageInfo) } returns false
|
coEvery { kv.hasDataForPackage(token, packageInfo) } returns false
|
||||||
|
@ -215,6 +245,7 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `nextRestorePackage() returns all packages from startRestore()`() = runBlocking {
|
fun `nextRestorePackage() returns all packages from startRestore()`() = runBlocking {
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
restore.startRestore(token, packageInfoArray2)
|
restore.startRestore(token, packageInfoArray2)
|
||||||
|
|
||||||
coEvery { kv.hasDataForPackage(token, packageInfo) } returns true
|
coEvery { kv.hasDataForPackage(token, packageInfo) } returns true
|
||||||
|
@ -234,22 +265,24 @@ internal class RestoreCoordinatorTest : TransportTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when kv#hasDataForPackage() throws return null`() = runBlocking {
|
fun `when kv#hasDataForPackage() throws, it tries next package`() = runBlocking {
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
restore.startRestore(token, packageInfoArray)
|
restore.startRestore(token, packageInfoArray)
|
||||||
|
|
||||||
coEvery { kv.hasDataForPackage(token, packageInfo) } throws IOException()
|
coEvery { kv.hasDataForPackage(token, packageInfo) } throws IOException()
|
||||||
|
|
||||||
assertNull(restore.nextRestorePackage())
|
assertEquals(NO_MORE_PACKAGES, restore.nextRestorePackage())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when full#hasDataForPackage() throws return null`() = runBlocking {
|
fun `when full#hasDataForPackage() throws, it tries next package`() = runBlocking {
|
||||||
|
restore.beforeStartRestore(metadata)
|
||||||
restore.startRestore(token, packageInfoArray)
|
restore.startRestore(token, packageInfoArray)
|
||||||
|
|
||||||
coEvery { kv.hasDataForPackage(token, packageInfo) } returns false
|
coEvery { kv.hasDataForPackage(token, packageInfo) } returns false
|
||||||
coEvery { full.hasDataForPackage(token, packageInfo) } throws IOException()
|
coEvery { full.hasDataForPackage(token, packageInfo) } throws IOException()
|
||||||
|
|
||||||
assertNull(restore.nextRestorePackage())
|
assertEquals(NO_MORE_PACKAGES, restore.nextRestorePackage())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -61,7 +61,7 @@ internal class RestoreV0IntegrationTest : TransportTest() {
|
||||||
kvRestore,
|
kvRestore,
|
||||||
fullRestore,
|
fullRestore,
|
||||||
metadataReader
|
metadataReader
|
||||||
)
|
).apply { beforeStartRestore(metadata) }
|
||||||
|
|
||||||
private val fileDescriptor = mockk<ParcelFileDescriptor>(relaxed = true)
|
private val fileDescriptor = mockk<ParcelFileDescriptor>(relaxed = true)
|
||||||
private val appData = ("562AB665C3543120FC794D7CDA3AC18E5959235A4D" +
|
private val appData = ("562AB665C3543120FC794D7CDA3AC18E5959235A4D" +
|
||||||
|
|
Loading…
Reference in a new issue