Coverage, Test Execution Data, Duplication, and External Analyzer Reports: Diagnostics, Failure Modes, and Production Practices
Diagnose missing, stale, incompatible, post-scan, container-path, exclusion-gaming, duplication, and external-issue failures by preserving the producer artifact and scanner evidence before changing configuration.
Learning objectives
- Diagnose a missing coverage measure by checking producer execution, artifact existence/format, path mapping, scanner logs, task completion, and metric state in that order.
- Recognize stale report reuse, reports generated after analysis, host paths passed inside containers, incompatible report merges, and unsupported formats.
- Reject metric gaming through broad coverage/source/duplication exclusions and preserve the original failing evidence before correction.
- Explain why a successful scanner upload cannot prove that the intended external report was imported or that the quality gate passed.
- Diagnose unexpected duplication by inspecting indexed files and CPD rules/exclusions rather than searching for an external duplication report.
- Distinguish external issue import failures from native analyzer/rule/profile problems and preserve first-failure evidence.
1. Evidence-first diagnostic sequence
-
Preserve first-failure evidence: producer logs,
report bytes/hash, scanner logs,
report-task.txt, CE task, API/UI result. - Confirm exact SonarQube edition/version, scanner, language/runtime, producer tool, and external analyzer versions.
-
Confirm source revision and
sonar.projectBaseDir. - Prove the report was generated before the scan and belongs to the same revision/build.
- Validate format and scanner-visible path.
- Inspect the corresponding scanner sensor/import log.
- Confirm report upload and Compute Engine task success.
- Inspect the persisted coverage/test/duplication/external-issue result.
- Only then inspect policy/gate effects.
- Apply the least destructive correction and rerun the smallest equivalent scenario.
2. Failure: “SonarQube should have run my tests”
Symptom: no coverage, but no test command exists in the pipeline.
Cause: scanner executed without a producer artifact.
Repair: add the test/coverage command before analysis, fail the pipeline if tests or report generation fails, then prove report existence/hash before scanning.
Do not: lower the gate threshold, mark files excluded, or treat Zero Coverage Sensor output as proof that tests ran.
3. Failure: report generated after the scan
09:00 sonar-scanner → 09:02 analysis uploaded → 09:05 pytest
writes coverage.xml
The 09:05 report belongs to no already-uploaded analysis. Reordering the pipeline is the fix. Retrying the server or Compute Engine cannot import a local file that was absent during scanner execution.
4. Failure: host path inside a scanner container
# Host producer creates:
/opt/ci/work/reports/coverage.xml
# Scanner container mounts repository at /workspace:
docker run --rm \
-v "$PWD:/workspace" -w /workspace \
...scanner-image...
# Correct scanner-visible path:
reports/coverage.xml
# Wrong host-only path inside container:
/opt/ci/work/reports/coverage.xml
Inspect the actual mount and run
ls -l reports/coverage.xml inside the scanner
environment before changing Sonar properties.
5. Failure: stale report from another revision
A cached coverage.xml can exist and still be wrong.
Compare build revision, report timestamps, artifact manifest, source
paths in the XML, and CI job lineage. Prefer cleaning/recreating the
report directory before the test run, then archive the new report
with the source SHA.
6. Failure: incompatible report merge
Combining XML by string concatenation can create malformed documents or semantically invalid totals. Even valid XML can represent incompatible producer versions or path roots. Validate each input and use the producer’s supported merge operation or Sonar’s documented multiple-path support.
7. Failure: exclusions added to inflate coverage
Symptom: coverage rises sharply after
sonar.coverage.exclusions=src/legacy/** without new
tests.
Diagnosis: denominator changed, not test evidence.
Preserve before/after lines_to_cover,
uncovered_lines, source scope, exclusion setting, and
change rationale.
Repair: restore the governed scope unless the excluded files are independently justified as generated/non-testable. Do not call the higher percentage an engineering improvement if it came only from scope removal.
8. Failure: expected duplication does not appear
Check:
- Are the files indexed and supported for duplication?
- Does the repeated region exceed current language-specific token/line thresholds?
-
Is
sonar.cpd.exclusionsset globally/project/scanner-side? - Do scanner logs show CPD calculating files?
- Did source scope change?
Do not invent a duplication report import. CPD is computed from source.
9. Failure: imported external finding is treated like a native rule
Symptom: team searches the quality profile for
SARIF rule ACADEMY001 and cannot find it.
Cause: external rules remain external; generic/SARIF imported rule definitions are not native profile rules.
Repair: trace producer name/version/rule ID/report and manage the rule in the external analyzer. Use Sonar for visibility and policy integration, not as a false source of rule ownership.
10. Layer map for causal troubleshooting
| Layer | Evidence | Typical failure |
|---|---|---|
| Producer | pytest/coverage/linter logs | Tool never ran or failed |
| Artifact | file, schema, size, hash | Missing/stale/incompatible format |
| Workspace/path | pwd, mounts, artifact download |
Scanner cannot see producer file |
| Scanner sensor | -X import logs |
Wrong parameter/path/format |
| Analysis report | report-task.txt |
No upload or wrong project key |
| Compute Engine | CE task JSON/UI | Server-side processing failure |
| Metric/issue | measures/issues API | Imported data absent or different semantics |
| Policy | gate/profile/new-code state | Data imported correctly but policy outcome misunderstood |
11. Intentionally broken example: preserve and repair
# Preserve producer evidence.
cp reports/coverage.xml evidence/coverage-before-fix.xml
sha256sum reports/coverage.xml > evidence/coverage-before-fix.sha256
# Broken: scanner-visible file is reports/coverage.xml.
sonar-scanner -X -Dsonar.python.coverage.reportPaths=/host-only/work/coverage.xml \
2>&1 | tee evidence/path-failure.log
cp .scannerwork/report-task.txt evidence/path-failure-report-task.txt
# Repair only the path; do not change source, exclusions, gate, or project key.
sonar-scanner -X -Dsonar.python.coverage.reportPaths=reports/coverage.xml \
2>&1 | tee evidence/path-repaired.log
Compare both scanner sensor logs, both CE tasks, and the final coverage measure. The original failure remains in the packet rather than being erased.
12. Production anti-patterns to reject
- Blanket scanner retries without preserving the first report/import error.
- Deleting report directories/logs before copying evidence.
- Disabling TLS validation because a report import failed.
- Using admin tokens in test jobs that only need analysis.
- Direct database edits to “fix” coverage or issue state.
- Lowering gate thresholds after seeing missing-report results.
- Mass exclusions/suppressions to recover a green pipeline.
- Re-running analysis under a different project key to escape history.
Knowledge check
Coverage XML exists, but its timestamp predates the current commit/build by a day. What should you suspect?
A stale artifact. Preserve it, then regenerate from the current revision before analysis.
Why can restarting SonarQube not fix a report generated after scanner upload?
The local report was never included in the uploaded analysis report; server restart cannot discover it retroactively.
Coverage rose from 45% to 90% after excluding half the source. Did test effectiveness necessarily improve?
No. The denominator changed; inspect exclusion rationale and underlying covered/uncovered counts.
CPD logs show “Calculating CPD for 0 files.” Which layer do you inspect?
Indexing/language/CPD exclusion state, not a nonexistent duplication report import.
A SARIF issue cannot be found on the Rules page. Is that automatically import failure?
No. External rules are not native quality-profile rules; inspect imported external issues and producer metadata.
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.