Skip to content

Commit

Permalink
Merge pull request #670 from grote/309-restore-choose-apps
Browse files Browse the repository at this point in the history
Allow choosing which apps will get restored
  • Loading branch information
grote authored Jun 19, 2024
2 parents fd5089d + c483332 commit 22ca255
Show file tree
Hide file tree
Showing 69 changed files with 2,384 additions and 883 deletions.
17 changes: 4 additions & 13 deletions .github/scripts/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,12 @@
# SPDX-License-Identifier: Apache-2.0
#

adb root
sleep 5
adb remount
echo "Disable auto-restore"
adb shell bmgr autorestore false

echo "Installing Seedvault app..."
adb shell mkdir -p /system/priv-app/Seedvault
adb push app/build/outputs/apk/release/app-release.apk /system/priv-app/Seedvault/Seedvault.apk

echo "Installing Seedvault permissions..."
adb push permissions_com.stevesoltys.seedvault.xml /system/etc/permissions/privapp-permissions-seedvault.xml
adb push allowlist_com.stevesoltys.seedvault.xml /system/etc/sysconfig/allowlist-seedvault.xml

echo "Setting Seedvault transport..."
sleep 10
adb shell bmgr transport com.stevesoltys.seedvault.transport.ConfigurableBackupTransport
./gradlew --stacktrace :app:installDebugAndroidTest
sleep 60

D2D_BACKUP_TEST=$1

