diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 628cead8ab2..c1c3d15ee91 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -113,7 +113,7 @@ Those sleeps look like recoverable overhead - `fm-watch-triage.test.sh` alone is Sampling less often does not remove that wait, it only delays detection: raising the interval to 0.5s and charging each sample proportionally measured `fm-watch-triage.test.sh` at 435s and 440s against 390s and 393s for the unchanged script, back to back on 2026-09-03, because each of its ~40 poll-cycle waits and ~73 process-exit waits paid up to half a second more. Some of those loops are also catching a transient rather than waiting for a settled condition, so a coarser sample can step over the state they assert on. Discover tests by listing `tests/*.test.sh`: each is a self-contained bash script named `.test.sh`, and its header comment describes what it covers, so pass one to `bin/fm-test-run.sh` to focus on a subject with canonical timing output. -Shared test helpers live in `tests/lib.sh` (reporters, temp roots, git fixtures), `tests/fixtures.sh` (fake toolchain and spawn-world builders), `tests/wake-helpers.sh`, and `tests/secondmate-helpers.sh`. +Shared test helpers live in `tests/lib.sh` (reporters, temp roots, git fixtures), `tests/fixtures.sh` (fake toolchain and spawn-world builders), `tests/wake-helpers.sh`, `tests/secondmate-helpers.sh`, and `tests/git-config-helpers.sh` (fixture Git isolation from the host's global and system configuration, already sourced by `tests/lib.sh` and `tests/herdr-test-safety.sh`; a suite that sources neither must source it itself before its first Git operation so a direct invocation stays isolated). Source those instead of copying a fake toolchain into a new suite. A fixture may shorten a production timeout to keep a failure path prompt, but never below what the real work inside that window costs on a loaded machine: a fork, an exec, a lock acquisition, a beacon publication, or a first-poll check. Where a case's assertion is not about the timeout itself, give that window headroom over the measured loaded cost, and bound the test's own waiting with iteration-counted poll loops, which stretch under load where a wall-clock budget does not. diff --git a/bin/fm-test-run.sh b/bin/fm-test-run.sh index ee9f32e04df..8817717424b 100755 --- a/bin/fm-test-run.sh +++ b/bin/fm-test-run.sh @@ -111,6 +111,10 @@ # live-capability (a live-harness guard governed by fm_live_gate, which records # unavailable tools and explicit policy skips; see tests/lib.sh), or none. # +# Every selected script runs isolated from the host's global and system Git +# configuration, including one that sources no test helper of its own; +# tests/git-config-helpers.sh owns that contract and its limits. +# # Family labels, the changed-file map, and production portable-shard composition # live in this script only (one owner). The proven-isolated candidate set remains # owned by bin/fm-test-isolation-proof.sh; portable parallel shards are a @@ -1139,12 +1143,14 @@ select_family() { [ "$found" -eq 1 ] || die "no tests mapped to family '$want'" } -families_for_test_reference() { - local needle=$1 s +families_for_test_reference() { # ... + local s needle local found=0 + local -a needles=() + for needle in "$@"; do needles+=(-e "$needle"); done while IFS= read -r s; do [ -n "$s" ] || continue - if grep -Fq "$needle" "$s"; then + if grep -Fq "${needles[@]}" "$s"; then family_for_basename "$(basename "$s")" found=1 fi @@ -1221,12 +1227,21 @@ families_for_changed_path() { # resolution in the caller; emit a marker family of __script__ printf '%s\n' "__script__:$(basename "$path")" ;; - bin/fm-test-run.sh|bin/fm-test-isolation-proof.sh) + bin/fm-test-run.sh) # Deliberately the WHOLE family, not just the two contract tests. This # runner executes every pure-contract-unit script, so a change to it is # only proven by running them: its own contract test passing says the # runner's logic is right, not that the suite it drives still runs. printf '%s\n' pure-contract-unit + # Only this script wraps each suite in run_script_bounded's fixture Git + # isolation, and only a standalone-family script proves it. + printf '%s\n' "__script__:fm-test-fixtures.test.sh" + ;; + bin/fm-test-isolation-proof.sh) + # Same reason as the runner above: the proof drives every + # pure-contract-unit script. It runs each candidate directly, never + # through run_script_bounded, so it cannot regress fixture Git isolation. + printf '%s\n' pure-contract-unit ;; bin/backends/herdr*|bin/fm-herdr-lab.sh|tests/herdr-test-safety.sh) printf '%s\n' real-herdr-gated @@ -1432,6 +1447,12 @@ families_for_changed_path() { docs/configuration.md|docs/supervision-protocols/*) printf '%s\n' pure-contract-unit ;; + tests/git-config-helpers.sh) + # The reference scan is not transitive, so match the two helpers that + # source this one as well: most suites inherit it only through them. + families_for_test_reference git-config-helpers.sh lib.sh herdr-test-safety.sh \ + || printf '%s\n' "__unmapped__:$path" + ;; tests/lib.sh|tests/*-helpers.sh|tests/fixtures.sh) families_for_test_reference "$(basename "$path")" \ || printf '%s\n' "__unmapped__:$path" @@ -2179,6 +2200,12 @@ record_script_result() { # because an unbounded suite is what silently outruns its caller's budget. run_script_bounded() { #