diff options
| author | Lena <lena@omega> | 2026-07-01 00:00:00 +0000 |
|---|---|---|
| committer | Lena <lena@omega> | 2026-07-01 00:00:00 +0000 |
| commit | f6668454daaa0b5b559723b3bb685c244456247b (patch) | |
| tree | 174280a1542fa4d1f5c9427b00f853458ca32022 /test-rig/screenshots.sh | |
| parent | 30ee8be82e44d1a8250d1f6e27e71f7d5f24aec5 (diff) | |
| download | scrcpy-android-f6668454daaa0b5b559723b3bb685c244456247b.tar.gz | |
test: add a screenshot capture tier
The store screenshots were made by hand, so nothing kept them in step
with the UI. Add ./test screenshots, which boots a pixel_6 AVD on the
same image the e2e tier uses and overwrites the tracked PNGs.
The AVD is its own so a capture run cannot disturb the device state e2e
asserts against, and the panel comes from the device profile: forcing
geometry with wm size on a booted software-GPU emulator ANRs SystemUI
and the ANR dialog lands in the capture. SystemUI demo mode does the
same, so the status bar is left live. Each shot is rejected unless the
app owns the focused window and the PNG has the expected geometry.
Diffstat (limited to 'test-rig/screenshots.sh')
| -rwxr-xr-x | test-rig/screenshots.sh | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/test-rig/screenshots.sh b/test-rig/screenshots.sh new file mode 100755 index 0000000..5da6349 --- /dev/null +++ b/test-rig/screenshots.sh @@ -0,0 +1,109 @@ +#!/bin/sh +# Regenerate the F-Droid listing screenshots. +# +# Boots a pixel_6 AVD on the same AOSP API-35 image the e2e tier uses - +# natively 1080x2400, the geometry the tracked PNGs already have - and +# screencaps Main and Settings into +# fastlane/metadata/android/en-US/images/phoneScreenshots/. +# +# The AVD is its own (scrcpy-shots, not scrcpy-test) so a screenshot run +# never perturbs the device state ./test e2e asserts against. Panel size +# comes from the device profile rather than `wm size`: overriding +# geometry on a booted software-GPU emulator ANRs SystemUI, and the ANR +# dialog lands in the capture. +# +# Runs inside the test image; orchestrated by ../test. + +set -eu + +ROOT=/work +# Debug applicationId (note the .debug suffix). Activity classes live in +# the invalid.lena.scrcpy namespace, which carries no suffix. +PKG=invalid.lena.scrcpy.debug +AVD=scrcpy-shots +SERIAL=emulator-5554 +APK=$ROOT/app/build/outputs/apk/debug/app-debug.apk +SHOTS=$ROOT/fastlane/metadata/android/en-US/images/phoneScreenshots +TMPDIR=$ROOT/.tools/shots +WIDTH=1080 +HEIGHT=2400 +mkdir -p "$TMPDIR" "$SHOTS" + +log() { printf 'screenshots: %s\n' "$*" >&2; } + +# ---- 0. server jar ---- +[ -f "$ROOT/app/src/main/assets/scrcpy-server.jar" ] || "$ROOT/scripts/update-server" + +# ---- 1. build APK ---- +log 'gradle :app:assembleDebug' +"$ROOT/gradlew" --no-daemon -q :app:assembleDebug + +# ---- 2. AVD + boot ---- +if ! avdmanager list avd | grep -q "Name: $AVD$"; then + log "creating avd $AVD (pixel_6, ${WIDTH}x${HEIGHT})" + echo no | avdmanager create avd -n "$AVD" -k 'system-images;android-35;default;x86_64' -d pixel_6 >/dev/null +fi + +# Clean stale lock files from a previously aborted run; snapshot.lock.lock +# and read-snapshot.txt in particular make the next boot die with +# "a snapshot operation is pending and timeout has expired". +AVD_DIR="$ANDROID_AVD_HOME/$AVD.avd" +rm -f "$AVD_DIR"/multiinstance.lock "$AVD_DIR"/hardware-qemu.ini.lock \ + "$AVD_DIR"/snapshot.lock.lock "$AVD_DIR"/read-snapshot.txt 2>/dev/null || true +rm -rf "$HOME/.android/avd/running" 2>/dev/null || true + +if ! pgrep -f "emulator.*-avd $AVD" >/dev/null; then + log 'booting emulator (no window, no snapshot)' + emulator -avd "$AVD" -no-window -no-audio -no-snapshot -gpu swiftshader_indirect \ + -no-boot-anim -accel on >"$TMPDIR/emulator.log" 2>&1 & +fi +adb -s "$SERIAL" wait-for-device +until [ "$(adb -s "$SERIAL" shell getprop sys.boot_completed | tr -d '\r')" = 1 ]; do + sleep 2 +done + +trap 'adb -s "$SERIAL" emu kill >/dev/null 2>&1 || true' EXIT INT TERM + +# ---- 3. install ---- +log "install $APK" +adb -s "$SERIAL" install -r "$APK" >/dev/null +# Screenshot 1 is the empty-list state; a re-run must not inherit the +# devices.json a previous run left behind. +adb -s "$SERIAL" shell pm clear "$PKG" >/dev/null + +# ---- 4. capture ---- +# One activity per shot: start it, let the window settle, screencap. +# SettingsActivity is not exported, so this needs adbd running as root. +log 'adb root' +adb -s "$SERIAL" root +adb -s "$SERIAL" wait-for-device + +shot() { + activity=$1 + out=$2 + log "capture $activity -> $out" + adb -s "$SERIAL" shell am force-stop "$PKG" + adb -s "$SERIAL" shell am start -W -n "$PKG/invalid.lena.scrcpy.$activity" >/dev/null + sleep 4 + # Anything else on top - a system dialog, an ANR, a failed launch - + # means the shot is not the screen we asked for. Fail, do not ship it. + focus=$(adb -s "$SERIAL" shell dumpsys window \ + | sed -n 's/.*mCurrentFocus=[^ ]* [^ ]* \([^ }]*\).*/\1/p' | head -1) + case "$focus" in + "$PKG/"*) ;; + *) log "focused window is '$focus', want $PKG/*"; exit 1 ;; + esac + adb -s "$SERIAL" exec-out screencap -p > "$SHOTS/$out" + [ -s "$SHOTS/$out" ] || { log "$out is empty"; exit 1; } + # Reject anything that is not the panel we expect: a wrong-sized PNG + # means the device profile changed and the shot is unusable. + got=$(ffprobe -v error -select_streams v:0 \ + -show_entries stream=width,height -of csv=p=0:s=x "$SHOTS/$out") + [ "$got" = "${WIDTH}x${HEIGHT}" ] || { log "$out is $got, want ${WIDTH}x${HEIGHT}"; exit 1; } +} + +shot Main 1.png +shot SettingsActivity 2.png + +log "wrote $SHOTS/1.png $SHOTS/2.png" +log 'done' |