summaryrefslogtreecommitdiff
path: root/test/run-tests.sh
AgeCommit message (Collapse)Author
2026-09-25publish-apk: allow publishing from another machine over sshHEADmainClaude
The host gate stopped the Fedora laptop publishing at all, which left a build made there with nowhere to go. It now takes PUBLISH_SSH and sends the file to the real host instead. The gate itself is unchanged in spirit: without PUBLISH_SSH a build from elsewhere is still refused, because the failure it guards against is writing to a local path that merely looks like the deploy host's. Two tests cover that -- the error names the way out, and a failed remote publish never falls back to writing locally. Transfers land on a temporary name and move into place only once complete, and the remote copy is hash-compared against the source before the served bytes are checked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
2026-09-21gradle-report: a missing report is a failure, not a passClaude
The lint path was wrong -- AGP writes to app/build/reports/, not app/build/ -- and the script fell back to the gradle exit code, printing 'report: MISSING ... result: PASS'. A checker that cannot find what it is meant to check and passes anyway is precisely the failure these wrappers exist to prevent, and it hid real lint errors in Listen for one run. Now locates the report rather than assuming a path, and a missing report fails while saying the check did not actually inspect anything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
2026-09-21publish-apk: cap the backups it createsClaude
Four publishes in one evening left 448MB of .bak copies of a 112MB APK, in a public web directory. A backup policy with no retention becomes the problem it was guarding against. Keeps the two most recent, configurable with --keep. Prunes before copying, so a failed publish doesn't skip the pruning as well. The first version of the test gave all four fixture backups the same mtime, so 'ls -t' ordered them arbitrarily and 'keeps the most recent' proved nothing -- it only failed because the arbitrary order happened to go the wrong way. Worth recording: a test that passes for the wrong reason is the same class of problem as the dead code these guards exist to catch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
2026-09-21publish-apk: fix a SIGPIPE this repo's own README warns aboutClaude
The zip check used `unzip -l | grep -q`. Under pipefail, grep exits on its first match and SIGPIPEs unzip, so the pipeline fails on a perfectly valid file. It rejected a real 112MB APK as 'not an APK'. That exact trap is in the README's table, written after it broke crash detection in android-launch-check. Knowing about a trap is not the same as not falling into it, which is the argument for these scripts existing at all. Captures the listing first and tests it with `case`, and the new test builds a 300-entry zip so grep matches long before the listing ends -- which is when SIGPIPE actually bites. A single-entry fixture would have passed either way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
2026-09-21gradle-report: name UP-TO-DATE as the cause of a stale reportClaude
Found by running it: detekt exited 0 while the report predated the run, because Gradle skipped the task. 'STALE' alone leaves the reader to work out why nothing changed, so it now says which task was skipped and how to force it, with --allow-stale to accept an older report deliberately. Same shape as gradle-test, which learned this first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
2026-09-21Add gradle-report, android-ui and publish-apkClaude
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
2026-09-18Add devtools: build/test wrappers that can't be misreadPeter Stone
Three times in one session I reported a build or test result that was about something other than what I thought I was testing: - A failed `cd` meant Gradle ran in the previous directory; the "BUILD SUCCESSFUL" I reported was for the wrong tree. - `adb shell monkey` + a single `pidof` sample reported a healthy app as crashed and a crashed app as healthy, on the same emulator minutes apart. - `adb install` refused a filename and printed so to stdout, which was redirected to /dev/null; the old build stayed installed and got tested. All three share a cause: concluding from console text rather than from exit codes and structured artifacts. These wrappers make that mistake hard. gradle-verify pins the project with -p from an absolutely-resolved argument, treats a missing dir as fatal rather than falling back to cwd, and reports from the exit code. gradle-test parses JUnit XML for counts, and fails on zero tests executed, on no XML at all, and on results whose mtime predates the run (Gradle reporting UP-TO-DATE and skipping the task). android-launch-check resolves the launcher activity from the package manager rather than guessing, starts logcat capture before launching, uses `am start -W`, samples the pid across a settle window so a process that starts and then dies is a failure, and prints the fatal stack. Optionally installs an APK first and verifies the install actually reported Success. Two bugs of exactly this class were found and fixed in the scripts themselves while testing them, both of which broke the crash-detection path specifically: `... | grep -q` under pipefail SIGPIPEs its upstream and reports failure on a successful match, and `pid=$(pidof ...)` under `set -e` aborts the script at the moment the process is gone. Verified against the real projects: nav/android-app 241 tests, nav/test-runner 122, feedreader 39, and both the pass and crash paths of android-launch-check on a live emulator. gradle-test immediately caught nav reporting UP-TO-DATE with 33 stale result files, which naively read as "241 tests pass" without running one. 12 self-tests, mostly negative cases.