Merge pull request #605 from grote/backup-size
Store and show the size of app backups
This commit is contained in:
commit
bd9ece2b11
15 changed files with 117 additions and 23 deletions
|
@ -74,6 +74,7 @@ data class PackageMetadata(
|
||||||
internal var time: Long = 0L,
|
internal var time: Long = 0L,
|
||||||
internal var state: PackageState = UNKNOWN_ERROR,
|
internal var state: PackageState = UNKNOWN_ERROR,
|
||||||
internal var backupType: BackupType? = null,
|
internal var backupType: BackupType? = null,
|
||||||
|
internal var size: Long? = null,
|
||||||
internal val system: Boolean = false,
|
internal val system: Boolean = false,
|
||||||
internal val version: Long? = null,
|
internal val version: Long? = null,
|
||||||
internal val installer: String? = null,
|
internal val installer: String? = null,
|
||||||
|
@ -97,6 +98,7 @@ enum class BackupType { KV, FULL }
|
||||||
internal const val JSON_PACKAGE_TIME = "time"
|
internal const val JSON_PACKAGE_TIME = "time"
|
||||||
internal const val JSON_PACKAGE_BACKUP_TYPE = "backupType"
|
internal const val JSON_PACKAGE_BACKUP_TYPE = "backupType"
|
||||||
internal const val JSON_PACKAGE_STATE = "state"
|
internal const val JSON_PACKAGE_STATE = "state"
|
||||||
|
internal const val JSON_PACKAGE_SIZE = "size"
|
||||||
internal const val JSON_PACKAGE_SYSTEM = "system"
|
internal const val JSON_PACKAGE_SYSTEM = "system"
|
||||||
internal const val JSON_PACKAGE_VERSION = "version"
|
internal const val JSON_PACKAGE_VERSION = "version"
|
||||||
internal const val JSON_PACKAGE_INSTALLER = "installer"
|
internal const val JSON_PACKAGE_INSTALLER = "installer"
|
||||||
|
|
|
@ -131,6 +131,7 @@ internal class MetadataManager(
|
||||||
fun onPackageBackedUp(
|
fun onPackageBackedUp(
|
||||||
packageInfo: PackageInfo,
|
packageInfo: PackageInfo,
|
||||||
type: BackupType,
|
type: BackupType,
|
||||||
|
size: Long?,
|
||||||
metadataOutputStream: OutputStream,
|
metadataOutputStream: OutputStream,
|
||||||
) {
|
) {
|
||||||
val packageName = packageInfo.packageName
|
val packageName = packageInfo.packageName
|
||||||
|
@ -143,12 +144,15 @@ internal class MetadataManager(
|
||||||
metadata.packageMetadataMap[packageName]!!.time = now
|
metadata.packageMetadataMap[packageName]!!.time = now
|
||||||
metadata.packageMetadataMap[packageName]!!.state = APK_AND_DATA
|
metadata.packageMetadataMap[packageName]!!.state = APK_AND_DATA
|
||||||
metadata.packageMetadataMap[packageName]!!.backupType = type
|
metadata.packageMetadataMap[packageName]!!.backupType = type
|
||||||
|
// don't override a previous K/V size, if there were no K/V changes
|
||||||
|
if (size != null) metadata.packageMetadataMap[packageName]!!.size = size
|
||||||
} else {
|
} else {
|
||||||
metadata.packageMetadataMap[packageName] = PackageMetadata(
|
metadata.packageMetadataMap[packageName] = PackageMetadata(
|
||||||
time = now,
|
time = now,
|
||||||
state = APK_AND_DATA,
|
state = APK_AND_DATA,
|
||||||
backupType = type,
|
backupType = type,
|
||||||
system = packageInfo.isSystemApp()
|
size = size,
|
||||||
|
system = packageInfo.isSystemApp(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,7 @@ internal class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader {
|
||||||
// because when only backing up the APK for example, there's no type
|
// because when only backing up the APK for example, there's no type
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
val pSize = p.optLong(JSON_PACKAGE_SIZE, -1L)
|
||||||
val pSystem = p.optBoolean(JSON_PACKAGE_SYSTEM, false)
|
val pSystem = p.optBoolean(JSON_PACKAGE_SYSTEM, false)
|
||||||
val pVersion = p.optLong(JSON_PACKAGE_VERSION, 0L)
|
val pVersion = p.optLong(JSON_PACKAGE_VERSION, 0L)
|
||||||
val pInstaller = p.optString(JSON_PACKAGE_INSTALLER)
|
val pInstaller = p.optString(JSON_PACKAGE_INSTALLER)
|
||||||
|
@ -136,6 +137,7 @@ internal class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader {
|
||||||
time = p.getLong(JSON_PACKAGE_TIME),
|
time = p.getLong(JSON_PACKAGE_TIME),
|
||||||
state = pState,
|
state = pState,
|
||||||
backupType = pBackupType,
|
backupType = pBackupType,
|
||||||
|
size = if (pSize < 0L) null else pSize,
|
||||||
system = pSystem,
|
system = pSystem,
|
||||||
version = if (pVersion == 0L) null else pVersion,
|
version = if (pVersion == 0L) null else pVersion,
|
||||||
installer = if (pInstaller == "") null else pInstaller,
|
installer = if (pInstaller == "") null else pInstaller,
|
||||||
|
|
|
@ -49,6 +49,9 @@ internal class MetadataWriterImpl(private val crypto: Crypto) : MetadataWriter {
|
||||||
if (packageMetadata.backupType != null) {
|
if (packageMetadata.backupType != null) {
|
||||||
put(JSON_PACKAGE_BACKUP_TYPE, packageMetadata.backupType!!.name)
|
put(JSON_PACKAGE_BACKUP_TYPE, packageMetadata.backupType!!.name)
|
||||||
}
|
}
|
||||||
|
if (packageMetadata.size != null) {
|
||||||
|
put(JSON_PACKAGE_SIZE, packageMetadata.size)
|
||||||
|
}
|
||||||
if (packageMetadata.system) {
|
if (packageMetadata.system) {
|
||||||
put(JSON_PACKAGE_SYSTEM, packageMetadata.system)
|
put(JSON_PACKAGE_SYSTEM, packageMetadata.system)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ data class AppStatus(
|
||||||
val icon: Drawable,
|
val icon: Drawable,
|
||||||
val name: String,
|
val name: String,
|
||||||
val time: Long,
|
val time: Long,
|
||||||
|
val size: Long?,
|
||||||
val status: AppBackupState,
|
val status: AppBackupState,
|
||||||
val isSpecial: Boolean = false,
|
val isSpecial: Boolean = false,
|
||||||
) : AppListItem()
|
) : AppListItem()
|
||||||
|
@ -87,6 +88,7 @@ internal class AppListRetriever(
|
||||||
icon = getIcon(packageName),
|
icon = getIcon(packageName),
|
||||||
name = context.getString(stringId),
|
name = context.getString(stringId),
|
||||||
time = metadata?.time ?: 0,
|
time = metadata?.time ?: 0,
|
||||||
|
size = metadata?.size,
|
||||||
status = status,
|
status = status,
|
||||||
isSpecial = true
|
isSpecial = true
|
||||||
)
|
)
|
||||||
|
@ -111,6 +113,7 @@ internal class AppListRetriever(
|
||||||
icon = getIcon(it.packageName),
|
icon = getIcon(it.packageName),
|
||||||
name = getAppName(context, it.packageName).toString(),
|
name = getAppName(context, it.packageName).toString(),
|
||||||
time = time,
|
time = time,
|
||||||
|
size = metadata?.size,
|
||||||
status = status
|
status = status
|
||||||
)
|
)
|
||||||
}.sortedBy { it.name.lowercase(locale) }
|
}.sortedBy { it.name.lowercase(locale) }
|
||||||
|
@ -125,6 +128,7 @@ internal class AppListRetriever(
|
||||||
icon = getIcon(it.packageName),
|
icon = getIcon(it.packageName),
|
||||||
name = getAppName(context, it.packageName).toString(),
|
name = getAppName(context, it.packageName).toString(),
|
||||||
time = 0,
|
time = 0,
|
||||||
|
size = null,
|
||||||
status = FAILED_NOT_ALLOWED
|
status = FAILED_NOT_ALLOWED
|
||||||
)
|
)
|
||||||
}.sortedBy { it.name.lowercase(locale) }
|
}.sortedBy { it.name.lowercase(locale) }
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.stevesoltys.seedvault.settings
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||||
|
import android.text.format.Formatter.formatShortFileSize
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.GONE
|
import android.view.View.GONE
|
||||||
|
@ -116,7 +117,12 @@ internal class AppStatusAdapter(private val toggleListener: AppStatusToggleListe
|
||||||
setState(item.status, false)
|
setState(item.status, false)
|
||||||
}
|
}
|
||||||
if (item.status == SUCCEEDED) {
|
if (item.status == SUCCEEDED) {
|
||||||
appInfo.text = item.time.toRelativeTime(context)
|
appInfo.text = if (item.size == null) {
|
||||||
|
item.time.toRelativeTime(context)
|
||||||
|
} else {
|
||||||
|
item.time.toRelativeTime(context).toString() +
|
||||||
|
" (${formatShortFileSize(v.context, item.size)})"
|
||||||
|
}
|
||||||
appInfo.visibility = VISIBLE
|
appInfo.visibility = VISIBLE
|
||||||
}
|
}
|
||||||
switchView.visibility = INVISIBLE
|
switchView.visibility = INVISIBLE
|
||||||
|
|
|
@ -363,6 +363,7 @@ internal class BackupCoordinator(
|
||||||
// getCurrentPackage() not-null because we have state, call before finishing
|
// getCurrentPackage() not-null because we have state, call before finishing
|
||||||
val packageInfo = kv.getCurrentPackage()!!
|
val packageInfo = kv.getCurrentPackage()!!
|
||||||
val packageName = packageInfo.packageName
|
val packageName = packageInfo.packageName
|
||||||
|
val size = kv.getCurrentSize()
|
||||||
// tell K/V backup to finish
|
// tell K/V backup to finish
|
||||||
var result = kv.finishBackup()
|
var result = kv.finishBackup()
|
||||||
if (result == TRANSPORT_OK) {
|
if (result == TRANSPORT_OK) {
|
||||||
|
@ -370,7 +371,7 @@ internal class BackupCoordinator(
|
||||||
// call onPackageBackedUp for @pm@ only if we can do backups right now
|
// call onPackageBackedUp for @pm@ only if we can do backups right now
|
||||||
if (!isPmBackup || settingsManager.canDoBackupNow()) {
|
if (!isPmBackup || settingsManager.canDoBackupNow()) {
|
||||||
try {
|
try {
|
||||||
onPackageBackedUp(packageInfo, BackupType.KV)
|
onPackageBackedUp(packageInfo, BackupType.KV, size)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Error calling onPackageBackedUp for $packageName", e)
|
Log.e(TAG, "Error calling onPackageBackedUp for $packageName", e)
|
||||||
result = TRANSPORT_PACKAGE_REJECTED
|
result = TRANSPORT_PACKAGE_REJECTED
|
||||||
|
@ -396,10 +397,11 @@ internal class BackupCoordinator(
|
||||||
// getCurrentPackage() not-null because we have state
|
// getCurrentPackage() not-null because we have state
|
||||||
val packageInfo = full.getCurrentPackage()!!
|
val packageInfo = full.getCurrentPackage()!!
|
||||||
val packageName = packageInfo.packageName
|
val packageName = packageInfo.packageName
|
||||||
|
val size = full.getCurrentSize()
|
||||||
// tell full backup to finish
|
// tell full backup to finish
|
||||||
var result = full.finishBackup()
|
var result = full.finishBackup()
|
||||||
try {
|
try {
|
||||||
onPackageBackedUp(packageInfo, BackupType.FULL)
|
onPackageBackedUp(packageInfo, BackupType.FULL, size)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Error calling onPackageBackedUp for $packageName", e)
|
Log.e(TAG, "Error calling onPackageBackedUp for $packageName", e)
|
||||||
result = TRANSPORT_PACKAGE_REJECTED
|
result = TRANSPORT_PACKAGE_REJECTED
|
||||||
|
@ -470,9 +472,9 @@ internal class BackupCoordinator(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun onPackageBackedUp(packageInfo: PackageInfo, type: BackupType) {
|
private suspend fun onPackageBackedUp(packageInfo: PackageInfo, type: BackupType, size: Long?) {
|
||||||
plugin.getMetadataOutputStream().use {
|
plugin.getMetadataOutputStream().use {
|
||||||
metadataManager.onPackageBackedUp(packageInfo, type, it)
|
metadataManager.onPackageBackedUp(packageInfo, type, size, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,8 @@ internal class FullBackup(
|
||||||
|
|
||||||
fun getCurrentPackage() = state?.packageInfo
|
fun getCurrentPackage() = state?.packageInfo
|
||||||
|
|
||||||
|
fun getCurrentSize() = state?.size
|
||||||
|
|
||||||
fun getQuota(): Long {
|
fun getQuota(): Long {
|
||||||
return if (settingsManager.isQuotaUnlimited()) Long.MAX_VALUE else DEFAULT_QUOTA_FULL_BACKUP
|
return if (settingsManager.isQuotaUnlimited()) Long.MAX_VALUE else DEFAULT_QUOTA_FULL_BACKUP
|
||||||
}
|
}
|
||||||
|
@ -190,7 +192,7 @@ internal class FullBackup(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun finishBackup(): Int {
|
fun finishBackup(): Int {
|
||||||
Log.i(TAG, "Finish full backup of ${state!!.packageName}.")
|
Log.i(TAG, "Finish full backup of ${state!!.packageName}. Wrote ${state!!.size} bytes")
|
||||||
return clearState()
|
return clearState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,10 @@ internal class KVBackup(
|
||||||
|
|
||||||
fun getCurrentPackage() = state?.packageInfo
|
fun getCurrentPackage() = state?.packageInfo
|
||||||
|
|
||||||
|
fun getCurrentSize() = getCurrentPackage()?.let {
|
||||||
|
dbManager.getDbSize(it.packageName)
|
||||||
|
}
|
||||||
|
|
||||||
fun getQuota(): Long = if (settingsManager.isQuotaUnlimited()) {
|
fun getQuota(): Long = if (settingsManager.isQuotaUnlimited()) {
|
||||||
Long.MAX_VALUE
|
Long.MAX_VALUE
|
||||||
} else {
|
} else {
|
||||||
|
@ -252,7 +256,7 @@ internal class KVBackup(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Uploaded db file for $packageName")
|
Log.d(TAG, "Uploaded db file for $packageName.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private class KVOperation(
|
private class KVOperation(
|
||||||
|
|
|
@ -29,6 +29,11 @@ interface KvDbManager {
|
||||||
* Use only for backup.
|
* Use only for backup.
|
||||||
*/
|
*/
|
||||||
fun existsDb(packageName: String): Boolean
|
fun existsDb(packageName: String): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current size of the DB in bytes or null, if no DB exists.
|
||||||
|
*/
|
||||||
|
fun getDbSize(packageName: String): Long?
|
||||||
fun deleteDb(packageName: String, isRestore: Boolean = false): Boolean
|
fun deleteDb(packageName: String, isRestore: Boolean = false): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +64,11 @@ class KvDbManagerImpl(private val context: Context) : KvDbManager {
|
||||||
return getDbFile(packageName).isFile
|
return getDbFile(packageName).isFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getDbSize(packageName: String): Long? {
|
||||||
|
val file = getDbFile(packageName)
|
||||||
|
return if (file.isFile) file.length() else null
|
||||||
|
}
|
||||||
|
|
||||||
override fun deleteDb(packageName: String, isRestore: Boolean): Boolean {
|
override fun deleteDb(packageName: String, isRestore: Boolean): Boolean {
|
||||||
return getDbFile(packageName, isRestore).delete()
|
return getDbFile(packageName, isRestore).delete()
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,6 +249,7 @@ class MetadataManagerTest {
|
||||||
time = time,
|
time = time,
|
||||||
packageMetadataMap = PackageMetadataMap() // otherwise this isn't copied, but referenced
|
packageMetadataMap = PackageMetadataMap() // otherwise this isn't copied, but referenced
|
||||||
)
|
)
|
||||||
|
val size = Random.nextLong()
|
||||||
val packageMetadata = PackageMetadata(time)
|
val packageMetadata = PackageMetadata(time)
|
||||||
updatedMetadata.packageMetadataMap[packageName] = packageMetadata
|
updatedMetadata.packageMetadataMap[packageName] = packageMetadata
|
||||||
|
|
||||||
|
@ -256,10 +257,15 @@ class MetadataManagerTest {
|
||||||
every { clock.time() } returns time
|
every { clock.time() } returns time
|
||||||
expectModifyMetadata(initialMetadata)
|
expectModifyMetadata(initialMetadata)
|
||||||
|
|
||||||
manager.onPackageBackedUp(packageInfo, BackupType.FULL, storageOutputStream)
|
manager.onPackageBackedUp(packageInfo, BackupType.FULL, size, storageOutputStream)
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
packageMetadata.copy(state = APK_AND_DATA, backupType = BackupType.FULL, system = true),
|
packageMetadata.copy(
|
||||||
|
state = APK_AND_DATA,
|
||||||
|
backupType = BackupType.FULL,
|
||||||
|
size = size,
|
||||||
|
system = true,
|
||||||
|
),
|
||||||
manager.getPackageMetadata(packageName)
|
manager.getPackageMetadata(packageName)
|
||||||
)
|
)
|
||||||
assertEquals(time, manager.getLastBackupTime())
|
assertEquals(time, manager.getLastBackupTime())
|
||||||
|
@ -270,6 +276,7 @@ class MetadataManagerTest {
|
||||||
cacheOutputStream.close()
|
cacheOutputStream.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test onPackageBackedUp() with D2D enabled`() {
|
fun `test onPackageBackedUp() with D2D enabled`() {
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
|
@ -278,7 +285,7 @@ class MetadataManagerTest {
|
||||||
|
|
||||||
every { settingsManager.d2dBackupsEnabled() } returns true
|
every { settingsManager.d2dBackupsEnabled() } returns true
|
||||||
|
|
||||||
manager.onPackageBackedUp(packageInfo, BackupType.FULL, storageOutputStream)
|
manager.onPackageBackedUp(packageInfo, BackupType.FULL, 0L, storageOutputStream)
|
||||||
assertTrue(initialMetadata.d2dBackup)
|
assertTrue(initialMetadata.d2dBackup)
|
||||||
|
|
||||||
verify {
|
verify {
|
||||||
|
@ -290,19 +297,20 @@ class MetadataManagerTest {
|
||||||
@Test
|
@Test
|
||||||
fun `test onPackageBackedUp() fails to write to storage`() {
|
fun `test onPackageBackedUp() fails to write to storage`() {
|
||||||
val updateTime = time + 1
|
val updateTime = time + 1
|
||||||
|
val size = Random.nextLong()
|
||||||
val updatedMetadata = initialMetadata.copy(
|
val updatedMetadata = initialMetadata.copy(
|
||||||
time = updateTime,
|
time = updateTime,
|
||||||
packageMetadataMap = PackageMetadataMap() // otherwise this isn't copied, but referenced
|
packageMetadataMap = PackageMetadataMap() // otherwise this isn't copied, but referenced
|
||||||
)
|
)
|
||||||
updatedMetadata.packageMetadataMap[packageName] =
|
updatedMetadata.packageMetadataMap[packageName] =
|
||||||
PackageMetadata(updateTime, APK_AND_DATA, BackupType.KV)
|
PackageMetadata(updateTime, APK_AND_DATA, BackupType.KV, size)
|
||||||
|
|
||||||
expectReadFromCache()
|
expectReadFromCache()
|
||||||
every { clock.time() } returns updateTime
|
every { clock.time() } returns updateTime
|
||||||
every { metadataWriter.write(updatedMetadata, storageOutputStream) } throws IOException()
|
every { metadataWriter.write(updatedMetadata, storageOutputStream) } throws IOException()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
manager.onPackageBackedUp(packageInfo, BackupType.KV, storageOutputStream)
|
manager.onPackageBackedUp(packageInfo, BackupType.KV, size, storageOutputStream)
|
||||||
fail()
|
fail()
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
// expected
|
// expected
|
||||||
|
@ -335,7 +343,7 @@ class MetadataManagerTest {
|
||||||
every { clock.time() } returns time
|
every { clock.time() } returns time
|
||||||
expectModifyMetadata(updatedMetadata)
|
expectModifyMetadata(updatedMetadata)
|
||||||
|
|
||||||
manager.onPackageBackedUp(packageInfo, BackupType.FULL, storageOutputStream)
|
manager.onPackageBackedUp(packageInfo, BackupType.FULL, 0L, storageOutputStream)
|
||||||
|
|
||||||
assertEquals(time, manager.getLastBackupTime())
|
assertEquals(time, manager.getLastBackupTime())
|
||||||
assertEquals(PackageMetadata(time), manager.getPackageMetadata(cachedPackageName))
|
assertEquals(PackageMetadata(time), manager.getPackageMetadata(cachedPackageName))
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
|
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
import kotlin.random.nextLong
|
||||||
|
|
||||||
@TestInstance(PER_CLASS)
|
@TestInstance(PER_CLASS)
|
||||||
internal class MetadataWriterDecoderTest {
|
internal class MetadataWriterDecoderTest {
|
||||||
|
@ -81,11 +82,12 @@ internal class MetadataWriterDecoderTest {
|
||||||
time = Random.nextLong(),
|
time = Random.nextLong(),
|
||||||
state = QUOTA_EXCEEDED,
|
state = QUOTA_EXCEEDED,
|
||||||
backupType = BackupType.FULL,
|
backupType = BackupType.FULL,
|
||||||
|
size = Random.nextLong(0..Long.MAX_VALUE),
|
||||||
system = Random.nextBoolean(),
|
system = Random.nextBoolean(),
|
||||||
version = Random.nextLong(),
|
version = Random.nextLong(),
|
||||||
installer = getRandomString(),
|
installer = getRandomString(),
|
||||||
sha256 = getRandomString(),
|
sha256 = getRandomString(),
|
||||||
signatures = listOf(getRandomString())
|
signatures = listOf(getRandomString()),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
put(
|
put(
|
||||||
|
@ -93,22 +95,24 @@ internal class MetadataWriterDecoderTest {
|
||||||
time = Random.nextLong(),
|
time = Random.nextLong(),
|
||||||
state = NO_DATA,
|
state = NO_DATA,
|
||||||
backupType = BackupType.KV,
|
backupType = BackupType.KV,
|
||||||
|
size = null,
|
||||||
system = Random.nextBoolean(),
|
system = Random.nextBoolean(),
|
||||||
version = Random.nextLong(),
|
version = Random.nextLong(),
|
||||||
installer = getRandomString(),
|
installer = getRandomString(),
|
||||||
sha256 = getRandomString(),
|
sha256 = getRandomString(),
|
||||||
signatures = listOf(getRandomString(), getRandomString())
|
signatures = listOf(getRandomString(), getRandomString()),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
put(
|
put(
|
||||||
getRandomString(), PackageMetadata(
|
getRandomString(), PackageMetadata(
|
||||||
time = 0L,
|
time = 0L,
|
||||||
state = NOT_ALLOWED,
|
state = NOT_ALLOWED,
|
||||||
|
size = 0,
|
||||||
system = Random.nextBoolean(),
|
system = Random.nextBoolean(),
|
||||||
version = Random.nextLong(),
|
version = Random.nextLong(),
|
||||||
installer = getRandomString(),
|
installer = getRandomString(),
|
||||||
sha256 = getRandomString(),
|
sha256 = getRandomString(),
|
||||||
signatures = listOf(getRandomString(), getRandomString())
|
signatures = listOf(getRandomString(), getRandomString()),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,7 +147,12 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
||||||
metadataManager.onApkBackedUp(packageInfo, packageMetadata, metadataOutputStream)
|
metadataManager.onApkBackedUp(packageInfo, packageMetadata, metadataOutputStream)
|
||||||
} just Runs
|
} just Runs
|
||||||
every {
|
every {
|
||||||
metadataManager.onPackageBackedUp(packageInfo, BackupType.KV, metadataOutputStream)
|
metadataManager.onPackageBackedUp(
|
||||||
|
packageInfo = packageInfo,
|
||||||
|
type = BackupType.KV,
|
||||||
|
size = more((appData.size + appData2.size).toLong()), // more because DB overhead
|
||||||
|
metadataOutputStream = metadataOutputStream,
|
||||||
|
)
|
||||||
} just Runs
|
} just Runs
|
||||||
|
|
||||||
// start K/V backup
|
// start K/V backup
|
||||||
|
@ -216,7 +221,12 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
||||||
backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA)
|
backupPlugin.getOutputStream(token, FILE_BACKUP_METADATA)
|
||||||
} returns metadataOutputStream
|
} returns metadataOutputStream
|
||||||
every {
|
every {
|
||||||
metadataManager.onPackageBackedUp(packageInfo, BackupType.KV, metadataOutputStream)
|
metadataManager.onPackageBackedUp(
|
||||||
|
packageInfo = packageInfo,
|
||||||
|
type = BackupType.KV,
|
||||||
|
size = more(size.toLong()), // more than $size, because DB overhead
|
||||||
|
metadataOutputStream = metadataOutputStream,
|
||||||
|
)
|
||||||
} just Runs
|
} just Runs
|
||||||
|
|
||||||
// start K/V backup
|
// start K/V backup
|
||||||
|
@ -289,7 +299,12 @@ internal class CoordinatorIntegrationTest : TransportTest() {
|
||||||
)
|
)
|
||||||
} just Runs
|
} just Runs
|
||||||
every {
|
every {
|
||||||
metadataManager.onPackageBackedUp(packageInfo, BackupType.FULL, metadataOutputStream)
|
metadataManager.onPackageBackedUp(
|
||||||
|
packageInfo = packageInfo,
|
||||||
|
type = BackupType.FULL,
|
||||||
|
size = appData.size.toLong(),
|
||||||
|
metadataOutputStream = metadataOutputStream,
|
||||||
|
)
|
||||||
} just Runs
|
} just Runs
|
||||||
|
|
||||||
// perform backup to output stream
|
// perform backup to output stream
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.junit.jupiter.api.Test
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
import kotlin.random.nextLong
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
internal class BackupCoordinatorTest : BackupTest() {
|
internal class BackupCoordinatorTest : BackupTest() {
|
||||||
|
@ -204,14 +205,22 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `finish backup delegates to KV plugin if it has state`() = runBlocking {
|
fun `finish backup delegates to KV plugin if it has state`() = runBlocking {
|
||||||
|
val size = 0L
|
||||||
|
|
||||||
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 { kv.finishBackup() } returns TRANSPORT_OK
|
coEvery { kv.finishBackup() } returns TRANSPORT_OK
|
||||||
every { settingsManager.getToken() } returns token
|
every { settingsManager.getToken() } returns token
|
||||||
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||||
|
every { kv.getCurrentSize() } returns size
|
||||||
every {
|
every {
|
||||||
metadataManager.onPackageBackedUp(packageInfo, BackupType.KV, metadataOutputStream)
|
metadataManager.onPackageBackedUp(
|
||||||
|
packageInfo = packageInfo,
|
||||||
|
type = BackupType.KV,
|
||||||
|
size = size,
|
||||||
|
metadataOutputStream = metadataOutputStream,
|
||||||
|
)
|
||||||
} just Runs
|
} just Runs
|
||||||
every { metadataOutputStream.close() } just Runs
|
every { metadataOutputStream.close() } just Runs
|
||||||
|
|
||||||
|
@ -225,6 +234,7 @@ 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 pmPackageInfo
|
every { kv.getCurrentPackage() } returns pmPackageInfo
|
||||||
|
every { kv.getCurrentSize() } returns 42L
|
||||||
|
|
||||||
coEvery { kv.finishBackup() } returns TRANSPORT_OK
|
coEvery { kv.finishBackup() } returns TRANSPORT_OK
|
||||||
every { settingsManager.canDoBackupNow() } returns false
|
every { settingsManager.canDoBackupNow() } returns false
|
||||||
|
@ -235,6 +245,7 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
@Test
|
@Test
|
||||||
fun `finish backup delegates to full plugin if it has state`() = runBlocking {
|
fun `finish backup delegates to full plugin if it has state`() = runBlocking {
|
||||||
val result = Random.nextInt()
|
val result = Random.nextInt()
|
||||||
|
val size: Long? = null
|
||||||
|
|
||||||
every { kv.hasState() } returns false
|
every { kv.hasState() } returns false
|
||||||
every { full.hasState() } returns true
|
every { full.hasState() } returns true
|
||||||
|
@ -242,8 +253,14 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
every { full.finishBackup() } returns result
|
every { full.finishBackup() } returns result
|
||||||
every { settingsManager.getToken() } returns token
|
every { settingsManager.getToken() } returns token
|
||||||
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
coEvery { plugin.getOutputStream(token, FILE_BACKUP_METADATA) } returns metadataOutputStream
|
||||||
|
every { full.getCurrentSize() } returns size
|
||||||
every {
|
every {
|
||||||
metadataManager.onPackageBackedUp(packageInfo, BackupType.FULL, metadataOutputStream)
|
metadataManager.onPackageBackedUp(
|
||||||
|
packageInfo = packageInfo,
|
||||||
|
type = BackupType.FULL,
|
||||||
|
size = size,
|
||||||
|
metadataOutputStream = metadataOutputStream,
|
||||||
|
)
|
||||||
} just Runs
|
} just Runs
|
||||||
every { metadataOutputStream.close() } just Runs
|
every { metadataOutputStream.close() } just Runs
|
||||||
|
|
||||||
|
@ -375,6 +392,7 @@ internal class BackupCoordinatorTest : BackupTest() {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
val packageMetadata: PackageMetadata = mockk()
|
val packageMetadata: PackageMetadata = mockk()
|
||||||
|
val size = Random.nextLong(1L..Long.MAX_VALUE)
|
||||||
|
|
||||||
every { settingsManager.canDoBackupNow() } returns true
|
every { settingsManager.canDoBackupNow() } returns true
|
||||||
every { metadataManager.requiresInit } returns false
|
every { metadataManager.requiresInit } returns false
|
||||||
|
@ -394,8 +412,14 @@ 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 pmPackageInfo
|
every { kv.getCurrentPackage() } returns pmPackageInfo
|
||||||
|
every { kv.getCurrentSize() } returns size
|
||||||
every {
|
every {
|
||||||
metadataManager.onPackageBackedUp(pmPackageInfo, BackupType.KV, metadataOutputStream)
|
metadataManager.onPackageBackedUp(
|
||||||
|
pmPackageInfo,
|
||||||
|
BackupType.KV,
|
||||||
|
size,
|
||||||
|
metadataOutputStream,
|
||||||
|
)
|
||||||
} just Runs
|
} just Runs
|
||||||
coEvery { kv.finishBackup() } returns TRANSPORT_OK
|
coEvery { kv.finishBackup() } returns TRANSPORT_OK
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,10 @@ class TestKvDbManager : KvDbManager {
|
||||||
return db != null
|
return db != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getDbSize(packageName: String): Long? {
|
||||||
|
return db?.serialize()?.toByteArray()?.size?.toLong()
|
||||||
|
}
|
||||||
|
|
||||||
override fun deleteDb(packageName: String, isRestore: Boolean): Boolean {
|
override fun deleteDb(packageName: String, isRestore: Boolean): Boolean {
|
||||||
clearDb()
|
clearDb()
|
||||||
return true
|
return true
|
||||||
|
|
Loading…
Reference in a new issue