π― Goal
Surface per-line execution hit counts (gcov-style: how many times each line ran) in coverage reports, instead of only boolean covered/not-covered. The raw data to do this largely exists already β this is mostly a reporting change.
π Where
- Raw hit records are produced by
bashunit::coverage::record_line into _BASHUNIT_COVERAGE_DATA_FILE / _BASHUNIT_COVERAGE_TEST_HITS_FILE (src/coverage.sh:294-301, flush at :336-339).
- Report readers collapse to a covered set today:
src/coverage.sh:575 (grep β¦ | sort -u), :688, :743.
- Report writers:
src/reports.sh (HTML/JUnit/etc.) and the console coverage summary.
β οΈ Interaction with the dedup issue (read first)
The per-line dedup issue proposes collapsing repeated hits at source (one record per file:line). If that lands, hit counts are lost at the source. Two options β the implementer must pick and document:
- (A) Count in memory: keep dedup, but maintain a small in-memory
file:line β count tally in record_line and flush counts (not repeated records). Preserves counts and the I/O win. Preferred.
- (B) Order the work: land hit-counts first (parse repeated raw records β counts at report time), then make dedup opt-out when counts are requested.
Coordinate so the two issues don't contradict. (A) is the clean end state.
π§ Approach (assuming option A)
- In
record_line, when a (file,line) is first seen, record it; on repeat, increment an in-memory counter keyed file:line rather than appending a duplicate record.
- On flush, write
file:line:count (extend the record format; keep backward-compatible parsing or bump the data-file format guarded by a version marker).
- Report layer: aggregate counts across the run (and across
--parallel per-$$ files at merge), expose per-line counts in HTML (heat/number column) and optionally a --coverage-mode flag to show counts in the console.
- Keep the existing boolean summary (coverage %) unchanged β counts are additive detail.
β
Acceptance criteria
- Reports can show, per line: run count (0 = uncovered).
--parallel merge sums counts across worker files correctly.
- Coverage percentage and the covered-line set are unchanged vs. current output.
- Data-file format change (if any) is versioned and parsed safely.
π§ͺ TDD / measurement
- RED: fixture with a line executed a known number of times (e.g. a 5-iteration loop) β assert the report attributes count
5 to that line; a once-run line β 1; an unrun line β 0.
--parallel: two workers each hitting the same shared-source line β summed count.
- Gate:
./bashunit tests/, ./bashunit --parallel --simple --strict tests/, make sa, make lint. Regenerate coverage HTML/report snapshots if any.
βοΈ Constraints
- Bash 3.0+ β in-memory count tally via the existing string-cache pattern (bounded by distinct lines); no
${var//} over big strings (perf-fork-budget.md).
- Don't regress the dedup I/O win β option (A) keeps both.
- If it materially adds
record_line overhead, gate counts behind a flag (e.g. BASHUNIT_COVERAGE_COUNTS=true) so default coverage stays lean.
π Impact
Turns coverage from "covered?" into "how hot?" β useful for spotting under-exercised branches and dead-ish code. Depends on / must be sequenced with the per-line dedup issue.
π― Goal
Surface per-line execution hit counts (gcov-style: how many times each line ran) in coverage reports, instead of only boolean covered/not-covered. The raw data to do this largely exists already β this is mostly a reporting change.
π Where
bashunit::coverage::record_lineinto_BASHUNIT_COVERAGE_DATA_FILE/_BASHUNIT_COVERAGE_TEST_HITS_FILE(src/coverage.sh:294-301, flush at:336-339).src/coverage.sh:575(grep β¦ | sort -u),:688,:743.src/reports.sh(HTML/JUnit/etc.) and the console coverage summary.The per-line dedup issue proposes collapsing repeated hits at source (one record per file:line). If that lands, hit counts are lost at the source. Two options β the implementer must pick and document:
file:line β counttally inrecord_lineand flush counts (not repeated records). Preserves counts and the I/O win. Preferred.Coordinate so the two issues don't contradict. (A) is the clean end state.
π§ Approach (assuming option A)
record_line, when a (file,line) is first seen, record it; on repeat, increment an in-memory counter keyedfile:linerather than appending a duplicate record.file:line:count(extend the record format; keep backward-compatible parsing or bump the data-file format guarded by a version marker).--parallelper-$$files at merge), expose per-line counts in HTML (heat/number column) and optionally a--coverage-mode flag to show counts in the console.β Acceptance criteria
--parallelmerge sums counts across worker files correctly.π§ͺ TDD / measurement
5to that line; a once-run line β1; an unrun line β0.--parallel: two workers each hitting the same shared-source line β summed count../bashunit tests/,./bashunit --parallel --simple --strict tests/,make sa,make lint. Regenerate coverage HTML/report snapshots if any.βοΈ Constraints
${var//}over big strings (perf-fork-budget.md).record_lineoverhead, gate counts behind a flag (e.g.BASHUNIT_COVERAGE_COUNTS=true) so default coverage stays lean.π Impact
Turns coverage from "covered?" into "how hot?" β useful for spotting under-exercised branches and dead-ish code. Depends on / must be sequenced with the per-line dedup issue.