Part of #761. Follow-up to #764 (PR #770), which removed most run_test subshells; these are the remaining ones.
Problem
Per test, bashunit::runner::run_test and the surrounding loop still fork:
- Two
normalize_test_function_name captures: label="$(bashunit::helper::normalize_test_function_name ...)" (src/runner.sh:1183) runs on every test; failure_label="$(...)" (src/runner.sh:1191) additionally on hook failures.
- Clock captures:
start_time=$(bashunit::clock::now) (src/runner.sh:1095) and end_time=$(...) (src/runner.sh:1125) whenever durations are measured (the default: needs_test_duration is true because show_execution_time defaults on). On Bash 5 the shell impl (src/clock.sh:127-141) is pure bash yet still pays the capture fork, and it nests a second $(bashunit::clock::shell_time) inside.
bashunit::parallel::is_enabled recomputed 3x per test iteration (src/runner.sh:782,789,819; also :830,880 per file). Each call (src/parallel.sh:146) re-evaluates env + OS checks that are constant for the whole run.
Task — checklist, one commit each
Tests first (TDD)
Behavior must not change. For the clock slot, add unit tests around now_to_slot for the shell and date-seconds branches (mock EPOCHREALTIME presence). For each return-slot helper add the dynamic-scoping regression test per .claude/rules/bash-style.md (bug precedent PR #672).
Constraints
- Bash 3.0+ only;
EPOCHREALTIME fast path is feature-gated exactly as clock::_choose_impl already does.
- Console output byte-identical; durations still reported when enabled.
Acceptance criteria
Line references valid at commit 1ae3e82.
Part of #761. Follow-up to #764 (PR #770), which removed most
run_testsubshells; these are the remaining ones.Problem
Per test,
bashunit::runner::run_testand the surrounding loop still fork:normalize_test_function_namecaptures:label="$(bashunit::helper::normalize_test_function_name ...)"(src/runner.sh:1183) runs on every test;failure_label="$(...)"(src/runner.sh:1191) additionally on hook failures.start_time=$(bashunit::clock::now)(src/runner.sh:1095) andend_time=$(...)(src/runner.sh:1125) whenever durations are measured (the default:needs_test_durationis true becauseshow_execution_timedefaults on). On Bash 5 theshellimpl (src/clock.sh:127-141) is pure bash yet still pays the capture fork, and it nests a second$(bashunit::clock::shell_time)inside.bashunit::parallel::is_enabledrecomputed 3x per test iteration (src/runner.sh:782,789,819; also:830,880per file). Each call (src/parallel.sh:146) re-evaluates env + OS checks that are constant for the whole run.Task — checklist, one commit each
normalize_test_function_name(src/helpers.sh:40; slot e.g._BASHUNIT_NORMALIZED_FN_OUT) and convertsrc/runner.sh:1183,1191. Keep the echoing function for other callers.bashunit::clock::now_to_slotwriting_BASHUNIT_CLOCK_NOW_OUT: theshellbranch computes fork-free fromEPOCHREALTIME(fold inshell_time);date-secondscomputes with one fork instead of two; interpreter/datebranches may keep an internal$(...). Convert bothrun_testcall sites. This must not change_choose_implselection or the perf(clock): skip per-test clock reads when the clock impl forks an interpreter #765 auto-skip behavior._BASHUNIT_PARALLEL_ENABLEDset where env layer finishes, after--parallel/--no-parallelare known) and makebashunit::parallel::is_enableda pure-global read. Beware: keep theinternal_logout of the hot reader or early-return it.grep -q "^# bashunit: no-parallel-tests"per-file probe (src/runner.sh:769) into the existing per-file awk scan (build_provider_map,src/helpers.sh:327) so one pass answers both questions.Tests first (TDD)
Behavior must not change. For the clock slot, add unit tests around
now_to_slotfor theshellanddate-secondsbranches (mockEPOCHREALTIMEpresence). For each return-slot helper add the dynamic-scoping regression test per.claude/rules/bash-style.md(bug precedent PR #672).Constraints
EPOCHREALTIMEfast path is feature-gated exactly asclock::_choose_implalready does.Acceptance criteria
./bashunit tests/and./bashunit --parallel tests/pass after every commit.make sa,make lintpass;shfmt -w .produces no diff.Line references valid at commit 1ae3e82.