feat(scripts): device-testing perf harness #370

Merged
JMR-dev merged 2 commits from feat-device-testing-harness into main 2026-07-06 20:22:20 +00:00
JMR-dev commented 2026-07-06 18:22:59 +00:00 (Migrated from github.com)

What

A cross-platform, standard-library-only Python package under scripts/device-testing/ that replicates LibreMail''s on-device performance-test scenarios and logging capture. It codifies the methodology run by hand on 2026-07-05 (Pixel 10 Pro XL) and written up in the perf/causation reports.

Package layout

File Role
perf_harness.py CLI entry: device selection, logcat streaming, timestamped run dir, report.
breadcrumbs.py Pure, unit-tested parser for the ImapPerf / MailReader / Reader / MailBackfiller breadcrumbs + open-correlation.
adb.py Safety-guarded adb wrapper (allow-list + deny-list + assertions).
uidump.py uiautomator XML parser + screen recognition (mailbox rows, reader, keyguard/foreign-app guards).
scenarios.py The five scenarios.
report.py Aggregates + timing-tables.md renderer mirroring the manual write-up.
tests/ 68 stdlib-unittest cases + fixtures (a verbatim perf-extract slice; reader/lockscreen/alarm dumps).

Scenarios

cold-open, message-open (uncached), back-nav, prefetch-ab (fetch-policy toggle: Fetch all on Wi-Fi vs Always on-demand), and cross-provider (buckets by breadcrumb account ref).

Device safety (baked in as hard guardrails)

  • Allow-list of adb subcommands; everything else (uninstall/root/remount/reboot/push/pull) is refused.
  • Deny-list + assertions on every shell command: no pm clear / pm uninstall, no reboot/remount/root/verity/factory, no touching databases/ files/ shared_prefs/ datastore/, no redirects; run-as / am constrained to the target package.
  • The only sanctioned app-state mutation is clearing LibreMail''s own cache/ (exact-match). No pm clear, uninstall, or data wipe can be constructed.
  • Screen kept awake and restored; every uiautomator/input step guarded against the keyguard and foreign apps (a sample against either is skipped, not measured).

Validated vs. needs the live run

Validated offline (unit tests, no device): breadcrumb parsing + correlation reproduce the manual timing-tables.md figures exactly (Gmail A1 took=31227 ms, rfc822=60457 B, 4.2 KB/s; Outlook O1 took=2934 ms, 139.5 KB/s); the guardrails accept all known-good and refuse all forbidden commands; screen/keyguard recognition; report rendering; and --dry-run emits the correct command plan for all five scenarios.

Needs the first monitored live run: end-to-end hardware timing capture, and the prefetch-ab settings-screen navigation (no settings dump existed in the manual run, so set_fetch_policy navigates by on-screen option text with a scroll fallback). Per the brief, auto-merge is intentionally not armed — the maintainer reviews the tooling and we validate it live together.

Tests

python -m unittest discover -s scripts/device-testing/tests -p "test_*.py"
# Ran 68 tests ... OK

Pure Python dev tool (stdlib only, matching the .claude/skills/preflight/*.py convention); no Kotlin/app source touched.

🤖 Generated with Claude Code

## What A cross-platform, **standard-library-only** Python package under `scripts/device-testing/` that replicates LibreMail''s on-device performance-test scenarios and logging capture. It codifies the methodology run by hand on 2026-07-05 (Pixel 10 Pro XL) and written up in the perf/causation reports. ## Package layout | File | Role | |------|------| | `perf_harness.py` | CLI entry: device selection, logcat streaming, timestamped run dir, report. | | `breadcrumbs.py` | **Pure, unit-tested** parser for the `ImapPerf` / `MailReader` / `Reader` / `MailBackfiller` breadcrumbs + open-correlation. | | `adb.py` | Safety-guarded `adb` wrapper (allow-list + deny-list + assertions). | | `uidump.py` | uiautomator XML parser + screen recognition (mailbox rows, reader, keyguard/foreign-app guards). | | `scenarios.py` | The five scenarios. | | `report.py` | Aggregates + `timing-tables.md` renderer mirroring the manual write-up. | | `tests/` | 68 stdlib-`unittest` cases + fixtures (a verbatim perf-extract slice; reader/lockscreen/alarm dumps). | ## Scenarios `cold-open`, `message-open` (uncached), `back-nav`, `prefetch-ab` (fetch-policy toggle: *Fetch all on Wi-Fi* vs *Always on-demand*), and `cross-provider` (buckets by breadcrumb account ref). ## Device safety (baked in as hard guardrails) - **Allow-list** of adb subcommands; everything else (uninstall/root/remount/reboot/push/pull) is refused. - **Deny-list + assertions** on every shell command: no `pm clear` / `pm uninstall`, no reboot/remount/root/verity/factory, **no touching `databases/` `files/` `shared_prefs/` `datastore/`**, no redirects; `run-as` / `am` constrained to the target package. - The **only** sanctioned app-state mutation is clearing LibreMail''s own **`cache/`** (exact-match). No `pm clear`, uninstall, or data wipe can be constructed. - Screen kept awake and restored; every uiautomator/input step guarded against the keyguard and foreign apps (a sample against either is skipped, not measured). ## Validated vs. needs the live run **Validated offline (unit tests, no device):** breadcrumb parsing + correlation reproduce the manual `timing-tables.md` figures exactly (Gmail A1 `took=31227 ms`, `rfc822=60457 B`, `4.2 KB/s`; Outlook O1 `took=2934 ms`, `139.5 KB/s`); the guardrails accept all known-good and refuse all forbidden commands; screen/keyguard recognition; report rendering; and `--dry-run` emits the correct command plan for all five scenarios. **Needs the first monitored live run:** end-to-end hardware timing capture, and the `prefetch-ab` **settings-screen** navigation (no settings dump existed in the manual run, so `set_fetch_policy` navigates by on-screen option text with a scroll fallback). Per the brief, **auto-merge is intentionally not armed** — the maintainer reviews the tooling and we validate it live together. ## Tests ``` python -m unittest discover -s scripts/device-testing/tests -p "test_*.py" # Ran 68 tests ... OK ``` Pure Python dev tool (stdlib only, matching the `.claude/skills/preflight/*.py` convention); no Kotlin/app source touched. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.