Problem
A source file that no test ever executed is absent from the coverage report, so it cannot lower the percentage. Coverage is measured over the files that ran, not over the files the user asked about.
should_track (src/coverage/paths.sh:29) only runs from inside the capture path, so a file enters files.dat the first time one of its lines executes. get_tracked_files then feeds every report section. A 0% file is structurally impossible to print.
Reproduced on Bash 3.2 arm64, macOS:
./bashunit --coverage --coverage-paths src --coverage-report /tmp/cov.info \
tests/unit/assert/basic_test.sh
- files in the LCOV report: 11
src/*.sh files tracked by git: 121- reported denominator: 2,200 executable lines
- real
src/ executable lines: 9,285
The reported total was 171/2200 (7%). Measured against the source the user pointed at, it is 171/9285 (1%). The nightly badge (.github/workflows/coverage.yml) inherits this: any src/ file the unit suite never reaches is invisible rather than red.
docs/coverage.md:46 describes the filtering step as "only files matching your coverage paths (and not excluded) are tracked", which reads as path-based selection. The implementation is execution-based.
Proposal
Seed the tracked-file list from the coverage paths at init, with zero hits.
- In
bashunit::coverage::init, enumerate the files under BASHUNIT_COVERAGE_PATHS once, apply BASHUNIT_COVERAGE_EXCLUDE, and write them into files.dat. - The capture path keeps adding files as it sees them, so nothing about tracking changes for executed code.
get_tracked_files already sorts and dedupes. - Cost is one
find at startup, which is bounded by the project, not by the test count. - Fix
docs/coverage.md so the described behaviour matches: with this change, "files matching your coverage paths" becomes true.
This raises report input by roughly 11x for this repo, so land the report-phase performance work first. Otherwise the honest number arrives with a report phase measured at 16.4s for 121 files.
Where to change
src/coverage/config.sh:61-112initsrc/coverage/paths.sh:22-27get_tracked_filesdocs/coverage.md:45-55 the pipeline description.github/workflows/coverage.yml no code change needed, but expect the badge to drop, which is the point
Acceptance criteria
Repo checklist (agent)
- TDD: RED then GREEN then REFACTOR. The RED test is a fixture project with one executed file and one never-executed file, asserting the second appears at 0%.
- Bash 3.0+ only: no
printf -v, no += append, no declare -A, no [[ ]], no ${var,,}, no &>>, no ${arr[-1]}. Expanding a possibly-empty array under set -u needs ${arr[@]+"${arr[@]}"}. - Fixtures under
tests/acceptance/fixtures/ must not end in *test.sh. - Gates:
make sa, make lint, ./bashunit tests/, ./bashunit --parallel tests/. Never run shfmt -w. - CHANGELOG.md: one line under
## Unreleased. This changes reported numbers for every user, so say so plainly. - One issue = one PR.
Problem
A source file that no test ever executed is absent from the coverage report, so it cannot lower the percentage. Coverage is measured over the files that ran, not over the files the user asked about.
should_track(src/coverage/paths.sh:29) only runs from inside the capture path, so a file entersfiles.datthe first time one of its lines executes.get_tracked_filesthen feeds every report section. A 0% file is structurally impossible to print.Reproduced on Bash 3.2 arm64, macOS:
src/*.shfiles tracked by git: 121src/executable lines: 9,285The reported total was
171/2200 (7%). Measured against the source the user pointed at, it is171/9285 (1%). The nightly badge (.github/workflows/coverage.yml) inherits this: anysrc/file the unit suite never reaches is invisible rather than red.docs/coverage.md:46describes the filtering step as "only files matching your coverage paths (and not excluded) are tracked", which reads as path-based selection. The implementation is execution-based.Proposal
Seed the tracked-file list from the coverage paths at init, with zero hits.
bashunit::coverage::init, enumerate the files underBASHUNIT_COVERAGE_PATHSonce, applyBASHUNIT_COVERAGE_EXCLUDE, and write them intofiles.dat.get_tracked_filesalready sorts and dedupes.findat startup, which is bounded by the project, not by the test count.docs/coverage.mdso the described behaviour matches: with this change, "files matching your coverage paths" becomes true.This raises report input by roughly 11x for this repo, so land the report-phase performance work first. Otherwise the honest number arrives with a report phase measured at 16.4s for 121 files.
Where to change
src/coverage/config.sh:61-112initsrc/coverage/paths.sh:22-27get_tracked_filesdocs/coverage.md:45-55the pipeline description.github/workflows/coverage.ymlno code change needed, but expect the badge to drop, which is the pointAcceptance criteria
0/N (0%)DArecords at0,LH:0, and in the HTML reportBASHUNIT_COVERAGE_PATHSBASHUNIT_COVERAGE_EXCLUDEstill removes files from the seeded set, tested with the defaulttests/*,vendor/*,*_test.sh,*Test.sh--coverage-mingates on the new, larger denominatorBASHUNIT_COVERAGE_PATHSkeeps its current behaviour rather than seeding the whole repoauto_discover_paths(src/coverage/config.sh:26) still behaves as documented--parallel, where the seed happens once in the parent, not per workertrapandxtrace)docs/coverage.mdupdatedRepo checklist (agent)
printf -v, no+=append, nodeclare -A, no[[ ]], no${var,,}, no&>>, no${arr[-1]}. Expanding a possibly-empty array underset -uneeds${arr[@]+"${arr[@]}"}.tests/acceptance/fixtures/must not end in*test.sh.make sa,make lint,./bashunit tests/,./bashunit --parallel tests/. Never runshfmt -w.## Unreleased. This changes reported numbers for every user, so say so plainly.