Define new and simpler BackupPlugin API
leaving the old one in place still
This commit is contained in:
parent
2932af463c
commit
5d1e3debd1
8 changed files with 206 additions and 73 deletions
|
@ -3,12 +3,19 @@ package com.stevesoltys.seedvault.plugins.saf
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import com.stevesoltys.seedvault.transport.backup.BackupPlugin
|
import com.stevesoltys.seedvault.transport.backup.BackupPlugin
|
||||||
|
import com.stevesoltys.seedvault.transport.backup.EncryptedMetadata
|
||||||
import com.stevesoltys.seedvault.transport.backup.FullBackupPlugin
|
import com.stevesoltys.seedvault.transport.backup.FullBackupPlugin
|
||||||
import com.stevesoltys.seedvault.transport.backup.KVBackupPlugin
|
import com.stevesoltys.seedvault.transport.backup.KVBackupPlugin
|
||||||
|
import java.io.FileNotFoundException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
private val TAG = DocumentsProviderBackupPlugin::class.java.simpleName
|
||||||
private const val MIME_TYPE_APK = "application/vnd.android.package-archive"
|
private const val MIME_TYPE_APK = "application/vnd.android.package-archive"
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
|
@ -45,12 +52,60 @@ internal class DocumentsProviderBackupPlugin(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override suspend fun getMetadataOutputStream(): OutputStream {
|
override suspend fun hasData(token: Long, name: String): Boolean {
|
||||||
val setDir = storage.getSetDir() ?: throw IOException()
|
val setDir = storage.getSetDir(token) ?: return false
|
||||||
|
return setDir.findFileBlocking(context, name) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override suspend fun getOutputStream(token: Long, name: String): OutputStream {
|
||||||
|
val setDir = storage.getSetDir(token) ?: throw IOException()
|
||||||
|
val file = setDir.createOrGetFile(context, name)
|
||||||
|
return storage.getOutputStream(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override suspend fun getInputStream(token: Long, name: String): InputStream {
|
||||||
|
val setDir = storage.getSetDir(token) ?: throw IOException()
|
||||||
|
val file = setDir.findFileBlocking(context, name) ?: throw FileNotFoundException()
|
||||||
|
return storage.getInputStream(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override suspend fun removeData(token: Long, name: String) {
|
||||||
|
val setDir = storage.getSetDir(token) ?: throw IOException()
|
||||||
|
val file = setDir.findFileBlocking(context, name) ?: return
|
||||||
|
if (!file.delete()) throw IOException("Failed to delete $name")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override suspend fun getMetadataOutputStream(token: Long): OutputStream {
|
||||||
|
val setDir = storage.getSetDir(token) ?: throw IOException()
|
||||||
val metadataFile = setDir.createOrGetFile(context, FILE_BACKUP_METADATA)
|
val metadataFile = setDir.createOrGetFile(context, FILE_BACKUP_METADATA)
|
||||||
return storage.getOutputStream(metadataFile)
|
return storage.getOutputStream(metadataFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override suspend fun hasBackup(uri: Uri): Boolean {
|
||||||
|
val parent = DocumentFile.fromTreeUri(context, uri) ?: throw AssertionError()
|
||||||
|
val rootDir = parent.findFileBlocking(context, DIRECTORY_ROOT) ?: return false
|
||||||
|
val backupSets = getBackups(context, rootDir)
|
||||||
|
return backupSets.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getAvailableBackups(): Sequence<EncryptedMetadata>? {
|
||||||
|
val rootDir = storage.rootBackupDir ?: return null
|
||||||
|
val backupSets = getBackups(context, rootDir)
|
||||||
|
val iterator = backupSets.iterator()
|
||||||
|
return generateSequence {
|
||||||
|
if (!iterator.hasNext()) return@generateSequence null // end sequence
|
||||||
|
val backupSet = iterator.next()
|
||||||
|
EncryptedMetadata(backupSet.token) {
|
||||||
|
storage.getInputStream(backupSet.metadataFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override suspend fun getApkOutputStream(
|
override suspend fun getApkOutputStream(
|
||||||
packageInfo: PackageInfo,
|
packageInfo: PackageInfo,
|
||||||
|
@ -69,3 +124,64 @@ internal class DocumentsProviderBackupPlugin(
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BackupSet(val token: Long, val metadataFile: DocumentFile)
|
||||||
|
|
||||||
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
|
internal suspend fun getBackups(context: Context, rootDir: DocumentFile): List<BackupSet> {
|
||||||
|
val backupSets = ArrayList<BackupSet>()
|
||||||
|
val files = try {
|
||||||
|
// block until the DocumentsProvider has results
|
||||||
|
rootDir.listFilesBlocking(context)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(TAG, "Error loading backups from storage", e)
|
||||||
|
return backupSets
|
||||||
|
}
|
||||||
|
for (set in files) {
|
||||||
|
// retrieve name only once as this causes a DB query
|
||||||
|
val name = set.name
|
||||||
|
|
||||||
|
// get current token from set or continue to next file/set
|
||||||
|
val token = set.getTokenOrNull(name) ?: continue
|
||||||
|
|
||||||
|
// block until children of set are available
|
||||||
|
val metadata = try {
|
||||||
|
set.findFileBlocking(context, FILE_BACKUP_METADATA)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(TAG, "Error reading metadata file in backup set folder: $name", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
if (metadata == null) {
|
||||||
|
Log.w(TAG, "Missing metadata file in backup set folder: $name")
|
||||||
|
} else {
|
||||||
|
backupSets.add(BackupSet(token, metadata))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return backupSets
|
||||||
|
}
|
||||||
|
|
||||||
|
private val tokenRegex = Regex("([0-9]{13})") // good until the year 2286
|
||||||
|
private val chunkFolderRegex = Regex("[a-f0-9]{2}")
|
||||||
|
|
||||||
|
private fun DocumentFile.getTokenOrNull(name: String?): Long? {
|
||||||
|
val looksLikeToken = name != null && tokenRegex.matches(name)
|
||||||
|
// check for isDirectory only if we already have a valid token (causes DB query)
|
||||||
|
if (!looksLikeToken || !isDirectory) {
|
||||||
|
// only log unexpected output
|
||||||
|
if (name != null && isUnexpectedFile(name)) {
|
||||||
|
Log.w(TAG, "Found invalid backup set folder: $name")
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
name?.toLong()
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
throw AssertionError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isUnexpectedFile(name: String): Boolean {
|
||||||
|
return name != FILE_NO_MEDIA &&
|
||||||
|
!chunkFolderRegex.matches(name) &&
|
||||||
|
!name.endsWith(".SeedSnap")
|
||||||
|
}
|
||||||
|
|
|
@ -24,9 +24,6 @@ internal class DocumentsProviderRestorePlugin(
|
||||||
override val fullRestorePlugin: FullRestorePlugin
|
override val fullRestorePlugin: FullRestorePlugin
|
||||||
) : RestorePlugin {
|
) : RestorePlugin {
|
||||||
|
|
||||||
private val tokenRegex = Regex("([0-9]{13})") // good until the year 2286
|
|
||||||
private val chunkFolderRegex = Regex("[a-f0-9]{2}")
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override suspend fun hasBackup(uri: Uri): Boolean {
|
override suspend fun hasBackup(uri: Uri): Boolean {
|
||||||
val parent = DocumentFile.fromTreeUri(context, uri) ?: throw AssertionError()
|
val parent = DocumentFile.fromTreeUri(context, uri) ?: throw AssertionError()
|
||||||
|
@ -52,61 +49,6 @@ internal class DocumentsProviderRestorePlugin(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getBackups(context: Context, rootDir: DocumentFile): List<BackupSet> {
|
|
||||||
val backupSets = ArrayList<BackupSet>()
|
|
||||||
val files = try {
|
|
||||||
// block until the DocumentsProvider has results
|
|
||||||
rootDir.listFilesBlocking(context)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.e(TAG, "Error loading backups from storage", e)
|
|
||||||
return backupSets
|
|
||||||
}
|
|
||||||
for (set in files) {
|
|
||||||
// retrieve name only once as this causes a DB query
|
|
||||||
val name = set.name
|
|
||||||
|
|
||||||
// get current token from set or continue to next file/set
|
|
||||||
val token = set.getTokenOrNull(name) ?: continue
|
|
||||||
|
|
||||||
// block until children of set are available
|
|
||||||
val metadata = try {
|
|
||||||
set.findFileBlocking(context, FILE_BACKUP_METADATA)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.e(TAG, "Error reading metadata file in backup set folder: $name", e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
if (metadata == null) {
|
|
||||||
Log.w(TAG, "Missing metadata file in backup set folder: $name")
|
|
||||||
} else {
|
|
||||||
backupSets.add(BackupSet(token, metadata))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return backupSets
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun DocumentFile.getTokenOrNull(name: String?): Long? {
|
|
||||||
val looksLikeToken = name != null && tokenRegex.matches(name)
|
|
||||||
// check for isDirectory only if we already have a valid token (causes DB query)
|
|
||||||
if (!looksLikeToken || !isDirectory) {
|
|
||||||
// only log unexpected output
|
|
||||||
if (name != null && isUnexpectedFile(name)) {
|
|
||||||
Log.w(TAG, "Found invalid backup set folder: $name")
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return try {
|
|
||||||
name?.toLong()
|
|
||||||
} catch (e: NumberFormatException) {
|
|
||||||
throw AssertionError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isUnexpectedFile(name: String): Boolean {
|
|
||||||
return name != FILE_NO_MEDIA &&
|
|
||||||
!chunkFolderRegex.matches(name) &&
|
|
||||||
!name.endsWith(".SeedSnap")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override suspend fun getApkInputStream(
|
override suspend fun getApkInputStream(
|
||||||
token: Long,
|
token: Long,
|
||||||
|
@ -120,5 +62,3 @@ internal class DocumentsProviderRestorePlugin(
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BackupSet(val token: Long, val metadataFile: DocumentFile)
|
|
||||||
|
|
|
@ -28,9 +28,11 @@ import com.stevesoltys.seedvault.metadata.PackageState.NO_DATA
|
||||||
import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED
|
import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED
|
||||||
import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
|
import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
|
||||||
import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
|
import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
|
||||||
|
import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
|
||||||
import com.stevesoltys.seedvault.settings.SettingsManager
|
import com.stevesoltys.seedvault.settings.SettingsManager
|
||||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.io.OutputStream
|
||||||
import java.util.concurrent.TimeUnit.DAYS
|
import java.util.concurrent.TimeUnit.DAYS
|
||||||
import java.util.concurrent.TimeUnit.HOURS
|
import java.util.concurrent.TimeUnit.HOURS
|
||||||
|
|
||||||
|
@ -492,4 +494,9 @@ internal class BackupCoordinator(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun BackupPlugin.getMetadataOutputStream(): OutputStream {
|
||||||
|
val token = settingsManager.getToken() ?: throw IOException("no current token")
|
||||||
|
return getOutputStream(token, FILE_BACKUP_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,18 @@ package com.stevesoltys.seedvault.transport.backup
|
||||||
|
|
||||||
import android.app.backup.RestoreSet
|
import android.app.backup.RestoreSet
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.annotation.WorkerThread
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
interface BackupPlugin {
|
interface BackupPlugin {
|
||||||
|
|
||||||
|
@Deprecated("Use methods in this interface instead")
|
||||||
val kvBackupPlugin: KVBackupPlugin
|
val kvBackupPlugin: KVBackupPlugin
|
||||||
|
|
||||||
|
@Deprecated("Use methods in this interface instead")
|
||||||
val fullBackupPlugin: FullBackupPlugin
|
val fullBackupPlugin: FullBackupPlugin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,16 +30,60 @@ interface BackupPlugin {
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
suspend fun initializeDevice()
|
suspend fun initializeDevice()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if there is data stored for the given name.
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
suspend fun hasData(token: Long, name: String): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a raw byte stream for writing data for the given name.
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
suspend fun getOutputStream(token: Long, name: String): OutputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a raw byte stream with data for the given name.
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
suspend fun getInputStream(token: Long, name: String): InputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all data associated with the given name.
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
suspend fun removeData(token: Long, name: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an [OutputStream] for writing backup metadata.
|
* Returns an [OutputStream] for writing backup metadata.
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
suspend fun getMetadataOutputStream(): OutputStream
|
@Deprecated("use getOutputStream() instead")
|
||||||
|
suspend fun getMetadataOutputStream(token: Long): OutputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches if there's really a backup available in the given location.
|
||||||
|
* Returns true if at least one was found and false otherwise.
|
||||||
|
*
|
||||||
|
* FIXME: Passing a Uri is too plugin-specific and should be handled differently
|
||||||
|
*/
|
||||||
|
@WorkerThread
|
||||||
|
@Throws(IOException::class)
|
||||||
|
suspend fun hasBackup(uri: Uri): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the set of all backups currently available for restore.
|
||||||
|
*
|
||||||
|
* @return metadata for the set of restore images available,
|
||||||
|
* or null if an error occurred (the attempt should be rescheduled).
|
||||||
|
**/
|
||||||
|
suspend fun getAvailableBackups(): Sequence<EncryptedMetadata>?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an [OutputStream] for writing an APK to be backed up.
|
* Returns an [OutputStream] for writing an APK to be backed up.
|
||||||
*/
|
*/
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
|
@Deprecated("Use getOutputStream() instead")
|
||||||
suspend fun getApkOutputStream(packageInfo: PackageInfo, suffix: String): OutputStream
|
suspend fun getApkOutputStream(packageInfo: PackageInfo, suffix: String): OutputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,3 +97,5 @@ interface BackupPlugin {
|
||||||
val providerPackageName: String?
|
val providerPackageName: String?
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EncryptedMetadata(val token: Long, val inputStreamRetriever: () -> InputStream)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.content.pm.PackageInfo
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
@Deprecated("Use BackupPlugin instead")
|
||||||
interface FullBackupPlugin {
|
interface FullBackupPlugin {
|
||||||
|
|
||||||
fun getQuota(): Long
|
fun getQuota(): Long
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.content.pm.PackageInfo
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
@Deprecated("Use BackupPlugin instead")
|
||||||
interface KVBackupPlugin {
|
interface KVBackupPlugin {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,6 +16,7 @@ import com.stevesoltys.seedvault.header.MAX_SEGMENT_CLEARTEXT_LENGTH
|
||||||
import com.stevesoltys.seedvault.metadata.MetadataReaderImpl
|
import com.stevesoltys.seedvault.metadata.MetadataReaderImpl
|
||||||
import com.stevesoltys.seedvault.metadata.PackageMetadata
|
import com.stevesoltys.seedvault.metadata.PackageMetadata
|
||||||
import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
|
import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
|
||||||
|
import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
|
||||||
import com.stevesoltys.seedvault.transport.backup.ApkBackup
|
import com.stevesoltys.seedvault.transport.backup.ApkBackup
|
||||||
import com.stevesoltys.seedvault.transport.backup.BackupCoordinator
|
import com.stevesoltys.seedvault.transport.backup.BackupCoordinator
|
||||||
import com.stevesoltys.seedvault.transport.backup.BackupPlugin
|
import com.stevesoltys.seedvault.transport.backup.BackupPlugin
|
||||||
|
@ -166,7 +167,10 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
||||||
any()
|
any()
|
||||||
)
|
)
|
||||||
} returns packageMetadata
|
} returns packageMetadata
|
||||||
coEvery { backupPlugin.getMetadataOutputStream() } returns metadataOutputStream
|
every { settingsManager.getToken() } returns token
|
||||||
|
coEvery {
|
||||||
|
backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA)
|
||||||
|
} returns metadataOutputStream
|
||||||
every {
|
every {
|
||||||
metadataManager.onApkBackedUp(
|
metadataManager.onApkBackedUp(
|
||||||
packageInfo,
|
packageInfo,
|
||||||
|
@ -244,7 +248,10 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
||||||
} returns bOutputStream
|
} returns bOutputStream
|
||||||
every { kvBackupPlugin.packageFinished(packageInfo) } just Runs
|
every { kvBackupPlugin.packageFinished(packageInfo) } just Runs
|
||||||
coEvery { apkBackup.backupApkIfNecessary(packageInfo, UNKNOWN_ERROR, any()) } returns null
|
coEvery { apkBackup.backupApkIfNecessary(packageInfo, UNKNOWN_ERROR, any()) } returns null
|
||||||
coEvery { backupPlugin.getMetadataOutputStream() } returns metadataOutputStream
|
every { settingsManager.getToken() } returns token
|
||||||
|
coEvery {
|
||||||
|
backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA)
|
||||||
|
} returns metadataOutputStream
|
||||||
every { metadataManager.onPackageBackedUp(packageInfo, metadataOutputStream) } just Runs
|
every { metadataManager.onPackageBackedUp(packageInfo, metadataOutputStream) } just Runs
|
||||||
|
|
||||||
// start and finish K/V backup
|
// start and finish K/V backup
|
||||||
|
@ -296,7 +303,10 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
||||||
any()
|
any()
|
||||||
)
|
)
|
||||||
} returns packageMetadata
|
} returns packageMetadata
|
||||||
coEvery { backupPlugin.getMetadataOutputStream() } returns metadataOutputStream
|
every { settingsManager.getToken() } returns token
|
||||||
|
coEvery {
|
||||||
|
backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA)
|
||||||
|
} returns metadataOutputStream
|
||||||
every {
|
every {
|
||||||
metadataManager.onApkBackedUp(
|
metadataManager.onApkBackedUp(
|
||||||
packageInfo,
|
packageInfo,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.stevesoltys.seedvault.metadata.PackageState.NO_DATA
|
||||||
import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED
|
import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED
|
||||||
import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
|
import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
|
||||||
import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
|
import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
|
||||||
|
import com.stevesoltys.seedvault.plugins.saf.FILE_BACKUP_METADATA
|
||||||
import com.stevesoltys.seedvault.settings.Storage
|
import com.stevesoltys.seedvault.settings.Storage
|
||||||
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
|
||||||
import io.mockk.Runs
|
import io.mockk.Runs
|
||||||
|
@ -85,7 +86,7 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
fun `device initialization succeeds and delegates to plugin`() = runBlocking {
|
fun `device initialization succeeds and delegates to plugin`() = runBlocking {
|
||||||
every { settingsManager.getToken() } returns token
|
every { settingsManager.getToken() } returns token
|
||||||
coEvery { plugin.initializeDevice() } just Runs
|
coEvery { plugin.initializeDevice() } just Runs
|
||||||
coEvery { plugin.getMetadataOutputStream() } returns metadataOutputStream
|
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||||
every { metadataManager.onDeviceInitialization(token, metadataOutputStream) } just Runs
|
every { metadataManager.onDeviceInitialization(token, metadataOutputStream) } just Runs
|
||||||
every { kv.hasState() } returns false
|
every { kv.hasState() } returns false
|
||||||
every { full.hasState() } returns false
|
every { full.hasState() } returns false
|
||||||
|
@ -251,7 +252,8 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
every { kv.hasState() } returns true
|
every { kv.hasState() } returns true
|
||||||
every { full.hasState() } returns false
|
every { full.hasState() } returns false
|
||||||
every { kv.getCurrentPackage() } returns packageInfo
|
every { kv.getCurrentPackage() } returns packageInfo
|
||||||
coEvery { plugin.getMetadataOutputStream() } returns metadataOutputStream
|
every { settingsManager.getToken() } returns token
|
||||||
|
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||||
every { metadataManager.onPackageBackedUp(packageInfo, metadataOutputStream) } just Runs
|
every { metadataManager.onPackageBackedUp(packageInfo, metadataOutputStream) } just Runs
|
||||||
every { kv.finishBackup() } returns result
|
every { kv.finishBackup() } returns result
|
||||||
every { metadataOutputStream.close() } just Runs
|
every { metadataOutputStream.close() } just Runs
|
||||||
|
@ -268,7 +270,8 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
every { kv.hasState() } returns false
|
every { kv.hasState() } returns false
|
||||||
every { full.hasState() } returns true
|
every { full.hasState() } returns true
|
||||||
every { full.getCurrentPackage() } returns packageInfo
|
every { full.getCurrentPackage() } returns packageInfo
|
||||||
coEvery { plugin.getMetadataOutputStream() } returns metadataOutputStream
|
every { settingsManager.getToken() } returns token
|
||||||
|
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||||
every { metadataManager.onPackageBackedUp(packageInfo, metadataOutputStream) } just Runs
|
every { metadataManager.onPackageBackedUp(packageInfo, metadataOutputStream) } just Runs
|
||||||
every { full.finishBackup() } returns result
|
every { full.finishBackup() } returns result
|
||||||
every { metadataOutputStream.close() } just Runs
|
every { metadataOutputStream.close() } just Runs
|
||||||
|
@ -412,7 +415,8 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
coEvery {
|
coEvery {
|
||||||
apkBackup.backupApkIfNecessary(notAllowedPackages[1], WAS_STOPPED, any())
|
apkBackup.backupApkIfNecessary(notAllowedPackages[1], WAS_STOPPED, any())
|
||||||
} returns packageMetadata
|
} returns packageMetadata
|
||||||
coEvery { plugin.getMetadataOutputStream() } returns metadataOutputStream
|
every { settingsManager.getToken() } returns token
|
||||||
|
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||||
every {
|
every {
|
||||||
metadataManager.onApkBackedUp(
|
metadataManager.onApkBackedUp(
|
||||||
notAllowedPackages[1],
|
notAllowedPackages[1],
|
||||||
|
@ -445,7 +449,8 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
} returns oldPackageMetadata
|
} returns oldPackageMetadata
|
||||||
// state differs now, was stopped before
|
// state differs now, was stopped before
|
||||||
every { oldPackageMetadata.state } returns WAS_STOPPED
|
every { oldPackageMetadata.state } returns WAS_STOPPED
|
||||||
coEvery { plugin.getMetadataOutputStream() } returns metadataOutputStream
|
every { settingsManager.getToken() } returns token
|
||||||
|
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||||
every {
|
every {
|
||||||
metadataManager.onPackageBackupError(
|
metadataManager.onPackageBackupError(
|
||||||
packageInfo,
|
packageInfo,
|
||||||
|
@ -473,7 +478,8 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
every {
|
every {
|
||||||
metadataManager.getPackageMetadata(packageInfo.packageName)
|
metadataManager.getPackageMetadata(packageInfo.packageName)
|
||||||
} returns null
|
} returns null
|
||||||
coEvery { plugin.getMetadataOutputStream() } returns metadataOutputStream
|
every { settingsManager.getToken() } returns token
|
||||||
|
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||||
every {
|
every {
|
||||||
metadataManager.onPackageBackupError(
|
metadataManager.onPackageBackupError(
|
||||||
packageInfo,
|
packageInfo,
|
||||||
|
@ -499,7 +505,8 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
any()
|
any()
|
||||||
)
|
)
|
||||||
} returns packageMetadata
|
} returns packageMetadata
|
||||||
coEvery { plugin.getMetadataOutputStream() } returns metadataOutputStream
|
every { settingsManager.getToken() } returns token
|
||||||
|
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||||
every {
|
every {
|
||||||
metadataManager.onApkBackedUp(
|
metadataManager.onApkBackedUp(
|
||||||
any(),
|
any(),
|
||||||
|
|
Loading…
Reference in a new issue