Skip to content

feat(coverage): --coverage-report-cobertura for GitLab, Azure and Jenkins - #1052

Merged
Chemaclass merged 3 commits into
mainfrom
feat/1017-cobertura-coverage
Aug 11, 2026
Merged

feat(coverage): --coverage-report-cobertura for GitLab, Azure and Jenkins#1052
Chemaclass merged 3 commits into
mainfrom
feat/1017-cobertura-coverage

Conversation

@Chemaclass

Copy link
Copy Markdown
Member

🤔 Background

Related #1017

Coverage output was LCOV-only (plus HTML), which serves Codecov and Coveralls but not the CI platforms with built-in coverage UIs — GitLab MR visualisation, Azure PublishCodeCoverageResults and the Jenkins Coverage plugin all want Cobertura XML.

💡 Changes

  • New --coverage-report-cobertura [file] (default coverage/cobertura.xml) reusing the existing line, branch and stats engines; packages group files by directory, branch lines carry condition-coverage="P% (t/n)", rates derive from the same integer percentage the console prints
  • filename attributes are repo-relative (GitLab silently shows nothing for absolute paths); zero-hit files still appear; coexists with LCOV and HTML in one run
  • Unit tests validate against the vendored coverage-04.dtd via xmllint (skipped cleanly when absent)
  • Unwritable target fails fast via a new creatable-path check that still allows missing ancestors, since the coverage writers mkdir -p their own directories

@Chemaclass Chemaclass added the enhancement New feature or request label Aug 10, 2026
@Chemaclass Chemaclass self-assigned this Aug 10, 2026
…kins

LCOV feeds Codecov and Coveralls; the CI platforms with built-in
coverage UIs want Cobertura XML: GitLab MR visualisation, Azure
PublishCodeCoverageResults and the Jenkins Coverage plugin.

Files group into packages by directory, every executable line carries
its hit count, branch decision lines get branch="true" with a
condition-coverage="P% (t/n)" derived from the engine's per-arm data,
and rates come from the same integer percentage the console prints so
the two cannot disagree. filename attributes stay repo-relative because
GitLab silently renders nothing for absolute paths. Zero-hit files
still appear.

Validated against the vendored coverage-04.dtd in the unit tests. The
path is checked up-front with a creatable-path rule: missing ancestors
are fine (the writer mkdir -p's them, like LCOV), an unwritable first
existing ancestor fails fast (#875).

Closes #1017
The creatable-path rule accepted any writable first existing ancestor,
so under root -- which is how the Bash 3.0 jobs run inside their
container -- '/nonexistent-root-dir/report.xml' was genuinely creatable
and the guard correctly did nothing, failing the test that asserted it
would refuse.

Require that ancestor to be a directory: a regular file in the path
prefix makes mkdir -p impossible for every user, root included, while
root passes a bare -w test on it. The acceptance test blocks with a
regular file instead of relying on permissions, which also sidesteps
Git Bash's faked permission bits on Windows.
The settings-parity contract added in #1063 requires every registered
BASHUNIT_* setting to have a configuration.md section.
@Chemaclass
Chemaclass force-pushed the feat/1017-cobertura-coverage branch from 8677863 to c54cece Compare August 11, 2026 18:39
@Chemaclass
Chemaclass merged commit cbb4760 into main Aug 11, 2026
37 checks passed
@Chemaclass
Chemaclass deleted the feat/1017-cobertura-coverage branch August 11, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant