| Q | A |
|---|
| OS | macOS 26.5.2 (arm64) |
| Shell & version | bash 3.2.57 |
| bashunit version | 0.50.0 (main @ 40560c2) |
Summary
Under --parallel, a malformed # @timeout or # @retry does not fail the run when another file passes. The abort happens inside the file's worker, the parent never learns, and the run exits 0 with "All tests passed". The same input exits 1 sequentially.
Found while fixing #1329, kept separate: that issue is the teardown the abort skipped, this is the exit code the abort never reaches.
Current behavior
The worker prints the annotation error to its captured stderr, which the parent replays, and stops running that file. Nothing else happens. The summary counts the other files' tests, finds no failure, and the run succeeds.
A single-file run does exit 1, but not for this reason: no test ran, so "No tests found" fails the run on its own. Add one passing file and the error goes silent.
How to reproduce
annot_test.sh:
# @timeout abcfunctiontest_bad_annotation() { assert_true true; }ok_test.sh:
functiontest_fine() { assert_true true; }$ ./bashunit --parallel .
✓ Passed: Fine
Stderr from ./annot_test.sh
|Error: @timeout 'abc' above test_bad_annotation in ./annot_test.sh is not a non-negative integer.
Tests: 1 passed, 1 total
All tests passed
$ echo $?
0
Sequentially the same directory exits 1.
Expected behavior
A malformed annotation fails the run under --parallel as it does sequentially, whatever else is in the selection. The error message is already right; only the exit code is wrong.
Where it happens
src/runner/discovery.sh:252: the worker drops call_test_functions's status. Propagating it as the worker's exit status does not help, because the parent's wait ignores itsrc/runner/result.sh:82: publish_file_hook_failure is the channel a worker already uses to publish a file-level failure into the aggregate. A malformed annotation is not a hook failure, so it needs its own shape or a shared onesrc/runner/discovery.sh:281: the sequential path exits with the file's status, which is where the difference comes from
Acceptance criteria
Notes for the implementer
- The worker cannot exit non-zero to say this. The parent's
wait at src/runner/discovery.sh:290 discards worker statuses, and wait_for_job_slot breaks its poll loop on the first non-zero wait -n, so a non-zero worker would change scheduling instead of the exit code. - Decide whether this counts as a failed test or only as a non-zero exit. The console summary and the json, junit, html and markdown reports all read the same aggregate, so the answer has to be the same in all five.
- Bash 3.0+ only. See
.claude/rules/bash-style.md. make sa, make lint, ./bashunit tests/, ./bashunit --parallel tests/.
Summary
Under
--parallel, a malformed# @timeoutor# @retrydoes not fail the run when another file passes. The abort happens inside the file's worker, the parent never learns, and the run exits 0 with "All tests passed". The same input exits 1 sequentially.Found while fixing #1329, kept separate: that issue is the teardown the abort skipped, this is the exit code the abort never reaches.
Current behavior
The worker prints the annotation error to its captured stderr, which the parent replays, and stops running that file. Nothing else happens. The summary counts the other files' tests, finds no failure, and the run succeeds.
A single-file run does exit 1, but not for this reason: no test ran, so "No tests found" fails the run on its own. Add one passing file and the error goes silent.
How to reproduce
annot_test.sh:ok_test.sh:Sequentially the same directory exits 1.
Expected behavior
A malformed annotation fails the run under
--parallelas it does sequentially, whatever else is in the selection. The error message is already right; only the exit code is wrong.Where it happens
src/runner/discovery.sh:252: the worker dropscall_test_functions's status. Propagating it as the worker's exit status does not help, because the parent'swaitignores itsrc/runner/result.sh:82:publish_file_hook_failureis the channel a worker already uses to publish a file-level failure into the aggregate. A malformed annotation is not a hook failure, so it needs its own shape or a shared onesrc/runner/discovery.sh:281: the sequential path exits with the file's status, which is where the difference comes fromAcceptance criteria
@timeoutnext to a passing file exits non-zero under--parallel.CHANGELOG.mdgets aFixedentry.Notes for the implementer
waitatsrc/runner/discovery.sh:290discards worker statuses, andwait_for_job_slotbreaks its poll loop on the first non-zerowait -n, so a non-zero worker would change scheduling instead of the exit code..claude/rules/bash-style.md.make sa,make lint,./bashunit tests/,./bashunit --parallel tests/.