Skip to content

perf(runner): remaining run_test hot-path forks (labels, clock capture, parallel::is_enabled) #774

Description

@Chemaclass

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:

  1. 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.
  2. 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.
  3. 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

  • Add a return-slot variant of normalize_test_function_name (src/helpers.sh:40; slot e.g. _BASHUNIT_NORMALIZED_FN_OUT) and convert src/runner.sh:1183,1191. Keep the echoing function for other callers.
  • Add bashunit::clock::now_to_slot writing _BASHUNIT_CLOCK_NOW_OUT: the shell branch computes fork-free from EPOCHREALTIME (fold in shell_time); date-seconds computes with one fork instead of two; interpreter/date branches may keep an internal $(...). Convert both run_test call sites. This must not change _choose_impl selection or the perf(clock): skip per-test clock reads when the clock impl forks an interpreter #765 auto-skip behavior.
  • Resolve parallel mode once after arg parsing (e.g. _BASHUNIT_PARALLEL_ENABLED set where env layer finishes, after --parallel/--no-parallel are known) and make bashunit::parallel::is_enabled a pure-global read. Beware: keep the internal_log out of the hot reader or early-return it.
  • Optional, only if trivial: fold the 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_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

  • ./bashunit tests/ and ./bashunit --parallel tests/ pass after every commit.
  • make sa, make lint pass; shfmt -w . produces no diff.
  • Report before/after 100-noop timing in the PR (current baseline: ~0.68s / 100 trivial tests on macOS bash 3.2).

Line references valid at commit 1ae3e82.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions