Skip to content

refactor: extract shared Android instrumentation-helper#903

Merged
thymikee merged 1 commit into
mainfrom
refactor/android-instrumentation-helper
Jun 27, 2026
Merged

refactor: extract shared Android instrumentation-helper#903
thymikee merged 1 commit into
mainfrom
refactor/android-instrumentation-helper

Conversation

@thymikee

Copy link
Copy Markdown
Member

What

The Android snapshot helper and multi-touch helper both drive am instrument -w and independently re-implement the same low-level glue:

  • parsing INSTRUMENTATION_STATUS / INSTRUMENTATION_RESULT key/value records
  • reading optional numeric/boolean values out of those records
  • validating a bundled JSON manifest's integer + string-literal fields

This PR extracts the genuinely-identical parts into a new single-source module src/platforms/android/instrumentation-helper.ts and has both helpers consume it:

  • parseInstrumentationRecords(output) — status + result record parsing (+ the shared = key/value reader)
  • readInstrumentationResultNumber / readInstrumentationResultBoolean
  • readAndroidHelperManifestInteger / readAndroidHelperManifestLiteral — label-parameterized ("snapshot helper" / "multi-touch helper") so each helper's exact error message is preserved byte-for-byte

multitouch-helper.ts previously carried its own ~25-line copy of the result parser; it now calls parseInstrumentationRecords(...).results. snapshot-helper-capture.ts re-exports the shared metadata readers under their existing public names (readAndroidSnapshotHelperMetadataNumber/Boolean) so snapshot-helper-session.ts keeps importing them unchanged.

LOC

Consumers shed 173 lines of duplicated/relocated parsing+validation logic, now single-sourced in the new 134-line shared module (+177 / -173 across 4 files; net ~flat, duplication eliminated). The genuine duplicate that was deleted outright is multi-touch's parallel INSTRUMENTATION parser plus the per-helper manifest integer/literal validators.

Validation

  • tsc -p tsconfig.json --noEmit — clean
  • oxfmt --write + oxlint --deny-warnings — clean
  • vitest run android snapshot-helper multitouch284 passed (25 files), including the manifest message tests (... outputFormat must be "uiautomator-xml"., ... sha256 must be a 64-character hex string.) and the result-number parsing tests (injectedEvents/elapsedMs).

behaviorless

No behavior change for any valid input. The shared manifest validators preserve each helper's exact INVALID_ARGS messages via a label parameter; the shared readInstrumentationResultNumber/Boolean are byte-identical to the prior local copies; multitouch's parseInstrumentationResults(output) is exactly parseInstrumentationRecords(output).results (the extra status records are collected but discarded by multitouch, which only reads .results).

Partial by design — the behavior-bearing differences were intentionally left in each consumer and NOT force-unified:

  • install cache: Set<string> (multi-touch, no policy) vs Map<string,number> (snapshot, with install-policy / forget-on-failure / retry-on-INSTALL_FAILED_UPDATE_INCOMPATIBLE)
  • installed-versionCode read: single-regex + hardcoded 5s timeout (multi-touch) vs per-line parser + caller-supplied timeout (snapshot)
  • sha256File: whole-file readFile (multi-touch) vs streaming (snapshot)
  • readString: value.length === 0 (multi-touch) vs value.trim().length === 0 (snapshot) — and therefore readSha256, which depends on it
  • artifact resolution + per-helper manifest schemas differ entirely

Both the snapshot helper and the multi-touch helper drive 'am instrument -w'
and parse the same INSTRUMENTATION_STATUS/INSTRUMENTATION_RESULT key/value
records, and both validate a bundled JSON manifest with the same integer and
literal field rules.

Extract the genuinely-identical parts into a shared
src/platforms/android/instrumentation-helper.ts:
- parseInstrumentationRecords (status + result record parsing) and the
  shared key/value reader
- readInstrumentationResultNumber / readInstrumentationResultBoolean
- readAndroidHelperManifestInteger / readAndroidHelperManifestLiteral
  (label-parameterized so each helper's exact error message is preserved)

multitouch-helper now consumes the shared record parser (was its own
~25-line copy) and the shared numeric reader; snapshot-helper-capture
re-exports the shared metadata readers under their existing public names so
snapshot-helper-session keeps importing them unchanged.

Behaviorless: the divergent, behavior-bearing parts are intentionally left
in each consumer (install caches Set vs Map + install-policy/forget/retry,
installed-versionCode regex + timeout, sha256File stream-vs-read,
readString trim semantics and the sha256 validator that depends on it,
artifact resolution + per-helper manifest schemas).
@github-actions

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.4 MB 1.4 MB -128 B
JS gzip 445.7 kB 445.7 kB -45 B
npm tarball 584.5 kB 584.5 kB 0 B
npm unpacked 2.0 MB 2.0 MB -128 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.3 ms 27.2 ms -0.1 ms
CLI --help 47.4 ms 47.5 ms +0.1 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/android.js -347 B -122 B
dist/src/7394.js +219 B +77 B

@thymikee

Copy link
Copy Markdown
Member Author

Codex review: no actionable findings.

I reviewed the Android helper extraction against both production consumers. parseInstrumentationRecords preserves the snapshot status/result parser and gives multi-touch the same result parsing it already had via .results; the numeric/boolean readers are equivalent to the previous local helpers. The manifest integer/literal validators keep the helper-specific labels, so the user-facing INVALID_ARGS messages stay the same. I also checked that behavior-bearing differences remain in the consumers rather than being forced into the shared helper.

Validation: all CI checks are green, including Android smoke. I also ran pnpm exec vitest run src/platforms/android/__tests__/snapshot-helper.test.ts src/platforms/android/__tests__/multitouch-helper.test.ts locally in the PR worktree: 33 tests passed.

Residual risk: low. This is Android device-adjacent, so the live path still depends on the CI Android smoke coverage rather than a separate manual emulator session from me.

@thymikee thymikee merged commit d291e4c into main Jun 27, 2026
20 checks passed
@thymikee thymikee deleted the refactor/android-instrumentation-helper branch June 27, 2026 12:57
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-06-27 12:57 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant