Skip to content

refactor(coverage): split src/coverage.sh into a src/coverage/ module #925

Description

@Chemaclass

Summary

src/coverage.sh is 2548 lines and 59 functions — the largest file in src/, bigger
than src/runner.sh was. Apply the same module split: src/coverage/ behind a thin
aggregator.

Blocked by #923 (path-keyed embed markers). Do after #924 so the
runner split sets the convention and this one just follows it.

This is a pure move

Namespace stays bashunit::coverage::*. No renames, no call-site changes,
no behavior changes. bashunit:76 keeps its single
source "$BASHUNIT_ROOT_DIR/src/coverage.sh"; that file becomes an aggregator
holding only source lines and comments.

Step 1: map the responsibilities first

Unlike #924, the target layout is not pre-decided here — nobody has mapped
this file yet. Produce the grouping before moving a single line:

  • one-line summary per function
  • internal call graph (which bashunit::coverage::* functions call which)
  • every _BASHUNIT_* global: which function assigns it, which read it, and whether
    anything outside src/coverage.sh touches it
  • proposed cohesive groups with an acyclic layering

Post that map on this issue for review, then split. Existing hints to fold in:
adrs/adr-007-branch-coverage-mvp.md and adrs/adr-009-coverage-tracing-engine.md
describe the intended internal boundaries, and the 9 existing
tests/unit/coverage_*_test.sh files (core, engine, branches, helpers, percent,
reporting, executable, subshell, parallel_aggregation) are a strong prior on what the
natural seams already are.

Known constraints specific to this file

  • .editorconfig:35 has a per-file rule, [src/coverage.sh] max_line_length = unset. Splitting leaves the new files under the global [**.sh]
    max_line_length = 120. The longest current line is 370 chars, so either widen the
    glob to cover the directory, or reflow the offending lines. Decide deliberately and
    say which in the PR — silently inheriting 120 will fail make lint.
  • src/coverage.sh:224 reads _BASHUNIT_RUNNER_RESULT_ORDINAL, a runner global.
    That cross-module read stays as-is; do not "fix" it in this PR.
  • Intentional dynamic-scope mutation. _branch_push_if and friends deliberately
    mutate caller locals (if_decision_line, if_arms, if_depth, if_arm_start),
    documented inline at src/coverage.sh:818-821 and called out in
    .claude/rules/bash-style.md. Those helpers and their callers must land in the
    same file, and the inline justification comment must travel with them.
  • DEBUG-trap code paths only run under --coverage, which is a nightly
    non-gating workflow (.github/workflows/coverage.yml). The default suite will not
    catch a regression here — run ./bashunit --coverage --parallel tests/ explicitly
    before opening the PR.
  • src/coverage.sh:173 carries a comment about a name kept "from the trap-only
    era" that references runner.sh. Update the reference if the runner path changed
    in refactor(runner): split src/runner.sh into a src/runner/ module #924.

Tests: keep them flat

make test globs one level deep only. Do not create tests/unit/coverage/.
The existing 9 flat coverage_*_test.sh files already mirror a module split — keep
that shape and rename only where a file's scope genuinely moves.

Acceptance criteria

  • Responsibility map posted on this issue and agreed before any code moves
  • git diff shows only relocations — no renamed functions, no changed logic
  • src/coverage.sh contains only source lines and comments
  • Every new file's first line is #!/usr/bin/env bash
  • .editorconfig line-length decision made explicitly and stated in the PR
  • ./bashunit tests/ green
  • ./bashunit --parallel tests/ green
  • ./bashunit --coverage --parallel tests/ green
  • make sa && make lint green
  • bash build.sh bin -v prints ✅ Build verified ✅
  • Fork-budget acceptance tests green
  • Bash 3.0+ compatible
  • No CHANGELOG entry — internal, no user-visible behavior change
  • .claude/rules/architecture-map.md coverage row updated to the module table

Do not

  • Do not start moving code before the responsibility map is reviewed
  • Do not rename any function or change any behavior
  • Do not create tests/unit/coverage/
  • Do not run shfmt -w; make lint is the format gate

Metadata

Metadata

Assignees

Labels

refactoringRefactoring or cleaning related

Type

No type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions