test-infra: reliable local instrumented-test helper (connectedDebugAndroidTest + emulator hygiene) #280

Merged
JMR-dev merged 3 commits from test-infra-269-local-instrumented-helper into main 2026-07-04 04:29:07 +00:00
JMR-dev commented 2026-07-04 03:55:38 +00:00 (Migrated from github.com)

What

Adds .claude/skills/preflight/local_instrumented.sh (+ a short sibling README): a helper for running LibreMail's instrumented / E2E tests locally without Gradle Managed Devices.

On this machine (Windows + AEHD 2.2), local GMD tasks (apiXXDebugAndroidTest) fail in GMD's AVD snapshot step (AvdSnapshotHandler$EmulatorSnapshotCannotCreatedException: Snapshot creation timed out) — the emulator itself boots fine; only GMD's snapshot machinery is broken. CI is unaffected (it uses connectedDebugAndroidTest, not GMD).

The helper cold-boots one emulator by hand (-no-snapshot, no GMD), waits for sys.boot_completed, then runs :app:connectedDebugAndroidTest filtered to the class(es) you pass — the same technique CI and api37_e2e.py already use:

.claude/skills/preflight/local_instrumented.sh org.libremail.ui.compose.ComposeScreenE2ETest

Key properties (all documented in the script header + README):

  • Targeted by design — takes an explicit <fully.qualified.TestClass>[,...] list. The full ~114-test suite tends to wedge mid-run on this box; small sets don't.
  • Emulator hygiene is mandatory — force-kills stray qemu/emulator processes before booting, tears the emulator down afterward (via an EXIT/INT/TERM trap so it runs on Ctrl-C/error too), and exits non-zero (code 3) if an orphaned qemu-system-x86_64-headless.exe survives. Accumulated orphans have frozen this machine, so this is not optional.
  • Pins JAVA_HOME to a JDK 17–21 (AGP 9.2 fails on 25+); everything is env-overridable (LOCAL_INSTRUMENTED_*).

Scope note

This PR delivers the reusable helper that encodes the known-reliable local recipe. It does not by itself rewire preflight's api35/api36 steps off GMD (part of #269's broader DoD) — that follow-up can build on this helper.

Validation

Script/doc change — validated by careful review + bash -n (clean) + an arg-guard smoke test (exits 2 with usage). No live emulator run was performed: although adb devices was empty (a run was permitted), the box showed fresh signs of wedged GMD state (active_gradle_devices: MDLockCount 2, stale .lock files) on a shared, freeze-prone machine, so I took the review-only path. The emulator/gradle commands are a faithful composition of the battle-tested manual recipe and the already-proven api37_e2e.py pattern.

Closes #269

🤖 Generated with Claude Code

## What Adds `.claude/skills/preflight/local_instrumented.sh` (+ a short sibling README): a helper for running LibreMail's instrumented / E2E tests **locally without Gradle Managed Devices**. On this machine (Windows + AEHD 2.2), local GMD tasks (`apiXXDebugAndroidTest`) fail in GMD's AVD **snapshot** step (`AvdSnapshotHandler$EmulatorSnapshotCannotCreatedException: Snapshot creation timed out`) — the emulator itself boots fine; only GMD's snapshot machinery is broken. CI is unaffected (it uses `connectedDebugAndroidTest`, not GMD). The helper cold-boots **one** emulator by hand (`-no-snapshot`, no GMD), waits for `sys.boot_completed`, then runs `:app:connectedDebugAndroidTest` filtered to the class(es) you pass — the same technique CI and `api37_e2e.py` already use: ```bash .claude/skills/preflight/local_instrumented.sh org.libremail.ui.compose.ComposeScreenE2ETest ``` Key properties (all documented in the script header + README): - **Targeted by design** — takes an explicit `<fully.qualified.TestClass>[,...]` list. The full ~114-test suite tends to wedge mid-run on this box; small sets don't. - **Emulator hygiene is mandatory** — force-kills stray `qemu`/`emulator` processes *before* booting, tears the emulator down afterward (via an `EXIT`/`INT`/`TERM` trap so it runs on Ctrl-C/error too), and **exits non-zero (code 3) if an orphaned `qemu-system-x86_64-headless.exe` survives**. Accumulated orphans have frozen this machine, so this is not optional. - Pins `JAVA_HOME` to a JDK 17–21 (AGP 9.2 fails on 25+); everything is env-overridable (`LOCAL_INSTRUMENTED_*`). ## Scope note This PR delivers the reusable **helper** that encodes the known-reliable local recipe. It does not by itself rewire preflight's `api35`/`api36` steps off GMD (part of #269's broader DoD) — that follow-up can build on this helper. ## Validation Script/doc change — validated by careful review + `bash -n` (clean) + an arg-guard smoke test (exits 2 with usage). No live emulator run was performed: although `adb devices` was empty (a run was permitted), the box showed fresh signs of wedged GMD state (`active_gradle_devices: MDLockCount 2`, stale `.lock` files) on a shared, freeze-prone machine, so I took the review-only path. The emulator/gradle commands are a faithful composition of the battle-tested manual recipe and the already-proven `api37_e2e.py` pattern. Closes #269 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.