aboutsummaryrefslogtreecommitdiff
path: root/test-rig/e2e.sh
blob: ae2a1a837d437885ebcc5c0fcdb013baf97ed593 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#!/bin/sh
# Single-emulator self-mirror E2E.
#
# Boot an AOSP API-35 emulator, root it, install the app, let it
# generate its keypair, copy that pubkey into /data/misc/adb/adb_keys
# so the app can connect to its own host's adbd over TCP, launch
# Mirror against 127.0.0.1:5555, wait for the first video frame in
# logcat, screencap, assert the frame is non-uniform.
#
# 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-test
SERIAL=emulator-5554
APK=$ROOT/app/build/outputs/apk/debug/app-debug.apk
TMPDIR=$ROOT/.tools/e2e
mkdir -p "$TMPDIR"

log() { printf 'e2e: %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"
    echo no | avdmanager create avd -n "$AVD" -k 'system-images;android-35;default;x86_64' -d pixel >/dev/null
fi

# Clean stale lock files from a previously aborted run; otherwise the
# emulator refuses to start ("Running multiple emulators with the same AVD").
AVD_DIR="$ANDROID_AVD_HOME/$AVD.avd"
rm -f "$AVD_DIR"/multiinstance.lock "$AVD_DIR"/hardware-qemu.ini.lock 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

log 'wait-for-device'
adb -s "$SERIAL" wait-for-device
log 'wait for boot'
until [ "$(adb -s "$SERIAL" shell getprop sys.boot_completed | tr -d '\r')" = 1 ]; do
    sleep 2
done

# ---- 3. root (emulator adbd already listens on TCP 5555) ----
log 'adb root'
adb -s "$SERIAL" root
adb -s "$SERIAL" wait-for-device

# ---- 4. install ----
log "install $APK"
adb -s "$SERIAL" install -r "$APK" >/dev/null

# ---- 5. let the app generate its keypair ----
log "launching Main once so Adb.java writes filesDir/adbkey"
adb -s "$SERIAL" shell am start -W -n "$PKG/invalid.lena.scrcpy.Main" >/dev/null
# Adb.java writes the keypair inside its constructor, before setContentView
sleep 1

# ---- 6. extract pubkey and authorise ----
log 'reading filesDir/adbkey via run-as'
adb -s "$SERIAL" shell "run-as $PKG cat files/adbkey" > "$TMPDIR/adbkey.der"
if [ ! -s "$TMPDIR/adbkey.der" ]; then
    log 'adbkey was empty - did Adb.java run?'
    adb -s "$SERIAL" logcat -d -t 200 -s scrcpy-android >&2 || true
    exit 1
fi

log 'computing android pubkey blob'
java "$ROOT/test-rig/Keygen.java" "$TMPDIR/adbkey.der" > "$TMPDIR/adb_keys.line"

log 'appending to /data/misc/adb/adb_keys'
adb -s "$SERIAL" push "$TMPDIR/adb_keys.line" /sdcard/adb_keys.line >/dev/null
adb -s "$SERIAL" shell "cat /sdcard/adb_keys.line >> /data/misc/adb/adb_keys \
                        && chown system:shell /data/misc/adb/adb_keys \
                        && chmod 640 /data/misc/adb/adb_keys \
                        && rm /sdcard/adb_keys.line"

# adbd rereads /data/misc/adb/adb_keys on every AUTH challenge,
# so no daemon restart is necessary. Restarting it would put the
# host's adb client into 'offline' state and force a reconnect dance.

# ---- 7. trigger Mirror ----
log 'starting Mirror -> 127.0.0.1:5555'
adb -s "$SERIAL" logcat -c
adb -s "$SERIAL" shell am start -n "$PKG/invalid.lena.scrcpy.Mirror" \
        --es host 127.0.0.1 --ei port 5555 >/dev/null

# ---- 8. wait for first frame, fail on session give-up ----
# Server's openAbstract budget (Server.OPEN_DEADLINE_MS) must stay
# shorter than this deadline; otherwise our retries finish before
# we've given the wire a chance.
deadline=$(( $(date +%s) + ${E2E_DEADLINE:-60} ))
ok=
while [ "$(date +%s)" -lt "$deadline" ]; do
    line=$(adb -s "$SERIAL" logcat -d -s scrcpy-android | tail -200)
    if printf '%s' "$line" | grep -q 'video frame n=1'; then
        ok=1; break
    fi
    if printf '%s' "$line" | grep -q 'session: gave up'; then
        log 'session gave up; logcat:'
        printf '%s\n' "$line" | tail -50 >&2
        exit 1
    fi
    sleep 1
done
if [ -z "$ok" ]; then
    log 'timed out waiting for video frame n=1'
    log "full app log: $TMPDIR/logcat.scrcpy"
    adb -s "$SERIAL" logcat -d -s scrcpy-android > "$TMPDIR/logcat.scrcpy" 2>&1 || true
    log "full system log: $TMPDIR/logcat.full"
    adb -s "$SERIAL" logcat -d > "$TMPDIR/logcat.full" 2>&1 || true
    log 'last 80 scrcpy-android lines:'
    tail -80 "$TMPDIR/logcat.scrcpy" >&2 || true
    exit 1
fi
log 'video frame n=1 - proceeding to screencap'

# Give the decoder a moment to actually render to the surface.
sleep 1

# ---- 9. screencap + pixhash ----
adb -s "$SERIAL" exec-out screencap -p > "$TMPDIR/shot.png"
log "screencap $(wc -c <"$TMPDIR/shot.png") bytes"
java "$ROOT/test-rig/Pixhash.java" "$TMPDIR/shot.png" --min 5

# ---- 9b. mid-session disconnect -> auto-reconnect ----
# Kill the scrcpy server on the target out from under the app. The video
# socket dies; Session.run() must detect it, rerun the bring-up ladder,
# and a fresh VideoStream must log 'video frame n=1' again. Clear logcat
# first so the only frame we can match is the post-reconnect one.
log 'forcing a mid-session disconnect (pkill scrcpy server on target)'
adb -s "$SERIAL" logcat -c
adb -s "$SERIAL" shell 'pkill -f com.genymobile.scrcpy.Server' || true

rdeadline=$(( $(date +%s) + ${E2E_RECONNECT_DEADLINE:-60} ))
relink=
while [ "$(date +%s)" -lt "$rdeadline" ]; do
    if adb -s "$SERIAL" logcat -d -s scrcpy-android | grep -q 'video frame n=1'; then
        relink=1; break
    fi
    sleep 1
done
if [ -z "$relink" ]; then
    log 'timed out waiting for auto-reconnect (no fresh video frame n=1)'
    adb -s "$SERIAL" logcat -d -s scrcpy-android > "$TMPDIR/logcat.reconnect" 2>&1 || true
    log "full app log: $TMPDIR/logcat.reconnect"
    tail -80 "$TMPDIR/logcat.reconnect" >&2 || true
    exit 1
fi
log 'auto-reconnect ok - fresh video frame n=1 after link loss'

# ---- 10. cleanup ----
log "full app log: $TMPDIR/logcat.scrcpy"
adb -s "$SERIAL" logcat -d -s scrcpy-android > "$TMPDIR/logcat.scrcpy" 2>&1 || true
log 'adb emu kill'
adb -s "$SERIAL" emu kill || true

log 'e2e: pass'