Skip to content

feat(coverage): Cobertura XML report for GitLab, Azure and Jenkins #1017

Description

@Chemaclass

Problem

Coverage output is LCOV-only (src/coverage/report_lcov.sh:5), plus a self-contained HTML report. LCOV is what Codecov and Coveralls want; it is not what the CI platforms with built-in coverage UIs want:

  • GitLab CI coverage visualisation in merge requests: Cobertura XML only.
  • Azure DevOps PublishCodeCoverageResults: Cobertura or JaCoCo.
  • Jenkins Coverage plugin: Cobertura among others.

So a bashunit user on GitLab has full line and branch coverage data and no way to render it next to the diff.

The tracking engine already produces everything Cobertura needs — per-file line hits (src/coverage/lines.sh), branch data (src/coverage/branches.sh) and totals (src/coverage/stats.sh).

Proposal

--coverage-report-cobertura [file]     Write Cobertura XML (default: coverage/cobertura.xml)

Emit a standard Cobertura document:

<coverage line-rate="0.87" branch-rate="0.71" lines-covered="" lines-valid=""
          branches-covered="" branches-valid="" version="" timestamp="">
  <sources><source>/abs/repo/root</source></sources>
  <packages>
    <package name="src.assert" line-rate="" branch-rate="">
      <classes>
        <class name="core.sh" filename="src/assert/core.sh" line-rate="" branch-rate="">
          <methods>…</methods>
          <lines><line number="12" hits="3" branch="false"/></lines>
        </class>
      </classes>
    </package>
  </packages>
</coverage>

filename must be repo-relative — GitLab resolves paths relative to the repository root and silently shows nothing when they are absolute.

Where to change

Acceptance criteria

  • --coverage --coverage-report-cobertura writes a well-formed document
  • Validates against the Cobertura DTD (coverage-04.dtd) — assert with xmllint when available, skip cleanly when not
  • filename attributes are repo-relative
  • line-rate and branch-rate match the percentages the console report prints
  • Branch data is emitted with branch="true" and condition-coverage="50% (1/2)" where the engine has it
  • Files with zero hits still appear (otherwise coverage looks better than it is)
  • Coexists with --coverage-report (LCOV) and --coverage-report-html in one run
  • Unwritable path fails fast (A missing --env bootstrap or an unwritable report path fails silently and exits 0 #875)
  • Unit tests in tests/unit/coverage/

Repo checklist (agent)

  • TDD: RED → GREEN → REFACTOR. Write the failing test first.
  • 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[@]}"}.
  • A new CLI flag must be wired in all of these or a parity test fails:
  • Gates: make sa, make lint, ./bashunit tests/, ./bashunit --parallel tests/. Never run shfmt -w.
  • Docs: update docs/command-line.md. Editing docs/assertions.md invalidates the bashunit doc acceptance snapshot — regenerate it.
  • CHANGELOG.md: add one line under ## Unreleased.
  • Fixtures under tests/acceptance/fixtures/ must not end in *test.sh.
  • One issue = one PR.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions