From d30d9d2ba1ae11d92036ac236970e020ee14e3e6 Mon Sep 17 00:00:00 2001 From: Diogo Silva Sena <227921880+DougBTW@users.noreply.github.com> Date: Thu, 29 Oct 2026 23:32:16 -0400 Subject: [PATCH 1/2] test: run the suite in parallel (bats --jobs), 48 min -> ~4.5 min CI installs GNU parallel and runs one bats job per CPU (serial when parallel is absent). Measured on the Mac: 48 min serial for 2055 tests, 254-264 s with --jobs 14 (3 clean runs in a row). Three tests were not safe side by side: - tests/helpers.bash: one cache dir per test (BATS_TEST_TMPDIR); auto-fix run logs (FI_AF_RUNS) live there and the classify tests mixed them. - tests/autofix-cancel.bats: sleep lengths unique to the test process and an exact pkill, instead of pkill 'sleep 471[34]' killing other tests'. tests/fix-batch8-long-root.bats carried the same pkill without ever starting those sleeps; removed. - tests/cli-sync-default-branch.bats: its own XDG_CACHE_HOME instead of the real ~/.cache plus a teardown that emptied it for everyone. Logs tests/helpers.bash:25 (tests keeping the real HOME write ~/.cache). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MVx3HWniZnrXYfPiCo5Lrs --- .github/workflows/test.yml | 19 ++++++++++++++++--- CONTRIBUTING.md | 9 +++++++-- docs/found-issues.md | 1 + tests/autofix-cancel.bats | 11 +++++++---- tests/cli-sync-default-branch.bats | 6 ++++-- tests/fix-batch8-long-root.bats | 2 +- tests/helpers.bash | 5 ++++- 7 files changed, 40 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6de7defe..8064b1b0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -174,8 +174,9 @@ jobs: - name: Install bats-core + jq run: | + # GNU parallel lets bats run tests side by side (--jobs, below). if [[ "$RUNNER_OS" == "macOS" ]]; then - brew install bats-core jq + brew install bats-core jq parallel elif [[ "$RUNNER_OS" == "Windows" ]]; then # bats-core ships an npm package that drops a Windows-compatible # bats wrapper on PATH. jq is preinstalled on windows-latest. @@ -183,7 +184,7 @@ jobs: jq --version else sudo apt-get update - sudo apt-get install -y bats jq + sudo apt-get install -y bats jq parallel fi - name: Verify dependencies @@ -198,8 +199,20 @@ jobs: git config --global user.email "ci@example.com" git config --global user.name "CI" + # One job per CPU (serial on the Mac took 48 min for 2055 tests, + # 2026-10-29; 14 jobs took 4 min). Serial when GNU parallel is absent. - name: Run test suite - run: bats tests/ + run: | + jobs=1 + if command -v parallel >/dev/null 2>&1; then + jobs="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 2)" + fi + echo "bats jobs: $jobs" + if (( jobs > 1 )); then + bats --jobs "$jobs" tests/ + else + bats tests/ + fi shellcheck: needs: changes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48a62977..e55a85eb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ most things can ship quickly if they fit the design. 1. **Fork** the repo and clone locally 2. **Branch** off `main`: `git checkout -b your-branch-name` 3. **Make changes** — see "What kinds of contributions are welcome" below -4. **Run tests**: `bats tests/` (install via `brew install bats-core` if needed) +4. **Run tests**: `bats --jobs 8 tests/` (install via `brew install bats-core parallel` if needed; plain `bats tests/` runs the same suite serially, about 10x slower) 5. **Open a PR** against `main` with a description of the change ## What kinds of contributions are welcome @@ -89,10 +89,15 @@ tests/ bats-core test suite The bats test suite is the primary safety net: ```bash -bats tests/ # run all +bats --jobs 8 tests/ # run all, 8 at a time (needs GNU parallel) +bats tests/ # run all, one at a time bats tests/canonicalize.bats # run one file ``` +Tests run side by side under `--jobs`, so a test keeps its state in its own +temp dirs (`fi_setup_tmp`, `$BATS_TEST_TMPDIR`) and never kills processes by a +pattern another test could also match. + When adding code, add tests. Existing files in `tests/` are good reference — they cover lib functions, CLI subcommands, and hooks. diff --git a/docs/found-issues.md b/docs/found-issues.md index e0034679..cb0665a0 100644 --- a/docs/found-issues.md +++ b/docs/found-issues.md @@ -60,3 +60,4 @@ Maintained automatically by Claude. See /dev/null || true; fi_teardown_tmp; } +teardown() { pkill -f "^sleep (${NAP_ENGINE}|${NAP_FOREIGN})\$" 2>/dev/null || true; fi_teardown_tmp; } # The engine child's group, once it runs: claim's base test run (3.2.1) has # its own, already finished, child group first. @@ -77,7 +80,7 @@ wait_engine() { } @test "autofix cancel: an A run and its engine child are stopped" { - export FI_STANDIN_SLEEP=4713 + export FI_STANDIN_SLEEP="$NAP_ENGINE" "$FI_BIN" autofix run "$ID" --engine claude >/dev/null 2>&1 & rpid=$! wait_engine @@ -101,7 +104,7 @@ wait_engine() { } @test "autofix cancel: a pid that is not an autofix run is never signalled" { - sleep 4714 & spid=$! + sleep "$NAP_FOREIGN" & spid=$! wt="$("$FI_BIN" autofix claim "$ID")" printf 'pid=%s\n' "$spid" >>"$ST/running/$ID" run "$FI_BIN" autofix cancel "$ID" @@ -111,7 +114,7 @@ wait_engine() { } @test "autofix cancel: a stale running item never signals the run that moved on" { - export FI_STANDIN_SLEEP=4713 + export FI_STANDIN_SLEEP="$NAP_ENGINE" "$FI_BIN" autofix run "$ID" --engine claude >/dev/null 2>&1 & rpid=$! wait_engine diff --git a/tests/cli-sync-default-branch.bats b/tests/cli-sync-default-branch.bats index 7b576a8f..bcab1e81 100644 --- a/tests/cli-sync-default-branch.bats +++ b/tests/cli-sync-default-branch.bats @@ -5,12 +5,14 @@ setup() { fi_setup_tmp fi_init_git fi_use_gh_shim + # The default-branch cache lives under XDG_CACHE_HOME: give each test its + # own, inside $TMP. A shared ~/.cache plus a teardown that emptied it let + # one test delete another's cache when they ran side by side (bats --jobs). + export XDG_CACHE_HOME="$TMP/xdg-cache" } teardown() { fi_teardown_tmp - # Clean up any cache files this test created (they live globally in ~/.cache) - rm -rf ~/.cache/found-issues/default-branch-* 2>/dev/null || true } @test "sync: uses gh repo view fallback when origin/HEAD is unset and gh available" { diff --git a/tests/fix-batch8-long-root.bats b/tests/fix-batch8-long-root.bats index 47b65fc9..7a0afaab 100644 --- a/tests/fix-batch8-long-root.bats +++ b/tests/fix-batch8-long-root.bats @@ -10,7 +10,7 @@ setup() { fi_setup_tmp TMP_TOP="$TMP" } -teardown() { pkill -f 'sleep 471[34]' 2>/dev/null || true; TMP="$TMP_TOP"; fi_teardown_tmp; } +teardown() { TMP="$TMP_TOP"; fi_teardown_tmp; } long_dir() { printf "$1%.0s" $(seq 1 100); } diff --git a/tests/helpers.bash b/tests/helpers.bash index c222cfd1..adce7e21 100644 --- a/tests/helpers.bash +++ b/tests/helpers.bash @@ -22,7 +22,10 @@ export FOUND_ISSUES_SEGMENT_AUTOSYNC=off # Segment renders write a cache (lib/segment-cache.sh) under the cache dir; # keep it out of the real ~/.cache. Tests that inspect it set their own. -export FOUND_ISSUES_CACHE_DIR="${BATS_RUN_TMPDIR:-${TMPDIR:-/tmp}}/fi-test-cache" +# One per test (BATS_TEST_TMPDIR exists when this file loads): auto-fix keeps +# its run logs there (FI_AF_RUNS), and tests running in parallel (bats +# --jobs) shared and mixed one another's logs. +export FOUND_ISSUES_CACHE_DIR="${BATS_TEST_TMPDIR:-${BATS_RUN_TMPDIR:-${TMPDIR:-/tmp}}}/fi-test-cache" # A suite started inside an auto-fix run inherits FOUND_ISSUES_AUTOFIX_CHILD=1, # which makes session-start take its headless path and fails every test of From 2aecd16ffa0c2098502205d0444c13ce26f8121c Mon Sep 17 00:00:00 2001 From: Diogo Silva Sena <227921880+DougBTW@users.noreply.github.com> Date: Thu, 29 Oct 2026 23:33:11 -0400 Subject: [PATCH 2/2] docs(found-issues): annotate tests/autofix-sweep.bats with PR #296 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MVx3HWniZnrXYfPiCo5Lrs --- docs/found-issues.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/found-issues.md b/docs/found-issues.md index cb0665a0..0b3b7742 100644 --- a/docs/found-issues.md +++ b/docs/found-issues.md @@ -59,5 +59,5 @@ Maintained automatically by Claude. See