From 8f94e3caa0b1b477bacbbbd8954f2cea743a9337 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 21 Sep 2026 19:17:27 +0000 Subject: Add gradle-report, android-ui and publish-apk Three more wrappers, each from a failure that actually happened while building Listen rather than an imagined one. gradle-report: detekt rejects an invalid config property before analysing anything, exits non-zero, and leaves the previous run's XML in place. That file was read as current findings three times in one session; the tell was line numbers that never moved after a refactor. It now names the rejected property and refuses any report older than the run. Both real shapes of the message are covered -- a misspelled property and structurally invalid YAML say different things. android-ui: a blind `input tap 400 300` landed in FeeDreader's alarm settings, and the screenshot taken immediately after was reported as Listen. Every command verifies the foreground package before acting, and taps resolve by label rather than coordinate. A label matching several elements refuses rather than taking the first -- in a list where every row has the same Play button, the first match is rarely the one meant. The node parsing is a separate file on purpose. The first version inlined it as python3 -c inside a bash case block and the nested quoting mangled the regexes silently: the dump reported one element and a tap landed somewhere else entirely. Shell-quoted parsers are their own trap. publish-apk: publishing is where a silent mistake costs most -- a stale published APK once sat for eight weeks and installing it threw a Room downgrade. It checks the host, proves the file is a real APK rather than an HTML error page, byte-compares what landed against what was tested, and confirms the server serves those same bytes. Test suite is 25 cases, still weighted toward the negative ones: a wrapper that only ever reports PASS is worse than no wrapper. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B --- README.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'README.md') diff --git a/README.md b/README.md index e3b96c6..a41b312 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ Corollaries, each learned the hard way: | `set -e` + a command that legitimately fails | `pid=$(pidof x)` aborts the whole script when the process is gone — i.e. exactly when you are trying to detect a crash | `|| true` on any substitution whose non-zero status is meaningful data | | Discarded command output | `adb install ... >/dev/null` hides "filename doesn't end .apk"; the old build stays installed and is silently what gets tested | `android-launch-check --apk` checks the install output for `Success` | | `monkey` as a launch check | Reports a healthy app dead and a dead app healthy; lands on whatever activity is foreground | `android-launch-check` uses `am start -W` against a resolved activity and samples the pid repeatedly | +| Stale **analysis** reports | An invalid detekt config aborts the run *before analysing anything*, leaving the previous XML in place. The findings look current and the line numbers never move | `gradle-report` names the rejected config and refuses any report older than the run | +| Blind `input tap X Y` | Coordinates are a guess about a layout. One tap landed in a different app's settings; the screenshot afterwards was reported as the app under test | `android-ui` verifies the foreground package before acting, and taps by label rather than coordinate | +| Tapping the first of several matches | Every row in a list carries the same "Play" button; the first match is rarely the one meant | `android-ui tap` refuses when a label matches more than one place | +| Publishing without verifying | A published APK went stale for eight weeks; installing it threw `A migration from 11 to 6 was required but not found` | `publish-apk` checks the host, proves the file is really an APK, byte-compares what landed, and confirms what the server serves | --- @@ -86,3 +90,55 @@ Exit codes: `0` launched and stayed up · `1` crashed or died · `2` operator er 12 cases, mostly negative — each drives a script into one failure mode and asserts it is caught. A wrapper that only ever reports PASS is worse than no wrapper, so the negative cases are the point. + +### `gradle-report [-- ]` + +Runs a static-analysis task and reports from its XML, refusing to speak if the +report is older than the run. + +The failure this exists for is subtle: detekt rejects an invalid config property +*before* analysing anything, exits non-zero, and leaves the previous run's XML +untouched. Read that file and you get a plausible list of findings describing +code that no longer exists — which is exactly what happened three times in one +session, the giveaway being line numbers that never moved after a refactor. + +``` +gradle-report /workspace/listen detekt +``` + +Prints `CONFIG INVALID` and names the offending property when the config is +rejected; prints `STALE` when the report predates the run; otherwise lists the +findings and passes only on zero. + +### `android-ui [args]` + +Drives an app on a device without guessing at coordinates. + +``` +android-ui org.terst.listen foreground # is the right app even in front? +android-ui org.terst.listen dump # what is on screen, with tap points +android-ui org.terst.listen tap "Settings" # tap by label, not by coordinate +android-ui org.terst.listen shot /tmp/x.png # screenshot, foreground verified +``` + +Every command verifies the foreground package first. `tap` refuses when a label +matches more than one element, because in a list where every row has the same +button the first match is rarely the one meant, and prefers a clickable node +when a label and its button are separate nodes. + +The parsing lives in `lib/uinodes.py` rather than inline in the shell script: +the first version embedded it as `python3 -c` inside a bash `case` block, and +the nested quoting silently mangled the regexes — the dump reported one element +and a tap landed on the wrong one. + +### `publish-apk [--host H] [--dir D]` + +Publishes a built APK to the static site, checking each thing that has gone +wrong before: that this is the deploy host and not a box resembling it, that the +file is genuinely an APK rather than an HTML error page, that what landed is +byte-identical to what was tested, and that the web server serves those same +bytes. Backs up any existing file first. + +``` +publish-apk app/build/outputs/apk/debug/app-debug.apk listen-debug.apk +``` -- cgit v1.2.3