Use new storage API for full backups

This commit is contained in:
Torsten Grote 2021-09-16 18:56:23 +02:00 committed by Chirayu Desai
parent 4bdaaa0ce9
commit 674568ca11
9 changed files with 93 additions and 59 deletions

View file

@ -219,6 +219,9 @@ internal class MetadataManager(
private val mLastBackupTime = MutableLiveData<Long>() private val mLastBackupTime = MutableLiveData<Long>()
internal val lastBackupTime: LiveData<Long> = mLastBackupTime.distinctUntilChanged() internal val lastBackupTime: LiveData<Long> = mLastBackupTime.distinctUntilChanged()
internal val salt: String
@Synchronized get() = metadata.salt
internal val isLegacyFormat: Boolean internal val isLegacyFormat: Boolean
@Synchronized get() = metadata.version < VERSION @Synchronized get() = metadata.version < VERSION

View file

@ -310,7 +310,9 @@ internal class BackupCoordinator(
flags: Int flags: Int
): Int { ): Int {
state.cancelReason = UNKNOWN_ERROR state.cancelReason = UNKNOWN_ERROR
return full.performFullBackup(targetPackage, fileDescriptor, flags) val token = settingsManager.getToken() ?: error("no token in performFullBackup")
val salt = metadataManager.salt
return full.performFullBackup(targetPackage, fileDescriptor, flags, token, salt)
} }
suspend fun sendBackupData(numBytes: Int) = full.sendBackupData(numBytes) suspend fun sendBackupData(numBytes: Int) = full.sendBackupData(numBytes)
@ -336,7 +338,9 @@ internal class BackupCoordinator(
" because of ${state.cancelReason}" " because of ${state.cancelReason}"
) )
onPackageBackupError(packageInfo, BackupType.FULL) onPackageBackupError(packageInfo, BackupType.FULL)
full.cancelFullBackup() val token = settingsManager.getToken() ?: error("no token in cancelFullBackup")
val salt = metadataManager.salt
full.cancelFullBackup(token, salt)
} }
// Clear and Finish // Clear and Finish
@ -353,6 +357,8 @@ internal class BackupCoordinator(
suspend fun clearBackupData(packageInfo: PackageInfo): Int { suspend fun clearBackupData(packageInfo: PackageInfo): Int {
val packageName = packageInfo.packageName val packageName = packageInfo.packageName
Log.i(TAG, "Clear Backup Data of $packageName.") Log.i(TAG, "Clear Backup Data of $packageName.")
val token = settingsManager.getToken() ?: error("no token in clearBackupData")
val salt = metadataManager.salt
try { try {
kv.clearBackupData(packageInfo) kv.clearBackupData(packageInfo)
} catch (e: IOException) { } catch (e: IOException) {
@ -360,7 +366,7 @@ internal class BackupCoordinator(
return TRANSPORT_ERROR return TRANSPORT_ERROR
} }
try { try {
full.clearBackupData(packageInfo) full.clearBackupData(packageInfo, token, salt)
} catch (e: IOException) { } catch (e: IOException) {
Log.w(TAG, "Error clearing full backup data for $packageName", e) Log.w(TAG, "Error clearing full backup data for $packageName", e)
return TRANSPORT_ERROR return TRANSPORT_ERROR

View file

@ -29,7 +29,7 @@ val backupModule = module {
} }
single { single {
FullBackup( FullBackup(
plugin = get<BackupPlugin>().fullBackupPlugin, plugin = get(),
settingsManager = get(), settingsManager = get(),
inputFactory = get(), inputFactory = get(),
crypto = get() crypto = get()

View file

@ -38,7 +38,7 @@ private val TAG = FullBackup::class.java.simpleName
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
internal class FullBackup( internal class FullBackup(
private val plugin: FullBackupPlugin, private val plugin: BackupPlugin,
private val settingsManager: SettingsManager, private val settingsManager: SettingsManager,
private val inputFactory: InputFactory, private val inputFactory: InputFactory,
private val crypto: Crypto private val crypto: Crypto
@ -51,7 +51,7 @@ internal class FullBackup(
fun getCurrentPackage() = state?.packageInfo fun getCurrentPackage() = state?.packageInfo
fun getQuota(): Long { fun getQuota(): Long {
return if (settingsManager.isQuotaUnlimited()) Long.MAX_VALUE else plugin.getQuota() return if (settingsManager.isQuotaUnlimited()) Long.MAX_VALUE else DEFAULT_QUOTA_FULL_BACKUP
} }
fun checkFullBackupSize(size: Long): Int { fun checkFullBackupSize(size: Long): Int {
@ -101,20 +101,24 @@ internal class FullBackup(
suspend fun performFullBackup( suspend fun performFullBackup(
targetPackage: PackageInfo, targetPackage: PackageInfo,
socket: ParcelFileDescriptor, socket: ParcelFileDescriptor,
@Suppress("UNUSED_PARAMETER") flags: Int = 0 @Suppress("UNUSED_PARAMETER") flags: Int = 0,
token: Long,
salt: String
): Int { ): Int {
if (state != null) throw AssertionError() if (state != null) throw AssertionError()
Log.i(TAG, "Perform full backup for ${targetPackage.packageName}.") val packageName = targetPackage.packageName
Log.i(TAG, "Perform full backup for $packageName.")
// create new state // create new state
val inputStream = inputFactory.getInputStream(socket) val inputStream = inputFactory.getInputStream(socket)
state = FullBackupState(targetPackage, socket, inputStream) { state = FullBackupState(targetPackage, socket, inputStream) {
Log.d(TAG, "Initializing OutputStream for ${targetPackage.packageName}.") Log.d(TAG, "Initializing OutputStream for $packageName.")
val name = crypto.getNameForPackage(salt, packageName)
// get OutputStream to write backup data into // get OutputStream to write backup data into
val outputStream = try { val outputStream = try {
plugin.getOutputStream(targetPackage) plugin.getOutputStream(token, name)
} catch (e: IOException) { } catch (e: IOException) {
"Error getting OutputStream for full backup of ${targetPackage.packageName}".let { "Error getting OutputStream for full backup of $packageName".let {
Log.e(TAG, it, e) Log.e(TAG, it, e)
} }
throw(e) throw(e)
@ -167,15 +171,16 @@ internal class FullBackup(
} }
@Throws(IOException::class) @Throws(IOException::class)
suspend fun clearBackupData(packageInfo: PackageInfo) { suspend fun clearBackupData(packageInfo: PackageInfo, token: Long, salt: String) {
plugin.removeDataOfPackage(packageInfo) val name = crypto.getNameForPackage(salt, packageInfo.packageName)
plugin.removeData(token, name)
} }
suspend fun cancelFullBackup() { suspend fun cancelFullBackup(token: Long, salt: String) {
Log.i(TAG, "Cancel full backup") Log.i(TAG, "Cancel full backup")
val state = this.state ?: throw AssertionError("No state when canceling") val state = this.state ?: throw AssertionError("No state when canceling")
try { try {
plugin.removeDataOfPackage(state.packageInfo) clearBackupData(state.packageInfo, token, salt)
} catch (e: IOException) { } catch (e: IOException) {
Log.w(TAG, "Error cancelling full backup for ${state.packageName}", e) Log.w(TAG, "Error cancelling full backup for ${state.packageName}", e)
} }

View file

@ -73,7 +73,7 @@ internal class CoordinatorIntegrationTest : TransportTest() {
) )
private val fullBackupPlugin = mockk<FullBackupPlugin>() private val fullBackupPlugin = mockk<FullBackupPlugin>()
private val fullBackup = FullBackup( private val fullBackup = FullBackup(
plugin = fullBackupPlugin, plugin = backupPlugin,
settingsManager = settingsManager, settingsManager = settingsManager,
inputFactory = inputFactory, inputFactory = inputFactory,
crypto = cryptoImpl crypto = cryptoImpl
@ -295,11 +295,13 @@ internal class CoordinatorIntegrationTest : TransportTest() {
val packageMetadata = metadata.packageMetadataMap[packageInfo.packageName]!! val packageMetadata = metadata.packageMetadataMap[packageInfo.packageName]!!
metadata.packageMetadataMap[packageInfo.packageName] = metadata.packageMetadataMap[packageInfo.packageName] =
packageMetadata.copy(backupType = BackupType.FULL) packageMetadata.copy(backupType = BackupType.FULL)
// as we use real crypto, we need a real name for packageInfo
val name = cryptoImpl.getNameForPackage(salt, packageInfo.packageName)
// return streams from plugin and app data // return streams from plugin and app data
val bOutputStream = ByteArrayOutputStream() val bOutputStream = ByteArrayOutputStream()
val bInputStream = ByteArrayInputStream(appData) val bInputStream = ByteArrayInputStream(appData)
coEvery { fullBackupPlugin.getOutputStream(packageInfo) } returns bOutputStream coEvery { backupPlugin.getOutputStream(token, name) } returns bOutputStream
every { inputFactory.getInputStream(fileDescriptor) } returns bInputStream every { inputFactory.getInputStream(fileDescriptor) } returns bInputStream
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.isQuotaUnlimited() } returns false
every { fullBackupPlugin.getQuota() } returns DEFAULT_QUOTA_FULL_BACKUP every { fullBackupPlugin.getQuota() } returns DEFAULT_QUOTA_FULL_BACKUP
@ -311,6 +313,7 @@ internal class CoordinatorIntegrationTest : TransportTest() {
) )
} returns packageMetadata } returns packageMetadata
every { settingsManager.getToken() } returns token every { settingsManager.getToken() } returns token
every { metadataManager.salt } returns salt
coEvery { coEvery {
backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA) backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA)
} returns metadataOutputStream } returns metadataOutputStream
@ -336,7 +339,7 @@ internal class CoordinatorIntegrationTest : TransportTest() {
assertEquals(TRANSPORT_OK, restore.startRestore(token, arrayOf(packageInfo))) assertEquals(TRANSPORT_OK, restore.startRestore(token, arrayOf(packageInfo)))
// finds data for full backup // finds data for full backup
every { crypto.getNameForPackage(metadata.salt, packageInfo.packageName) } returns name every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
coEvery { backupPlugin.hasData(token, name) } returns true coEvery { backupPlugin.hasData(token, name) } returns true
val restoreDescription = restore.nextRestorePackage() ?: fail() val restoreDescription = restore.nextRestorePackage() ?: fail()

View file

@ -58,6 +58,7 @@ internal abstract class TransportTest {
put(packageInfo.packageName, PackageMetadata(backupType = BackupType.KV)) put(packageInfo.packageName, PackageMetadata(backupType = BackupType.KV))
} }
) )
protected val salt = metadata.salt
protected val name = getRandomString(12) protected val name = getRandomString(12)
protected val name2 = getRandomString(23) protected val name2 = getRandomString(23)

View file

@ -220,6 +220,8 @@ internal class BackupCoordinatorTest : BackupTest() {
@Test @Test
fun `clearing KV backup data throws`() = runBlocking { fun `clearing KV backup data throws`() = runBlocking {
every { settingsManager.getToken() } returns token
every { metadataManager.salt } returns salt
coEvery { kv.clearBackupData(packageInfo) } throws IOException() coEvery { kv.clearBackupData(packageInfo) } throws IOException()
assertEquals(TRANSPORT_ERROR, backup.clearBackupData(packageInfo)) assertEquals(TRANSPORT_ERROR, backup.clearBackupData(packageInfo))
@ -227,16 +229,20 @@ internal class BackupCoordinatorTest : BackupTest() {
@Test @Test
fun `clearing full backup data throws`() = runBlocking { fun `clearing full backup data throws`() = runBlocking {
every { settingsManager.getToken() } returns token
every { metadataManager.salt } returns salt
coEvery { kv.clearBackupData(packageInfo) } just Runs coEvery { kv.clearBackupData(packageInfo) } just Runs
coEvery { full.clearBackupData(packageInfo) } throws IOException() coEvery { full.clearBackupData(packageInfo, token, salt) } throws IOException()
assertEquals(TRANSPORT_ERROR, backup.clearBackupData(packageInfo)) assertEquals(TRANSPORT_ERROR, backup.clearBackupData(packageInfo))
} }
@Test @Test
fun `clearing backup data succeeds`() = runBlocking { fun `clearing backup data succeeds`() = runBlocking {
every { settingsManager.getToken() } returns token
every { metadataManager.salt } returns salt
coEvery { kv.clearBackupData(packageInfo) } just Runs coEvery { kv.clearBackupData(packageInfo) } just Runs
coEvery { full.clearBackupData(packageInfo) } just Runs coEvery { full.clearBackupData(packageInfo, token, salt) } just Runs
assertEquals(TRANSPORT_OK, backup.clearBackupData(packageInfo)) assertEquals(TRANSPORT_OK, backup.clearBackupData(packageInfo))
@ -288,7 +294,11 @@ internal class BackupCoordinatorTest : BackupTest() {
@Test @Test
fun `metadata does not get updated when no APK was backed up`() = runBlocking { fun `metadata does not get updated when no APK was backed up`() = runBlocking {
coEvery { full.performFullBackup(packageInfo, fileDescriptor, 0) } returns TRANSPORT_OK every { settingsManager.getToken() } returns token
every { metadataManager.salt } returns salt
coEvery {
full.performFullBackup(packageInfo, fileDescriptor, 0, token, salt)
} returns TRANSPORT_OK
coEvery { apkBackup.backupApkIfNecessary(packageInfo, UNKNOWN_ERROR, any()) } returns null coEvery { apkBackup.backupApkIfNecessary(packageInfo, UNKNOWN_ERROR, any()) } returns null
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, fileDescriptor, 0)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, fileDescriptor, 0))
@ -296,7 +306,11 @@ internal class BackupCoordinatorTest : BackupTest() {
@Test @Test
fun `app exceeding quota gets cancelled and reason written to metadata`() = runBlocking { fun `app exceeding quota gets cancelled and reason written to metadata`() = runBlocking {
coEvery { full.performFullBackup(packageInfo, fileDescriptor, 0) } returns TRANSPORT_OK every { settingsManager.getToken() } returns token
every { metadataManager.salt } returns salt
coEvery {
full.performFullBackup(packageInfo, fileDescriptor, 0, token, salt)
} returns TRANSPORT_OK
expectApkBackupAndMetadataWrite() expectApkBackupAndMetadataWrite()
every { full.getQuota() } returns DEFAULT_QUOTA_FULL_BACKUP every { full.getQuota() } returns DEFAULT_QUOTA_FULL_BACKUP
every { every {
@ -311,7 +325,7 @@ internal class BackupCoordinatorTest : BackupTest() {
BackupType.FULL BackupType.FULL
) )
} just Runs } just Runs
coEvery { full.cancelFullBackup() } just Runs coEvery { full.cancelFullBackup(token, metadata.salt) } just Runs
every { settingsManager.getStorage() } returns storage every { settingsManager.getStorage() } returns storage
every { metadataOutputStream.close() } just Runs every { metadataOutputStream.close() } just Runs
@ -343,7 +357,11 @@ internal class BackupCoordinatorTest : BackupTest() {
@Test @Test
fun `app with no data gets cancelled and reason written to metadata`() = runBlocking { fun `app with no data gets cancelled and reason written to metadata`() = runBlocking {
coEvery { full.performFullBackup(packageInfo, fileDescriptor, 0) } returns TRANSPORT_OK every { settingsManager.getToken() } returns token
every { metadataManager.salt } returns salt
coEvery {
full.performFullBackup(packageInfo, fileDescriptor, 0, token, salt)
} returns TRANSPORT_OK
expectApkBackupAndMetadataWrite() expectApkBackupAndMetadataWrite()
every { full.getQuota() } returns DEFAULT_QUOTA_FULL_BACKUP every { full.getQuota() } returns DEFAULT_QUOTA_FULL_BACKUP
every { full.checkFullBackupSize(0) } returns TRANSPORT_PACKAGE_REJECTED every { full.checkFullBackupSize(0) } returns TRANSPORT_PACKAGE_REJECTED
@ -356,7 +374,7 @@ internal class BackupCoordinatorTest : BackupTest() {
BackupType.FULL BackupType.FULL
) )
} just Runs } just Runs
coEvery { full.cancelFullBackup() } just Runs coEvery { full.cancelFullBackup(token, metadata.salt) } just Runs
every { settingsManager.getStorage() } returns storage every { settingsManager.getStorage() } returns storage
every { metadataOutputStream.close() } just Runs every { metadataOutputStream.close() } just Runs

View file

@ -12,6 +12,6 @@ internal abstract class BackupTest : TransportTest() {
protected val outputStream = mockk<OutputStream>() protected val outputStream = mockk<OutputStream>()
protected val encryptedOutputStream = mockk<OutputStream>() protected val encryptedOutputStream = mockk<OutputStream>()
protected val quota = 42L protected val quota = DEFAULT_QUOTA_FULL_BACKUP
} }

View file

@ -23,7 +23,7 @@ import kotlin.random.Random
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
internal class FullBackupTest : BackupTest() { internal class FullBackupTest : BackupTest() {
private val plugin = mockk<FullBackupPlugin>() private val plugin = mockk<BackupPlugin>()
private val backup = FullBackup(plugin, settingsManager, inputFactory, crypto) private val backup = FullBackup(plugin, settingsManager, inputFactory, crypto)
private val bytes = ByteArray(23).apply { Random.nextBytes(this) } private val bytes = ByteArray(23).apply { Random.nextBytes(this) }
@ -38,9 +38,8 @@ internal class FullBackupTest : BackupTest() {
@Test @Test
fun `checkFullBackupSize exceeds quota`() { fun `checkFullBackupSize exceeds quota`() {
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.isQuotaUnlimited() } returns false
every { plugin.getQuota() } returns quota
assertEquals(TRANSPORT_QUOTA_EXCEEDED, backup.checkFullBackupSize(quota + 1)) assertEquals(TRANSPORT_QUOTA_EXCEEDED, backup.checkFullBackupSize(DEFAULT_QUOTA_FULL_BACKUP + 1))
} }
@Test @Test
@ -63,7 +62,6 @@ internal class FullBackupTest : BackupTest() {
@Test @Test
fun `checkFullBackupSize accepts min data`() { fun `checkFullBackupSize accepts min data`() {
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.isQuotaUnlimited() } returns false
every { plugin.getQuota() } returns quota
assertEquals(TRANSPORT_OK, backup.checkFullBackupSize(1)) assertEquals(TRANSPORT_OK, backup.checkFullBackupSize(1))
} }
@ -71,7 +69,6 @@ internal class FullBackupTest : BackupTest() {
@Test @Test
fun `checkFullBackupSize accepts max data`() { fun `checkFullBackupSize accepts max data`() {
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.isQuotaUnlimited() } returns false
every { plugin.getQuota() } returns quota
assertEquals(TRANSPORT_OK, backup.checkFullBackupSize(quota)) assertEquals(TRANSPORT_OK, backup.checkFullBackupSize(quota))
} }
@ -81,7 +78,7 @@ internal class FullBackupTest : BackupTest() {
every { inputFactory.getInputStream(data) } returns inputStream every { inputFactory.getInputStream(data) } returns inputStream
expectClearState() expectClearState()
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_OK, backup.finishBackup()) assertEquals(TRANSPORT_OK, backup.finishBackup())
assertFalse(backup.hasState()) assertFalse(backup.hasState())
@ -96,7 +93,7 @@ internal class FullBackupTest : BackupTest() {
expectSendData(numBytes) expectSendData(numBytes)
expectClearState() expectClearState()
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_QUOTA_EXCEEDED, backup.sendBackupData(numBytes)) assertEquals(TRANSPORT_QUOTA_EXCEEDED, backup.sendBackupData(numBytes))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
@ -115,7 +112,7 @@ internal class FullBackupTest : BackupTest() {
expectSendData(numBytes2) expectSendData(numBytes2)
expectClearState() expectClearState()
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_OK, backup.sendBackupData(numBytes1)) assertEquals(TRANSPORT_OK, backup.sendBackupData(numBytes1))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
@ -130,12 +127,11 @@ internal class FullBackupTest : BackupTest() {
every { inputFactory.getInputStream(data) } returns inputStream every { inputFactory.getInputStream(data) } returns inputStream
expectInitializeOutputStream() expectInitializeOutputStream()
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.isQuotaUnlimited() } returns false
every { plugin.getQuota() } returns quota
every { crypto.newEncryptingStream(outputStream, ad) } returns encryptedOutputStream every { crypto.newEncryptingStream(outputStream, ad) } returns encryptedOutputStream
every { inputStream.read(any(), any(), bytes.size) } throws IOException() every { inputStream.read(any(), any(), bytes.size) } throws IOException()
expectClearState() expectClearState()
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_ERROR, backup.sendBackupData(bytes.size)) assertEquals(TRANSPORT_ERROR, backup.sendBackupData(bytes.size))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
@ -148,11 +144,11 @@ internal class FullBackupTest : BackupTest() {
every { inputFactory.getInputStream(data) } returns inputStream every { inputFactory.getInputStream(data) } returns inputStream
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.isQuotaUnlimited() } returns false
every { plugin.getQuota() } returns quota every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
coEvery { plugin.getOutputStream(packageInfo) } throws IOException() coEvery { plugin.getOutputStream(token, name) } throws IOException()
expectClearState() expectClearState()
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_ERROR, backup.sendBackupData(bytes.size)) assertEquals(TRANSPORT_ERROR, backup.sendBackupData(bytes.size))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
@ -165,13 +161,13 @@ internal class FullBackupTest : BackupTest() {
every { inputFactory.getInputStream(data) } returns inputStream every { inputFactory.getInputStream(data) } returns inputStream
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.isQuotaUnlimited() } returns false
every { plugin.getQuota() } returns quota every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
coEvery { plugin.getOutputStream(packageInfo) } returns outputStream coEvery { plugin.getOutputStream(token, name) } returns outputStream
every { inputFactory.getInputStream(data) } returns inputStream every { inputFactory.getInputStream(data) } returns inputStream
every { outputStream.write(ByteArray(1) { VERSION }) } throws IOException() every { outputStream.write(ByteArray(1) { VERSION }) } throws IOException()
expectClearState() expectClearState()
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_ERROR, backup.sendBackupData(bytes.size)) assertEquals(TRANSPORT_ERROR, backup.sendBackupData(bytes.size))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
@ -185,13 +181,12 @@ internal class FullBackupTest : BackupTest() {
every { inputFactory.getInputStream(data) } returns inputStream every { inputFactory.getInputStream(data) } returns inputStream
expectInitializeOutputStream() expectInitializeOutputStream()
every { settingsManager.isQuotaUnlimited() } returns false every { settingsManager.isQuotaUnlimited() } returns false
every { plugin.getQuota() } returns quota
every { crypto.newEncryptingStream(outputStream, ad) } returns encryptedOutputStream every { crypto.newEncryptingStream(outputStream, ad) } returns encryptedOutputStream
every { inputStream.read(any(), any(), bytes.size) } returns bytes.size every { inputStream.read(any(), any(), bytes.size) } returns bytes.size
every { encryptedOutputStream.write(any<ByteArray>()) } throws IOException() every { encryptedOutputStream.write(any<ByteArray>()) } throws IOException()
expectClearState() expectClearState()
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_ERROR, backup.sendBackupData(bytes.size)) assertEquals(TRANSPORT_ERROR, backup.sendBackupData(bytes.size))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
@ -210,7 +205,7 @@ internal class FullBackupTest : BackupTest() {
expectSendData(numBytes2) expectSendData(numBytes2)
expectClearState() expectClearState()
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_OK, backup.sendBackupData(numBytes1)) assertEquals(TRANSPORT_OK, backup.sendBackupData(numBytes1))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
@ -222,9 +217,10 @@ internal class FullBackupTest : BackupTest() {
@Test @Test
fun `clearBackupData delegates to plugin`() = runBlocking { fun `clearBackupData delegates to plugin`() = runBlocking {
coEvery { plugin.removeDataOfPackage(packageInfo) } just Runs every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
coEvery { plugin.removeData(token, name) } just Runs
backup.clearBackupData(packageInfo) backup.clearBackupData(packageInfo, token, salt)
} }
@Test @Test
@ -232,11 +228,12 @@ internal class FullBackupTest : BackupTest() {
every { inputFactory.getInputStream(data) } returns inputStream every { inputFactory.getInputStream(data) } returns inputStream
expectInitializeOutputStream() expectInitializeOutputStream()
expectClearState() expectClearState()
coEvery { plugin.removeDataOfPackage(packageInfo) } just Runs every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
coEvery { plugin.removeData(token, name) } just Runs
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
backup.cancelFullBackup() backup.cancelFullBackup(token, salt)
assertFalse(backup.hasState()) assertFalse(backup.hasState())
} }
@ -245,11 +242,12 @@ internal class FullBackupTest : BackupTest() {
every { inputFactory.getInputStream(data) } returns inputStream every { inputFactory.getInputStream(data) } returns inputStream
expectInitializeOutputStream() expectInitializeOutputStream()
expectClearState() expectClearState()
coEvery { plugin.removeDataOfPackage(packageInfo) } throws IOException() every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
coEvery { plugin.removeData(token, name) } throws IOException()
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
backup.cancelFullBackup() backup.cancelFullBackup(token, salt)
assertFalse(backup.hasState()) assertFalse(backup.hasState())
} }
@ -262,7 +260,7 @@ internal class FullBackupTest : BackupTest() {
expectSendData(numBytes) expectSendData(numBytes)
every { encryptedOutputStream.flush() } throws IOException() every { encryptedOutputStream.flush() } throws IOException()
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_OK, backup.sendBackupData(numBytes)) assertEquals(TRANSPORT_OK, backup.sendBackupData(numBytes))
assertEquals(TRANSPORT_ERROR, backup.finishBackup()) assertEquals(TRANSPORT_ERROR, backup.finishBackup())
@ -278,7 +276,7 @@ internal class FullBackupTest : BackupTest() {
every { inputStream.close() } just Runs every { inputStream.close() } just Runs
every { data.close() } just Runs every { data.close() } just Runs
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_OK, backup.finishBackup()) assertEquals(TRANSPORT_OK, backup.finishBackup())
assertFalse(backup.hasState()) assertFalse(backup.hasState())
@ -293,7 +291,7 @@ internal class FullBackupTest : BackupTest() {
every { inputStream.close() } throws IOException() every { inputStream.close() } throws IOException()
every { data.close() } just Runs every { data.close() } just Runs
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_OK, backup.finishBackup()) assertEquals(TRANSPORT_OK, backup.finishBackup())
assertFalse(backup.hasState()) assertFalse(backup.hasState())
@ -308,19 +306,19 @@ internal class FullBackupTest : BackupTest() {
every { inputStream.close() } just Runs every { inputStream.close() } just Runs
every { data.close() } throws IOException() every { data.close() } throws IOException()
assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data)) assertEquals(TRANSPORT_OK, backup.performFullBackup(packageInfo, data, 0, token, salt))
assertTrue(backup.hasState()) assertTrue(backup.hasState())
assertEquals(TRANSPORT_OK, backup.finishBackup()) assertEquals(TRANSPORT_OK, backup.finishBackup())
assertFalse(backup.hasState()) assertFalse(backup.hasState())
} }
private fun expectInitializeOutputStream() { private fun expectInitializeOutputStream() {
coEvery { plugin.getOutputStream(packageInfo) } returns outputStream every { crypto.getNameForPackage(salt, packageInfo.packageName) } returns name
coEvery { plugin.getOutputStream(token, name) } returns outputStream
every { outputStream.write(ByteArray(1) { VERSION }) } just Runs every { outputStream.write(ByteArray(1) { VERSION }) } just Runs
} }
private fun expectSendData(numBytes: Int, readBytes: Int = numBytes) { private fun expectSendData(numBytes: Int, readBytes: Int = numBytes) {
every { plugin.getQuota() } returns quota
every { inputStream.read(any(), any(), numBytes) } returns readBytes every { inputStream.read(any(), any(), numBytes) } returns readBytes
every { crypto.newEncryptingStream(outputStream, ad) } returns encryptedOutputStream every { crypto.newEncryptingStream(outputStream, ad) } returns encryptedOutputStream
every { encryptedOutputStream.write(any<ByteArray>()) } just Runs every { encryptedOutputStream.write(any<ByteArray>()) } just Runs