summaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md88
1 files changed, 88 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e3b96c6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,88 @@
+# devtools
+
+Small, shared wrappers around Gradle and adb whose only job is to make a build or
+test result **impossible to misread**.
+
+These exist because of a specific, repeated failure: concluding that something
+passed or failed by reading console text, when the text was about a different
+project, a stale result, or a command that never ran. Every script here reports
+from exit codes and structured artifacts instead, and fails loudly rather than
+defaulting to something plausible.
+
+Invoke by absolute path — `/workspace/devtools/bin/<script>`. Nothing needs to be
+on `PATH`, which is itself one less piece of ambient state to get wrong.
+
+---
+
+## The rule these encode
+
+> **Never conclude from grepped stdout. Conclude from exit codes and structured
+> artifacts.**
+
+Corollaries, each learned the hard way:
+
+| Trap | What it looks like | Guard |
+| --- | --- | --- |
+| cwd drift | `cd x && cp y .` fails; the next line runs Gradle in the old directory and prints `BUILD SUCCESSFUL` about the wrong project | `gradle-verify` takes the project dir as an argument, resolves it absolutely, and passes `-p`. A missing dir is fatal, never a fallback. |
+| Stale test results | Gradle reports `UP-TO-DATE`, runs nothing, and the XML on disk describes older code | `gradle-test` compares result mtimes against a marker written at run start and refuses stale numbers |
+| Zero tests | A test source set that won't compile leaves a green build with nothing executed | `gradle-test` fails when the parsed test count is 0 |
+| `cmd \| tail; echo $?` | Reports `tail`'s status, not the command's | Redirect to a file, then check `$?`. Or use `PIPESTATUS[0]`. |
+| `... \| grep -q` under `pipefail` | `grep -q` exits on first match and SIGPIPEs upstream; the pipeline reports failure despite matching | Do membership tests in pure shell (`case`), not pipelines |
+| `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 |
+
+---
+
+## Scripts
+
+### `gradle-verify <project-dir> <task...> [-- <gradle args>]`
+
+Runs Gradle against an explicitly named project. Exits with Gradle's exit code.
+Prints the absolute project directory actually used, so the output is
+self-describing.
+
+```
+/workspace/devtools/bin/gradle-verify /workspace/nav/android-app compileDebugKotlin
+```
+
+### `gradle-test <project-dir> [task] [--allow-stale] [-- <gradle args>]`
+
+Runs the test task, then reports counts parsed from the JUnit XML. Auto-detects
+`testDebugUnitTest` for Android projects and `test` otherwise.
+
+Fails on: non-zero Gradle exit, any failure or error in the XML, **zero tests
+executed**, **no XML at all**, or **results that predate this run**.
+
+```
+/workspace/devtools/bin/gradle-test /workspace/nav/android-app -- --rerun-tasks
+```
+
+`--rerun-tasks` is usually what you want when the answer matters — otherwise
+Gradle may skip the task and the script will (correctly) refuse the stale
+results.
+
+### `android-launch-check <package> [--apk <path>] [--serial <s>] [--settle <secs>]`
+
+Answers "does this build actually start and stay up", and shows the exception if
+not. Optionally installs an APK first, verifying the install genuinely succeeded.
+
+Exit codes: `0` launched and stayed up · `1` crashed or died · `2` operator error
+(no device, package not installed, unusable filename).
+
+```
+/workspace/devtools/bin/android-launch-check org.terst.feedreader \
+ --apk /path/to/app-debug.apk --settle 15
+```
+
+---
+
+## Tests
+
+```
+/workspace/devtools/test/run-tests.sh
+```
+
+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.