Summary
Under --parallel, per-test results are bucketed into a directory named by the test file's
basename. Two test files with the same basename in different directories share that bucket,
their per-suite ordinals collide, and one file's results overwrite the other's.
The run stays green and simply reports fewer tests. Nothing errors.
Same class of defect as #923, which fixed exactly this in build.sh — the fix there was to key
on the repo-relative path instead of the basename.
Root cause
src/runner/result.sh:28-32:
local test_suite_base="${test_file##*/}"# basenamelocal test_suite_dir="${TEMP_DIR_PARALLEL_TEST_SUITE}/${test_suite_base%.sh}"
[ -d"$test_suite_dir" ] || mkdir -p "$test_suite_dir"local unique_test_result_file="${test_suite_dir}/${_BASHUNIT_RUNNER_RESULT_ORDINAL}.result"tests/a/foo_test.sh and tests/b/foo_test.sh both resolve to <tmp>/foo_test/. The ordinal
is unique per suite, not globally, so <tmp>/foo_test/1.result is written twice and the
first write is lost.
Reproduce
Measured while mirroring tests/unit/ onto the src/ module layout (#957), which produced
three duplicate basenames (helpers_test.sh, json_test.sh, doubles_test.sh each in two
directories):
| --parallel --simple --strict total |
|---|
| with 3 duplicate basenames | 1536 |
| after renaming them unique | 1588 |
52 tests silently vanished. Reproducible, not flaky — three consecutive runs each.
Sequential mode reports the full count either way, so the two modes disagree and only the
parallel one is wrong.
Minimal case:
mkdir -p t/a t/b
printf'#!/usr/bin/env bash\nfunction test_a() { assert_same 1 1; }\n'> t/a/dup_test.sh
printf'#!/usr/bin/env bash\nfunction test_b() { assert_same 1 1; }\n'> t/b/dup_test.sh
./bashunit t # 2 tests
./bashunit --parallel t # fewerImpact
Affects any user project whose test tree has two same-named files in different directories
— a normal layout when tests mirror a source tree, which is precisely what the framework's own
docs encourage. Results are lost silently, so a failing test can disappear and CI stays green.
Proposal
Key the suite directory on the repo-relative path with separators sanitized, not the
basename — the #923 fix, applied here. bashunit::helper::generate_id already sanitizes a
path into a variable-safe token and could be reused, but note the comment above this code:
it is on the per-test parallel path and deliberately fork-free, so the derivation must stay
pure parameter expansion.
Something of this shape, keeping the existing no-fork property:
local test_suite_base="${test_file#./}"
test_suite_base="${test_suite_base%.sh}"
test_suite_base="${test_suite_base//\//_}"Acceptance criteria
Do not
- Do not fix it by forbidding duplicate basenames; mirroring a source tree makes them normal
- Do not add a
mktemp/basename fork to the per-test path
Summary
Under
--parallel, per-test results are bucketed into a directory named by the test file'sbasename. Two test files with the same basename in different directories share that bucket,
their per-suite ordinals collide, and one file's results overwrite the other's.
The run stays green and simply reports fewer tests. Nothing errors.
Same class of defect as #923, which fixed exactly this in
build.sh— the fix there was to keyon the repo-relative path instead of the basename.
Root cause
src/runner/result.sh:28-32:tests/a/foo_test.shandtests/b/foo_test.shboth resolve to<tmp>/foo_test/. The ordinalis unique per suite, not globally, so
<tmp>/foo_test/1.resultis written twice and thefirst write is lost.
Reproduce
Measured while mirroring
tests/unit/onto thesrc/module layout (#957), which producedthree duplicate basenames (
helpers_test.sh,json_test.sh,doubles_test.sheach in twodirectories):
--parallel --simple --stricttotal52 tests silently vanished. Reproducible, not flaky — three consecutive runs each.
Sequential mode reports the full count either way, so the two modes disagree and only the
parallel one is wrong.
Minimal case:
Impact
Affects any user project whose test tree has two same-named files in different directories
— a normal layout when tests mirror a source tree, which is precisely what the framework's own
docs encourage. Results are lost silently, so a failing test can disappear and CI stays green.
Proposal
Key the suite directory on the repo-relative path with separators sanitized, not the
basename — the #923 fix, applied here.
bashunit::helper::generate_idalready sanitizes apath into a variable-safe token and could be reused, but note the comment above this code:
it is on the per-test parallel path and deliberately fork-free, so the derivation must stay
pure parameter expansion.
Something of this shape, keeping the existing no-fork property:
Acceptance criteria
report their results under
--parallel./bashunit --parallel tests/and./bashunit tests/report the same total on atree containing duplicate basenames
.claude/rules/perf-fork-budget.md); the fork-budgetacceptance tests must stay green
./bashunit tests/·--parallel·--parallel --simple --strict·make sa·make lint·bash build.sh bin -vgreenDo not
mktemp/basenamefork to the per-test path