Skip to content

Make the test suite runnable under bun test --parallel - #915

Open
Gustav-Simonsson wants to merge 5 commits into
corbitsdev:mainfrom
Gustav-Simonsson:faster_tests
Open

Make the test suite runnable under bun test --parallel#915
Gustav-Simonsson wants to merge 5 commits into
corbitsdev:mainfrom
Gustav-Simonsson:faster_tests

Conversation

@Gustav-Simonsson

Copy link
Copy Markdown

Summary

bun test --parallel on Bun 1.4.x can livelock (upstream oven-sh/bun#36235): a
worker spins at 100% CPU with no output, and since bun test has no run-level
timeout, the hang is permanent. This PR adds a safe local parallel mode and removes
unnecessary fixed sleeps from the tests.

  • New bun run test:parallel [N] (default 4 workers) runs the same seeded suite as
    bun run test with --parallel=N, under a watchdog (scripts/test-parallel.ts).
    If the suite produces no output for 90 s, the watchdog kills its process group and
    restarts it (3 attempts total). A run that exits on its own is never retried, so
    real test failures still fail the gate.
  • Tests now wait for the condition they depend on instead of sleeping for a fixed
    time, which makes them stable under parallel load and removes dead time.
  • Three test-only timeouts (tool-watchdog salvage grace, teardown deadline,
    agents-panel linger) can now be shortened via optional config values that
    production code does not set.

Full-suite wall time: ~21 s with test:parallel vs ~75 s sequential at the
base commit (≈3.5×). Sequential runs are also ~1.3× faster from the removed waits.
CI is unchanged (sharded sequential test:paths).

Unchanged production behavior

  • All production changes are new optional parameters/config fields whose defaults
    are the previous hard-coded values, and no production call site sets them.
  • Test changes only replace fixed sleeps with condition waits. No assertions were
    added, removed, or modified. The only new tests (+6) cover the watchdog wrapper.

Verification

  • bun run typecheck and bun run lint pass at HEAD.
  • Full-suite results are identical before and after, apart from the 6 new tests:
    base 6765 pass / 1 skip / 4 fail, HEAD 6771 pass / 1 skip / 4 fail. The 4 failures
    also occur on the unmodified tree in this environment (permission/git sandbox
    issues) and are unrelated to this PR.
  • The --parallel livelock reproduces on the base commit, and the watchdog rescued a
    live occurrence during verification (killed the stalled run after 90 s; the retry
    completed in 21.2 s).
  • Caveat: under heavy parallel load, 11 tests in src/agent/exa-web-fetch-alias.test.ts
    (untouched by this PR) hit Bun's 5 s per-test timeout in one run. They pass
    sequentially and in isolated parallel runs.

Test plan

  • bun run test:parallel — local full-suite run with the stall watchdog.
  • bun run test — unchanged sequential mode.
  • CI: unchanged (test:paths).

The parallel test matrix exposed three tests that raced wall-clock time.
Approval-log tests slept a fixed 10 ms before reading a JSONL log that
is appended fire-and-forget, so a slightly loaded event loop dropped the
last record; the transcript anchor test slept a fixed 250 ms for the
async tree-sitter highlighter to paint, which the parallel load exceeds;
and the stall-recovery tests measured a 30 ms stall window against
Date.now, so a load gap between awaited decides tripped a spurious stall
nudge. Await the actual condition instead of sleeping: the approval log
now exposes a flush that resolves its append tail, the transcript test
polls for the newest painted row, and the stall tests inject a frozen
clock, which the constructor already accepted.
bun test --parallel on Bun 1.4.x intermittently livelocks: a worker
spins at 100% CPU holding a zombie git child while the main process
idles with no output, and bun test has no run-level timeout, so a
stalled run hangs forever (upstream oven-sh/bun bug, still open on
1.4.2, reproduced locally at ~50% of runs with 4 workers). A healthy
run never exceeds 0.64s of output silence, so the wrapper runs the
seeded suite in its own process group, kills the whole group after 90s
of silence, and retries up to 3 times. A child that exits on its own,
pass or fail, is never retried, so real failures still fail the gate.
CI keeps sharded sequential runs and is unaffected.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant