Coverage, Test Execution Data, Duplication, and External Analyzer Reports: Configuration, Design Patterns, and Trade-Offs
Choose coverage versus test execution, native versus external findings, report-merging and path-mapping strategies, coverage/duplication exclusions, and local-versus-container layouts using explicit provenance and rollback criteria.
Learning objectives
- Choose language-native report parameters versus generic Sonar formats based on producer compatibility and long-term maintainability.
- Keep coverage and test-execution data separate even when the same test run produces both artifacts.
- Use coverage exclusions only for legitimate non-testable scope decisions and keep them distinct from source exclusions and CPD exclusions.
- Design report paths that survive local, CI, container, and monorepo workspace changes without embedding host-only absolute paths.
- Distinguish native Sonar rules from imported external rules whose lifecycle and configuration remain owned by the third-party analyzer.
- Evaluate single/merged report strategies using provenance, compatibility, failure isolation, portability, and auditability.
1. Configuration is an evidence-supply-chain design
A report import setting is not “just another scanner flag.” It couples a producer, format, path, workspace, scanner version, language analyzer, server version, and downstream policy. Good design makes those dependencies explicit and reproducible.
2. Coverage versus test execution: keep separate contracts
| Decision | Coverage | Test execution |
|---|---|---|
| Question | What code/conditions were exercised? | What tests ran and what happened? |
| Typical producer | coverage.py, JaCoCo, LCOV producer | pytest/JUnit/NUnit/xUnit/etc. |
| Failure symptom | Missing/zero coverage or uncovered lines | Missing test count/failure/time measures |
| PR support | Depends on coverage/language path and PR feature support | Current Community Build execution-report import is branch-only |
| Governance | Often gate-relevant | Usually diagnostic/operational evidence |
One producer command may generate both, but retain separate files, hashes, parameters, and assertions. Combining semantic roles into one “test report” label makes diagnosis harder.
3. Native producer format versus generic conversion
Prefer a documented language-native parameter when SonarQube
directly supports the producer format—for example Python Cobertura
XML through sonar.python.coverage.reportPaths. Use
generic coverage/test formats when the producer is unsupported and
conversion is stable and tested.
Native path
Less custom transformation, clearer vendor documentation, but tied to language/tool integration.
Generic path
Portable across unsupported tools, but the conversion becomes your owned build artifact and must be versioned/tested.
Do not merge unlike XML documents just because they share an extension. “XML” is serialization, not a semantic format.
4. Single versus merged reports
Multiple test shards or modules can create multiple compatible coverage reports. Merge at the producer layer when the producer officially supports it, or use a Sonar property that accepts multiple paths where documented. Preserve per-shard artifacts as provenance even if a merged file is used for scanning.
shard reports → validated compatible merge → merged report hash →
scanner import
Avoid blind concatenation. XML documents, LCOV records, JaCoCo reports, and Cobertura outputs have different merge semantics.
5. Source exclusions, coverage exclusions, and duplication exclusions are not interchangeable
| Mechanism | Effect | Governance question |
|---|---|---|
sonar.exclusions |
Removes files from source analysis scope | Should these files be analyzed at all? |
sonar.coverage.exclusions |
Keeps files analyzed but omits them from coverage calculation | Are they legitimately non-testable/generated/configuration artifacts? |
sonar.cpd.exclusions |
Omits files from duplication detection | Is repeated generated/vendor content distorting CPD? |
Changing any exclusion alters the measurement population. Record ownership, reason, affected paths, before/after measures, and rollback. Never add broad exclusions after seeing an unfavorable number simply to improve the dashboard.
6. Local, CI, and container path patterns
| Pattern | Strength | Failure mode |
|---|---|---|
Repo-relative reports/coverage.xml |
Portable and reviewable | Producer/scanner use different working roots |
| Absolute host path | Unambiguous on one machine | Breaks on agents/containers/other OSes |
| CI artifact download into fixed repo-relative directory | Clear stage boundary and provenance | Artifact not downloaded or wrong revision |
| Container bind/volume mount | Explicit namespace mapping | Host path configured instead of container-visible path |
Recommended default: materialize all reports under a predictable
project-relative reports/ directory before starting the
scanner, then prove that directory inside the scanner execution
environment.
7. Native findings versus external findings
Native Sonar findings are produced by active quality-profile rules. External findings come with external rule IDs and lifecycle/configuration owned outside the profile. They can still affect analysis/gate status, but do not pretend their remediation/status semantics are identical to native rules.
Dedicated analyzer integration
Use documented producer output and analyzer-specific property; simplest when supported.
Generic Sonar issue format
Useful for unsupported producers; your converter owns schema correctness and rule metadata.
SARIF 2.1.0
Standard interchange; Sonar maps SARIF severities into current MQR/Standard semantics. Preserve producer name/rule IDs.
8. Ownership and least privilege
- Test team/build owns producer commands and tool versions.
- Repository/CI owns report placement and transfer.
- Sonar project admins own import settings and exclusions.
- Security/quality governance owns policy exceptions.
- External analyzer owners own rule configuration for imported issues.
- Scanner token needs analysis permission, not broad system administration.
9. Worked decision table
| Scenario | Choice | Why | Evidence |
|---|---|---|---|
| Python unit coverage |
pytest-cov → Cobertura XML →
sonar.python.coverage.reportPaths
|
Direct supported path | Tool version, XML hash, scanner import log, coverage measure |
| Unsupported in-house test harness | Convert to Generic coverage/execution format | No native integration | Converter version/tests + schema-valid output |
| Two CI stages | Publish/download report artifact | Scanner stage cannot see producer filesystem | Artifact manifest, revision, hash, final path |
| Third-party security linter | Dedicated integration or SARIF | Preserve external ownership | Producer/rule/version + report + imported issue |
| Generated code hurts coverage | Evaluate narrow coverage exclusion | Keep code analysis while excluding non-testable generated surface | Generator ownership, path pattern, rationale, before/after denominator |
Knowledge check
When is generic coverage preferable to a language-native parameter?
When the producer is unsupported and you can maintain a tested conversion to Sonar’s generic format.
Why is sonar.coverage.exclusions safer than
sonar.exclusions for genuinely non-testable
generated source?
Coverage exclusion preserves static analysis of the file while changing only the coverage denominator.
Can you simply concatenate two Cobertura XML files?
Not as a general rule. Use producer-supported merging or a documented multi-path import; XML concatenation does not preserve schema semantics.
Who owns an imported SARIF rule’s configuration?
The external analyzer/configuration system, not the Sonar quality profile.
What is the first design preference for report paths?
A predictable project-relative path that exists in the scanner execution environment, with artifact provenance preserved.
Official references and version notes
- Test coverage overview — SonarQube consumes coverage reports generated by external build/test tools; it does not execute tests.
-
Test coverage parameters
— language-specific and generic coverage import keys including
sonar.python.coverage.reportPathsandsonar.coverageReportPaths. -
Test execution parameters
— execution-report keys including
sonar.testExecutionReportPathsandsonar.python.xunit.reportPath; execution reports are branch-only. - Generic test data — generic coverage and execution XML formats when a producer has no native integration.
- About external issues — supported external-analyzer integrations and how imported results participate in analysis.
- External analyzer reports — analyzer-specific report paths including Python Pylint/Bandit/Flake8/Mypy/Ruff integrations.
-
Generic external-issue format
—
sonar.externalIssuesReportPathsand ownership semantics for third-party rules. -
SARIF reports
— SARIF 2.1.0 requirements and
sonar.sarifReportPaths. -
Scanner-only parameters
— external report paths,
report-task.txt, quality-gate wait behavior, and path resolution. - Metric definitions — coverage/test metrics and current duplication/CPD thresholds and keys.
- SonarQube downloads — current Community Build release identity.
- SonarScanner CLI 8.1.0.6389 — standalone scanner baseline used in the local lab.
Rechecked 2026-09-07. Mandatory examples target SonarQube
Community Build 26.9.0.129388 and SonarScanner
CLI 8.1.0.6389. The Python lab uses Python 3.11+
with pytest/pytest-cov and Cobertura XML. Current Community Build
accepts Python coverage through
sonar.python.coverage.reportPaths, Python xUnit
execution data through sonar.python.xunit.reportPath,
generic coverage through sonar.coverageReportPaths,
generic test execution through
sonar.testExecutionReportPaths, generic external
issues through sonar.externalIssuesReportPaths, and
SARIF through sonar.sarifReportPaths. Paths are
interpreted relative to sonar.projectBaseDir unless
the property documents otherwise. Duplication is calculated from
indexed source; non-Java duplication currently requires at least
100 successive duplicated tokens spread across at least 10 lines
for languages other than COBOL/ABAP, while Java uses 10 successive
duplicated statements. Re-check producer format compatibility and
report parameters before applying these examples to another
release/language.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.