diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8978bf5..2035e58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -606,17 +606,26 @@ jobs: fi tail -F "${{ github.workspace }}/emulator_run$N.txt" & TAIL_PID=$! + # Every adb call here is bounded. Against a wedged device adb blocks + # indefinitely, so an unbounded call makes these loops never iterate: + # the budgets below would not apply and the step would sit silent + # until the job timeout, with no diagnostics. for _ in $(seq 1 36); do - kill -0 "$EMU_PID" 2>/dev/null || { echo "ERROR: emulator process exited early (run $N)"; exit 1; } - adb get-state >/dev/null 2>&1 && break + kill -0 "$EMU_PID" 2>/dev/null || { echo "ERROR: emulator process exited early (run $N)"; tail -40 "${{ github.workspace }}/emulator_run$N.txt"; exit 1; } + timeout 10 adb get-state >/dev/null 2>&1 && break sleep 5 done - adb get-state >/dev/null 2>&1 || { echo "ERROR: no device after 180s (run $N)"; exit 1; } + timeout 10 adb get-state >/dev/null 2>&1 || { echo "ERROR: no device after 180s (run $N)"; tail -40 "${{ github.workspace }}/emulator_run$N.txt"; exit 1; } for _ in $(seq 1 48); do - [ "$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" = "1" ] && break + [ "$(timeout 15 adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" = "1" ] && break sleep 10 done - [ "$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" = "1" ] || { echo "===== boot timeout (run $N) ====="; exit 1; } + if [ "$(timeout 15 adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" != "1" ]; then + echo "===== boot timeout (run $N): the emulator never finished booting =====" + echo "----- last 60 lines of the emulator log -----" + tail -60 "${{ github.workspace }}/emulator_run$N.txt" || true + exit 1 + fi kill "$TAIL_PID" 2>/dev/null || true T_RESUME=$(date +%s) @@ -639,22 +648,45 @@ jobs: echo "RUN $N entry screenshot: $(stat -c %s "${{ github.workspace }}/screenshots/run$N-entry.png" 2>/dev/null || echo 0) bytes" fi - echo "RUN $N app pid after boot/restore (pre am-start): $(adb shell pidof "$PKG" 2>/dev/null | tr -d '\r' || echo none)" - # Start (or re-foreground, after a restored snapshot) the app; the - # TITLE screen needs one tap to start a round — auto-play only - # pilots during PLAYING. - adb shell am start -n "$PKG/android.app.NativeActivity" || true - for _ in $(seq 1 30); do - adb shell dumpsys window 2>/dev/null | grep -qi "ocus.*vulkanspaceinvaders" && break - sleep 1 - done + if [ "$N" = "1" ]; then + # The only launch in the whole job. The TITLE screen needs one tap + # to start a round -- auto-play only pilots during PLAYING. + adb shell am start -n "$PKG/android.app.NativeActivity" || true + for _ in $(seq 1 30); do + timeout 15 adb shell dumpsys window 2>/dev/null | grep -qi "ocus.*vulkanspaceinvaders" && break + sleep 1 + done + # pidof races a launch: a single query can come back empty while + # the process is still starting, which would then suppress the + # taps below and leave the game sitting on the TITLE screen. + for _ in $(seq 1 15); do + APP_PID="$(timeout 15 adb shell pidof "$PKG" 2>/dev/null | tr -d '\r')" + [ -n "$APP_PID" ] && break + sleep 1 + done + echo "RUN $N app pid after launch: ${APP_PID:-none}" + else + # Deliberately NOT relaunched. The question this job asks is + # whether the snapshot brings the app back by itself, so starting + # it here would answer it for the emulator. + APP_PID="$(timeout 15 adb shell pidof "$PKG" 2>/dev/null | tr -d '\r')" + if [ -n "$APP_PID" ]; then + echo "RUN $N app survived restore: YES (pid $APP_PID)" + else + echo "RUN $N app survived restore: NO (process gone; left as-is)" + fi + fi sleep 3 T_FIRST_TAP=$(date +%s) - for _ in 1 2 3 4; do - adb shell input tap 540 1500 || true - sleep 2 - done - sleep 5 + # Only drive the game while it is actually running; tapping a home + # screen would just launch something at random. + if [ -n "${APP_PID:-}" ]; then + for _ in 1 2 3 4; do + adb shell input tap 540 1500 || true + sleep 2 + done + sleep 5 + fi # Diagnostics and the liveness probe run BEFORE the exit screenshot, # so that screenshot is the last thing captured and sits ~1s from the # freeze. (It used to come first, leaving 15-20s of auto-play between