Expand Down
1 change: 1 addition & 0 deletions .idea/dictionaries/user.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,15 @@ dependencies {
// anything less than 'implementation' fails tests run with gradlew
testImplementation(aospLibs)
testImplementation("androidx.test.ext:junit:1.1.5")
testImplementation("org.robolectric:robolectric:4.10.3")
testImplementation("org.robolectric:robolectric:4.12.2")
testImplementation("org.hamcrest:hamcrest:2.2")
testImplementation("org.junit.jupiter:junit-jupiter-api:${libs.versions.junit5.get()}")
testImplementation("org.junit.jupiter:junit-jupiter-params:${libs.versions.junit5.get()}")
testImplementation("io.mockk:mockk:${libs.versions.mockk.get()}")
testImplementation(
"org.jetbrains.kotlinx:kotlinx-coroutines-test:${libs.versions.coroutines.get()}"
)
testImplementation("app.cash.turbine:turbine:1.0.0")
testImplementation("org.bitcoinj:bitcoinj-core:0.16.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${libs.versions.junit5.get()}")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:${libs.versions.junit5.get()}")
Expand Down
13 changes: 4 additions & 9 deletions app/development/scripts/install_app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ if [ -z "$ANDROID_HOME" ]; then
fi

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
DEVELOPMENT_DIR=$SCRIPT_DIR/..
ROOT_PROJECT_DIR=$SCRIPT_DIR/../../..

EMULATOR_DEVICE_NAME=$($ANDROID_HOME/platform-tools/adb devices | grep emulator | cut -f1)
EMULATOR_DEVICE_NAME=$("$ANDROID_HOME"/platform-tools/adb devices | grep emulator | cut -f1)

if [ -z "$EMULATOR_DEVICE_NAME" ]; then
echo "Emulator device name not found"
Expand All @@ -29,13 +28,9 @@ $ADB remount # remount /system as writable

echo "Installing Seedvault app..."
$ADB shell mkdir -p /system/priv-app/Seedvault
$ADB push $ROOT_PROJECT_DIR/app/build/outputs/apk/release/app-release.apk /system/priv-app/Seedvault/Seedvault.apk
$ADB push "$ROOT_PROJECT_DIR"/app/build/outputs/apk/release/app-release.apk /system/priv-app/Seedvault/Seedvault.apk

echo "Installing Seedvault permissions..."
$ADB push $ROOT_PROJECT_DIR/permissions_com.stevesoltys.seedvault.xml /system/etc/permissions/privapp-permissions-seedvault.xml
$ADB push $ROOT_PROJECT_DIR/allowlist_com.stevesoltys.seedvault.xml /system/etc/sysconfig/allowlist-seedvault.xml
$ADB shell am force-stop com.stevesoltys.seedvault
$ADB push "$ROOT_PROJECT_DIR"/permissions_com.stevesoltys.seedvault.xml /system/etc/permissions/privapp-permissions-seedvault.xml
$ADB push "$ROOT_PROJECT_DIR"/allowlist_com.stevesoltys.seedvault.xml /system/etc/sysconfig/allowlist-seedvault.xml
$ADB shell am broadcast -a android.intent.action.BOOT_COMPLETED

echo "Setting Seedvault transport..."
$ADB shell bmgr transport com.stevesoltys.seedvault.transport.ConfigurableBackupTransport
22 changes: 9 additions & 13 deletions app/development/scripts/provision_emulator.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,23 @@ EMULATOR_NAME=$1
SYSTEM_IMAGE=$2

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
DEVELOPMENT_DIR=$SCRIPT_DIR/..
ROOT_PROJECT_DIR=$SCRIPT_DIR/../../..

echo "Downloading system image..."
yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install "$SYSTEM_IMAGE"
yes | "$ANDROID_HOME"/cmdline-tools/latest/bin/sdkmanager --install "$SYSTEM_IMAGE"

# create AVD if it doesn't exist
if $ANDROID_HOME/cmdline-tools/latest/bin/avdmanager list avd | grep -q "$EMULATOR_NAME"; then
if "$ANDROID_HOME"/cmdline-tools/latest/bin/avdmanager list avd | grep -q "$EMULATOR_NAME"; then
echo "AVD already exists. Skipping creation."
else
echo "Creating AVD..."
echo 'no' | $ANDROID_HOME/cmdline-tools/latest/bin/avdmanager create avd -n "$EMULATOR_NAME" -k "$SYSTEM_IMAGE"
echo 'no' | "$ANDROID_HOME"/cmdline-tools/latest/bin/avdmanager create avd -n "$EMULATOR_NAME" -k "$SYSTEM_IMAGE"
sleep 1
fi

EMULATOR_DEVICE_NAME=$($ANDROID_HOME/platform-tools/adb devices | grep emulator | cut -f1)
EMULATOR_DEVICE_NAME=$("$ANDROID_HOME"/platform-tools/adb devices | grep emulator | cut -f1)

if [ -z "$EMULATOR_DEVICE_NAME" ]; then
$SCRIPT_DIR/start_emulator.sh "$EMULATOR_NAME"
"$SCRIPT_DIR"/start_emulator.sh "$EMULATOR_NAME"
fi

# wait for emulator device to appear with 180 second timeout
Expand All @@ -47,7 +45,7 @@ echo "Waiting for emulator device..."
for i in {1..180}; do
if [ -z "$EMULATOR_DEVICE_NAME" ]; then
sleep 1
EMULATOR_DEVICE_NAME=$($ANDROID_HOME/platform-tools/adb devices | grep emulator | cut -f1)
EMULATOR_DEVICE_NAME=$("$ANDROID_HOME"/platform-tools/adb devices | grep emulator | cut -f1)
else
break
fi
Expand All @@ -73,16 +71,14 @@ $ADB reboot # need to reboot first time we remount
$ADB wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;'

echo "Provisioning emulator for Seedvault..."
$SCRIPT_DIR/install_app.sh
"$SCRIPT_DIR"/install_app.sh

echo "Rebooting emulator..."
$ADB reboot
$ADB wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;'

echo "Setting backup transport to Seedvault..."
$ADB shell bmgr enable true
sleep 5
$ADB shell bmgr transport com.stevesoltys.seedvault.transport.ConfigurableBackupTransport
echo "Disabling backup..."
$ADB shell bmgr enable false

echo "Downloading and extracting test backup to '/sdcard/seedvault_baseline'..."

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,19 @@ class KoinInstrumentationTestApp : App() {

viewModel {
currentRestoreViewModel =
spyk(RestoreViewModel(context, get(), get(), get(), get(), get(), get(), get()))
spyk(
RestoreViewModel(
app = context,
settingsManager = get(),
keyManager = get(),
backupManager = get(),
restoreCoordinator = get(),
apkRestore = get(),
iconManager = get(),
storageBackup = get(),
pluginManager = get(),
)
)
currentRestoreViewModel!!
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ internal interface LargeRestoreTestBase : LargeTestBase {
backupListItem.clickAndWaitForNewWindow()
waitUntilIdle()

waitForAppSelectionLoaded()
// just tap next in app selection
appsSelectedButton.clickAndWaitForNewWindow()

waitForInstallResult()

if (someAppsNotInstalledText.exists()) {
Expand Down Expand Up @@ -104,13 +108,22 @@ internal interface LargeRestoreTestBase : LargeTestBase {
spyOnKVRestoreData(result)
}

private fun waitForAppSelectionLoaded() = runBlocking {
withContext(Dispatchers.Main) {
withTimeout(RESTORE_TIMEOUT) {
while (spyRestoreViewModel.selectedApps.value?.apps?.isNotEmpty() != true) {
delay(100)
}
}
}
waitUntilIdle()
}

private fun waitForInstallResult() = runBlocking {

withContext(Dispatchers.Main) {
withTimeout(RESTORE_TIMEOUT) {
while (spyRestoreViewModel.installResult.value == null ||
spyRestoreViewModel.nextButtonEnabled.value == false
) {
while (spyRestoreViewModel.installResult.value?.isFinished != true) {
delay(100)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,11 @@ internal interface LargeTestBase : KoinComponent {
}

fun testResultFilename(testName: String): String {
val arguments = InstrumentationRegistry.getArguments()
val d2d = if (arguments.getString("d2d_backup_test") == "true") "d2d" else ""
val simpleDateFormat = SimpleDateFormat("yyyyMMdd_hhmmss")
val timeStamp = simpleDateFormat.format(Calendar.getInstance().time)
return "${timeStamp}_${testName.replace(" ", "_")}"
return "${timeStamp}_${d2d}_${testName.replace(" ", "_")}"
}

@OptIn(DelicateCoroutinesApi::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.junit.rules.TestName
import org.junit.runner.RunWith
import org.koin.core.component.KoinComponent
import java.io.File
import java.lang.Thread.sleep
import java.util.concurrent.atomic.AtomicBoolean

@RunWith(AndroidJUnit4::class)
Expand Down Expand Up @@ -44,6 +45,11 @@ internal abstract class SeedvaultLargeTest :
resetApplicationState()
clearTestBackups()

runCommand("bmgr enable true")
sleep(60_000)
runCommand("bmgr transport com.stevesoltys.seedvault.transport.ConfigurableBackupTransport")
sleep(60_000)

startRecordingTest(keepRecordingScreen, name.methodName)
restoreBaselineBackup()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package com.stevesoltys.seedvault.e2e.impl

import androidx.test.filters.LargeTest
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
import com.stevesoltys.seedvault.e2e.SeedvaultLargeTest
import com.stevesoltys.seedvault.e2e.SeedvaultLargeTestResult
import com.stevesoltys.seedvault.metadata.PackageState
Expand Down Expand Up @@ -127,17 +128,19 @@ internal class BackupRestoreTest : SeedvaultLargeTest() {
) {
// Assert all "key/value" restored data matches the backup data.
restore.kv.forEach { (pkg, kvData) ->
assert(backup.kv.containsKey(pkg)) {
"KV data for $pkg missing from backup."
}

kvData.forEach { (key, value) ->
assert(backup.kv[pkg]!!.containsKey(key)) {
"KV data for $pkg/$key exists in restore but is missing from backup."
if (pkg != MAGIC_PACKAGE_MANAGER) {
assert(backup.kv.containsKey(pkg)) {
"KV data for $pkg missing from backup."
}

assert(value.contentEquals(backup.kv[pkg]!![key]!!)) {
"KV data for $pkg/$key does not match."
kvData.forEach { (key, value) ->
assert(backup.kv[pkg]!!.containsKey(key)) {
"KV data for $pkg/$key exists in restore but is missing from backup."
}

assert(value.contentEquals(backup.kv[pkg]!![key]!!)) {
"KV data for $pkg/$key does not match."
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ object RestoreScreen : UiDeviceScreen<RestoreScreen>() {

val backupListItem = findObject { textContains("Last backup") }

val appsSelectedButton = findObject { text("Restore backup") }

val nextButton = findObject { text("Next") }

val finishButton = findObject { text("Finish") }
Expand Down
15 changes: 14 additions & 1 deletion app/src/main/java/com/stevesoltys/seedvault/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,19 @@ open class App : Application() {
)
}
viewModel { RestoreStorageViewModel(this@App, get(), get(), get(), get()) }
viewModel { RestoreViewModel(this@App, get(), get(), get(), get(), get(), get(), get()) }
viewModel {
RestoreViewModel(
app = this@App,
settingsManager = get(),
keyManager = get(),
backupManager = get(),
restoreCoordinator = get(),
apkRestore = get(),
iconManager = get(),
storageBackup = get(),
pluginManager = get(),
)
}
viewModel { FileSelectionViewModel(this@App, get()) }
}

Expand Down Expand Up @@ -189,6 +201,7 @@ open class App : Application() {

const val MAGIC_PACKAGE_MANAGER: String = PACKAGE_MANAGER_SENTINEL
const val ANCESTRAL_RECORD_KEY = "@ancestral_record@"
const val NO_DATA_END_SENTINEL = "@end@"
const val GLOBAL_METADATA_KEY = "@meta@"
const val ERROR_BACKUP_CANCELLED: Int = BackupManager.ERROR_BACKUP_CANCELLED

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ internal interface Crypto {
internal const val TYPE_METADATA: Byte = 0x00
internal const val TYPE_BACKUP_KV: Byte = 0x01
internal const val TYPE_BACKUP_FULL: Byte = 0x02
internal const val TYPE_ICONS: Byte = 0x03

internal class CryptoImpl(
private val keyManager: KeyManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ data class BackupMetadata(
internal var d2dBackup: Boolean = false,
internal val packageMetadataMap: PackageMetadataMap = PackageMetadataMap(),
) {
val size: Long?
val size: Long
get() = packageMetadataMap.values.sumOf { m ->
(m.size ?: 0L) + (m.splits?.sumOf { it.size ?: 0L } ?: 0L)
}
Expand Down Expand Up @@ -85,13 +85,16 @@ data class PackageMetadata(
internal var state: PackageState = UNKNOWN_ERROR,
internal var backupType: BackupType? = null,
internal var size: Long? = null,
internal var name: CharSequence? = null,
internal val system: Boolean = false,
internal val isLaunchableSystemApp: Boolean = false,
internal val version: Long? = null,
internal val installer: String? = null,
internal val splits: List<ApkSplit>? = null,
internal val sha256: String? = null,
internal val signatures: List<String>? = null,
) {
val isInternalSystem: Boolean = system && !isLaunchableSystemApp
fun hasApk(): Boolean {
return version != null && sha256 != null && signatures != null
}
Expand All @@ -110,7 +113,9 @@ internal const val JSON_PACKAGE_TIME = "time"
internal const val JSON_PACKAGE_BACKUP_TYPE = "backupType"
internal const val JSON_PACKAGE_STATE = "state"
internal const val JSON_PACKAGE_SIZE = "size"
internal const val JSON_PACKAGE_APP_NAME = "name"
internal const val JSON_PACKAGE_SYSTEM = "system"
internal const val JSON_PACKAGE_SYSTEM_LAUNCHER = "systemLauncher"
internal const val JSON_PACKAGE_VERSION = "version"
internal const val JSON_PACKAGE_INSTALLER = "installer"
internal const val JSON_PACKAGE_SPLITS = "splits"
Expand Down
Loading

0 comments on commit 22ca255

Please sign in to comment.