Summary
Printing "Running N tests" sources the whole suite a second time, and runs every data provider a second time, before the run starts.
Measured
The counting pass over this repo's 240 test files costs 671 ms on macOS arm64, bash 3.2.57: 487 ms of nested subshells and 184 ms of re-sourcing. That is before any data provider executes, and 44 provider annotations in the suite do execute inside it.
Current behavior
bashunit::helper::find_total_tests, src/helper/discovery.sh:225-275, called from src/console/header.sh:36-46:
file_count=$( (
source "$file"
all_fn_names=$(compgen -A function)
filtered_functions=$(bashunit::helper::get_functions_to_run "test" "$filter" "$all_fn_names")
...
done <<<"$(bashunit::helper::execute_function_if_exists "$_BASHUNIT_PROVIDER_FN_OUT")"
) )
A double subshell per file, which sources the file and runs its providers. The runner then sources everything again (src/runner/discovery.sh:96, :143, :146, :148) and runs the providers again (src/runner/exec.sh:184-188).
It is skipped only under --parallel --simple (src/console/header.sh:38-40).
Expected behavior
Count provider-free files statically. bashunit::helper::build_provider_map (src/helper/provider.sh:29-100) already walks every line of the file with awk, and is memoized by path (:44-46) and deliberately pre-warmed in the parent so its cache survives into the runner (see the comment at src/console/header.sh:42-44). Extending that same awk pass to also emit top-level test_* names adds no fork. The count is then the names surviving get_functions_to_run (src/helper/discovery.sh:132-159).
Files whose provider map is non-empty keep the sourcing fallback, since a provider's row count is only knowable by running it. In this suite that leaves roughly 85% of files counted without a fork.
The risk is a test function a static scan cannot see, defined through eval, a loop, or a nested source. Then the header would disagree with the run. The gate for this change should be a differential over every file in tests/, plus several --filter and --exclude-filter values, requiring the old and new counts to be identical.
Whatever lands must preserve exactly what the count includes today. It ignores --tag, because tags are applied later in the runner (src/runner/discovery.sh:153-164).
Summary
Printing "Running N tests" sources the whole suite a second time, and runs every data provider a second time, before the run starts.
Measured
The counting pass over this repo's 240 test files costs 671 ms on macOS arm64, bash 3.2.57: 487 ms of nested subshells and 184 ms of re-sourcing. That is before any data provider executes, and 44 provider annotations in the suite do execute inside it.
Current behavior
bashunit::helper::find_total_tests,src/helper/discovery.sh:225-275, called fromsrc/console/header.sh:36-46:A double subshell per file, which sources the file and runs its providers. The runner then sources everything again (
src/runner/discovery.sh:96,:143,:146,:148) and runs the providers again (src/runner/exec.sh:184-188).It is skipped only under
--parallel --simple(src/console/header.sh:38-40).Expected behavior
Count provider-free files statically.
bashunit::helper::build_provider_map(src/helper/provider.sh:29-100) already walks every line of the file with awk, and is memoized by path (:44-46) and deliberately pre-warmed in the parent so its cache survives into the runner (see the comment atsrc/console/header.sh:42-44). Extending that same awk pass to also emit top-leveltest_*names adds no fork. The count is then the names survivingget_functions_to_run(src/helper/discovery.sh:132-159).Files whose provider map is non-empty keep the sourcing fallback, since a provider's row count is only knowable by running it. In this suite that leaves roughly 85% of files counted without a fork.
The risk is a test function a static scan cannot see, defined through
eval, a loop, or a nestedsource. Then the header would disagree with the run. The gate for this change should be a differential over every file intests/, plus several--filterand--exclude-filtervalues, requiring the old and new counts to be identical.Whatever lands must preserve exactly what the count includes today. It ignores
--tag, because tags are applied later in the runner (src/runner/discovery.sh:153-164).