Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .cirrus.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
container:
image: ghcr.io/cirruslabs/android-sdk:34
image: ghcr.io/cirruslabs/android-sdk:35
kvm: true
cpu: 8
memory: 16G

instrumentation_tests_task:
name: "Cirrus CI Instrumentation Tests"
start_avd_background_script:
sdkmanager --install "system-images;android-34;default;x86_64" "emulator";
echo no | avdmanager create avd -n seedvault -k "system-images;android-34;default;x86_64";
sdkmanager --install "system-images;android-35;google_apis;x86_64" "emulator";
echo no | avdmanager create avd -n seedvault -k "system-images;android-35;google_apis;x86_64";
$ANDROID_HOME/emulator/emulator
-avd seedvault
-no-audio
-no-boot-anim
-gpu swiftshader_indirect
-gpu swangle_indirect
-no-snapshot
-no-window
-writable-system;
Expand All @@ -36,6 +36,8 @@ instrumentation_tests_task:
timeout 180s bash -c 'while [[ -z $(adb shell mount | grep "/system " | grep "(rw,") ]]; do sleep 1; done;';
adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;';

for pkg in com.google.android.webview com.google.android.apps.photos com.google.android.youtube com.google.android.apps.youtube.music com.android.musicfx com.google.android.soundpicker com.android.soundpicker com.google.android.gm com.google.android.dialer com.google.android.apps.messaging com.android.mms.service com.google.android.cellbroadcastreceiver com.google.android.cellbroadcastservice com.android.cellbroadcastreceiver com.google.android.apps.docs com.google.android.calendar com.google.android.contacts com.google.android.deskclock com.android.providers.calendar com.android.providers.contacts com.google.android.apps.maps com.google.android.projection.gearhead com.android.chrome com.google.android.googlequicksearchbox com.google.android.as com.google.android.as.oss com.google.android.apps.customization.pixel com.android.wallpapercropper com.android.wallpaper.livepicker com.google.android.accessibility.switchaccess com.google.android.apps.accessibility.voiceaccess com.google.android.marvin.talkback com.android.systemui.accessibility.accessibilitymenu com.google.android.apps.wellbeing com.google.android.healthconnect.controller com.android.camera2 com.android.cameraextensions com.android.bips com.android.printspooler com.google.android.printservice.recommendation com.android.egg com.android.theme.font.notoserifsource com.google.android.federatedcompute com.google.android.ondevicepersonalization.services com.google.android.odad com.google.android.onetimeinitializer com.google.android.telephony.satellite com.google.android.apps.safetyhub com.android.DeviceAsWebcam com.android.dreams.basic com.google.android.tag; do adb shell pm disable-user --user 0 "$pkg" || echo "$pkg not found, skipping..."; done;

adb shell mkdir -p /sdcard/seedvault_baseline;
adb push backup.tar.gz /sdcard/seedvault_baseline/backup.tar.gz;
adb shell tar xzf /sdcard/seedvault_baseline/backup.tar.gz --directory=/sdcard/seedvault_baseline;
Expand All @@ -55,6 +57,7 @@ instrumentation_tests_task:
adb shell bmgr transport com.stevesoltys.seedvault.transport.ConfigurableBackupTransport;
adb reboot;
adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;';
for folder in Audiobooks Documents Downloads DCIM Movies Music Pictures Podcasts Ringtones Notifications Alarms Recordings; do adb shell rm -Rf "/sdcard/$folder" || echo "/sdcard/$folder cleanup failed, skipping..."; done;
run_large_tests_script: ./gradlew -Pandroid.testInstrumentationRunnerArguments.size=large :app:connectedAndroidTest
run_other_tests_script: ./gradlew -Pandroid.testInstrumentationRunnerArguments.notAnnotation=androidx.test.filters.LargeTest connectedAndroidTest
always:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ jobs:
strategy:
fail-fast: false
matrix:
android_target: [ 34 ]
emulator_type: [ aosp_atd ]
android_target: [ 35 ]
emulator_type: [ google_apis ]
steps:
- name: Checkout Code
uses: actions/checkout@v3
Expand All @@ -46,7 +46,7 @@ jobs:
target: ${{ matrix.emulator_type }}
arch: x86_64
force-avd-creation: true
emulator-options: -cores 2 -writable-system -no-snapshot-load -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
emulator-options: -cores 2 -writable-system -no-snapshot-load -no-snapshot-save -no-window -gpu swangle_indirect -noaudio -no-boot-anim -camera-back none
disk-size: '14G'
sdcard-path-or-size: '4096M'
disable-animations: true
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ storage/build/
contactsbackup/build/
/lib/
.idea/*
!.idea/runConfigurations*
!.idea/runConfigurations/*
.idea/runConfigurations.xml
!.idea/inspectionProfiles*
!.idea/codeStyles*
!.idea/copyright*
Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ tasks.register<Exec>("provisionEmulator") {
commandLine(
"${project.projectDir}/development/scripts/provision_emulator.sh",
"seedvault",
"system-images;android-34;default;x86_64"
"system-images;android-35;google_apis;x86_64"
)

environment("ANDROID_HOME", android.sdkDirectory.absolutePath)
Expand Down
74 changes: 71 additions & 3 deletions app/development/scripts/provision_emulator.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,69 @@ fi
ADB="$ANDROID_HOME/platform-tools/adb -s $EMULATOR_DEVICE_NAME"

echo "Waiting for emulator to boot..."
$ADB wait-for-device shell "while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;"
$ADB wait-for-device shell "while [[ -z \$(getprop sys.boot_completed) ]]; do sleep 1; done;"

# List of packages to disable
PACKAGES_TO_DISABLE=(
"com.google.android.webview"
"com.google.android.apps.photos"
"com.google.android.youtube"
"com.google.android.apps.youtube.music"
"com.android.musicfx"
"com.google.android.soundpicker"
"com.android.soundpicker"
"com.google.android.gm"
"com.google.android.dialer"
"com.google.android.apps.messaging"
"com.android.mms.service"
"com.google.android.cellbroadcastreceiver"
"com.google.android.cellbroadcastservice"
"com.android.cellbroadcastreceiver"
"com.google.android.apps.docs"
"com.google.android.calendar"
"com.google.android.contacts"
"com.google.android.deskclock"
"com.android.providers.calendar"
"com.android.providers.contacts"
"com.google.android.apps.maps"
"com.google.android.projection.gearhead"
"com.android.chrome"
"com.google.android.webview"
"com.google.android.googlequicksearchbox"
"com.google.android.as"
"com.google.android.as.oss"
"com.google.android.apps.customization.pixel"
"com.android.wallpapercropper"
"com.android.wallpaper.livepicker"
"com.google.android.accessibility.switchaccess"
"com.google.android.apps.accessibility.voiceaccess"
"com.google.android.marvin.talkback"
"com.android.systemui.accessibility.accessibilitymenu"
"com.google.android.apps.wellbeing"
"com.google.android.healthconnect.controller"
"com.android.camera2"
"com.android.cameraextensions"
"com.android.bips"
"com.android.printspooler"
"com.google.android.printservice.recommendation"
"com.android.egg"
"com.android.theme.font.notoserifsource"
"com.google.android.federatedcompute"
"com.google.android.ondevicepersonalization.services"
"com.google.android.odad"
"com.google.android.onetimeinitializer"
"com.google.android.telephony.satellite"
"com.google.android.apps.safetyhub"
"com.android.DeviceAsWebcam"
"com.android.dreams.basic"
"com.google.android.tag"
)

echo "Disabling unwanted apps..."
for PACKAGE in "${PACKAGES_TO_DISABLE[@]}"; do
echo "Disabling $PACKAGE"
$ADB shell pm disable-user --user 0 "$PACKAGE" || echo "$PACKAGE not found, skipping..."
done

echo "Provisioning emulator for write access to '/system'..."
$ADB root
Expand All @@ -68,14 +130,14 @@ $ADB remount # remount /system as writable

echo "Rebooting emulator..."
$ADB reboot # need to reboot first time we remount
$ADB wait-for-device shell "while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;"
$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

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

echo "Disabling backup..."
$ADB shell bmgr enable false
Expand All @@ -97,7 +159,13 @@ $ADB shell mkdir -p /sdcard/seedvault_baseline
$ADB shell tar xzf /sdcard/backup.tar.gz --directory=/sdcard/seedvault_baseline
$ADB shell rm /sdcard/backup.tar.gz

$ADB shell rm -Rf /sdcard/Audiobooks /sdcard/Documents /sdcard/Downloads \
/sdcard/DCIM /sdcard/Movies /sdcard/Music /sdcard/Pictures \
/sdcard/Podcasts /sdcard/Ringtones /sdcard/Notifications /sdcard/Alarms \
/sdcard/Recordings

# sometimes a system dialog (e.g. launcher stopped) is showing and taking focus
$ADB shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS

echo "Emulator '$EMULATOR_NAME' has been provisioned with Seedvault!"

2 changes: 1 addition & 1 deletion app/development/scripts/start_emulator.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@
EMULATOR_NAME=$1

echo "Starting emulator..."
nohup $ANDROID_HOME/emulator/emulator -avd "$EMULATOR_NAME" -gpu swiftshader_indirect -writable-system >/dev/null 2>&1 &
nohup $ANDROID_HOME/emulator/emulator -avd "$EMULATOR_NAME" -gpu swangle_indirect -writable-system >/dev/null 2>&1 &

Check notice

Code scanning / Shellcheck (reported by Codacy)

Double quote to prevent globbing and word splitting. Note

Double quote to prevent globbing and word splitting.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ internal interface LargeRestoreTestBase : LargeTestBase {
}
}

fun performRestore(): SeedvaultLargeTestResult {
fun performRestore(
backupName: String
): SeedvaultLargeTestResult {
val result = SeedvaultLargeTestResult(
full = mutableMapOf(),
kv = mutableMapOf(),
Expand All @@ -71,7 +73,7 @@ internal interface LargeRestoreTestBase : LargeTestBase {
spyOnRestoreData(result)

RestoreScreen {
backupListItem.clickAndWaitForNewWindow()
backupListItem(backupName).clickAndWaitForNewWindow()
waitUntilIdle()

waitForAppSelectionLoaded()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ internal abstract class SeedvaultLargeTest :
launchRestoreActivity()
chooseStorageLocation(folderName = BASELINE_BACKUP_FOLDER, exists = true)
typeInRestoreCode(baselineBackupRecoveryCode())
performRestore()
performRestore("Android SDK")

resetApplicationState()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ import org.junit.Test
@LargeTest
internal class BackupRestoreTest : SeedvaultLargeTest() {

companion object {
private val IGNORED_PACKAGES = setOf(
"com.google.android.webview",
"com.android.chrome"
)
}

@Test
fun `backup and restore applications`() {
launchBackupActivity()
Expand All @@ -41,27 +48,31 @@ internal class BackupRestoreTest : SeedvaultLargeTest() {
uninstallPackages(backupResult.allUserApps())

launchRestoreActivity()
val restoreResult = performRestore()
val restoreResult = performRestore("Google")

assertValidResults(backupResult, restoreResult)
}

private fun assertValidBackupMetadata(backup: SeedvaultLargeTestResult) {
// Assert all user apps have metadata.
backup.allUserApps().forEach { app ->
assert(backup.backupResults.containsKey(app.packageName)) {
"Metadata for $app missing from backup."
backup.allUserApps()
.filter { it.packageName !in IGNORED_PACKAGES }
.forEach { app ->
assert(backup.backupResults.containsKey(app.packageName)) {
"Metadata for $app missing from backup."
}
}
}

// Assert all metadata has a valid state.
backup.backupResults.forEach { (pkg, metadata) ->
assert(metadata != null) { "Metadata for $pkg is null." }
backup.backupResults
.filterKeys { it !in IGNORED_PACKAGES }
.forEach { (pkg, metadata) ->
assert(metadata != null) { "Metadata for $pkg is null." }

assert(metadata!!.state != PackageState.UNKNOWN_ERROR) {
"Metadata for $pkg has an unknown state."
assert(metadata!!.state != PackageState.UNKNOWN_ERROR) {
"Metadata for $pkg has an unknown state."
}
}
}
}

private fun launchStoppedApps() {
Expand Down Expand Up @@ -100,9 +111,11 @@ internal class BackupRestoreTest : SeedvaultLargeTest() {
restore: SeedvaultLargeTestResult,
) {
val backupUserApps = backup.allUserApps()
.filter { it.packageName !in IGNORED_PACKAGES }
.map { it.packageName }.toSet()

val restoreUserApps = restore.allUserApps()
.filter { it.packageName !in IGNORED_PACKAGES }
.map { it.packageName }.toSet()

// Assert we re-installed all user apps.
Expand All @@ -117,6 +130,7 @@ internal class BackupRestoreTest : SeedvaultLargeTest() {
// Assert we restored data for all user apps that had successful backups.
// This is expected to succeed because we are uninstalling the apps before restoring.
val missingFromRestore = backup.userApps
.filter { it.packageName !in IGNORED_PACKAGES }
.map { it.packageName }
.filter { backup.backupResults[it]?.state == PackageState.APK_AND_DATA }
.filter { !restore.kv.containsKey(it) && !restore.full.containsKey(it) }
Expand All @@ -133,15 +147,17 @@ internal class BackupRestoreTest : SeedvaultLargeTest() {
restore: SeedvaultLargeTestResult,
) {
// Assert all "full" restored data matches the backup data.
val allUserPkgs = backup.allUserApps().map { it.packageName }
val allUserPkgs = backup.allUserApps()
.filter { it.packageName !in IGNORED_PACKAGES }
.map { it.packageName }

restore.full.forEach { (pkg, fullData) ->
if (allUserPkgs.contains(pkg)) {
assert(backup.full.containsKey(pkg)) {
"Full data for $pkg missing from restore."
}

if (backup.backupResults[pkg]!!.state == PackageState.APK_AND_DATA) {
assert(backup.full.containsKey(pkg)) {
"Full data for $pkg missing from restore."
}

assert(fullData == backup.full[pkg]!!) {
"Full data for $pkg does not match."
}
Expand All @@ -155,22 +171,24 @@ internal class BackupRestoreTest : SeedvaultLargeTest() {
restore: SeedvaultLargeTestResult,
) {
// Assert all "key/value" restored data matches the backup data.
restore.kv.forEach { (pkg, kvData) ->
if (pkg != MAGIC_PACKAGE_MANAGER) {
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."
restore.kv
.filter { it.key !in IGNORED_PACKAGES }
.forEach { (pkg, kvData) ->
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."
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import com.stevesoltys.seedvault.e2e.screen.UiDeviceScreen

object RestoreScreen : UiDeviceScreen<RestoreScreen>() {

val backupListItem = findObject {
textContains("Android SDK") // device name of test backups
fun backupListItem(name: String) = findObject {
textContains(name)
}

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