Checkpoint Lab — Coverage, Test Execution Data, Duplication, and External Analyzer Reports
Create a tiny tested project, produce and import coverage evidence, deliberately break the report path, prove the corrected coverage measure without gaming exclusions, capture duplication/test evidence, and deliver a provenance-rich evidence packet.
Learning objectives
- Execute a complete wrong-path → corrected-path coverage experiment on one fixed source revision.
- Predict and verify at least two independent state changes: report-import state and Sonar coverage measure, while source revision stays unchanged.
- Capture test-execution, coverage, duplication, scanner, report-task, Compute Engine, and API/UI evidence in a reusable dossier.
- Prove that correction came from artifact/path alignment rather than exclusions, gate changes, source edits, or a new project key.
- Revoke disposable credentials and remove only guarded lab resources after preserving evidence.
- Bridge the evidence-ingestion model to Chapter 15: Security Hotspots, Vulnerabilities, Taint Analysis, and Review Workflow.
1. Checkpoint scenario
Build one clean local evidence chain: create a tiny Python project, run real tests, generate coverage and execution reports, deliberately point the scanner at the wrong coverage path, preserve the failure, correct only that path, and prove the corrected persisted measure. Also capture duplication and test-execution state and document how external issues would be added without confusing them with native rules.
The checkpoint succeeds only if the evidence shows why the measure changed. A green number without provenance is insufficient.
2. Exact assumptions and resource preflight
| Item | Checkpoint assumption | Evidence |
|---|---|---|
| SonarQube | Community Build 26.9.0.129388 | /api/server/version |
| Scanner | CLI 8.1.0.6389 | sonar-scanner --version |
| Python | 3.11+; pytest/pytest-cov/coverage | tool version output |
| Project | academy-sq-ch14 |
stable project key |
| Coverage format | Cobertura XML |
reports/coverage.xml root/schema evidence
|
| Execution format | pytest xUnit/JUnit XML | reports/junit.xml |
| Auth | short-lived project-analysis token + optional Browse API token | token name/scope/lifetime record, never token value |
| Commercial/plugin/CI | none required | assumptions note |
curl -fsS "$SONAR_HOST_URL/api/server/version"
sonar-scanner --version
python --version
python -m pytest --version
python -m coverage --version
git rev-parse --is-shallow-repository
git rev-parse HEAD
3. Predictions before touching the import path
| State | Broken run prediction | Corrected run prediction | Proof |
|---|---|---|---|
| Source revision | SHA X | same SHA X | git rev-parse HEAD |
| Coverage XML bytes | hash H | same hash H | sha256sum |
| Coverage import | scanner cannot load configured missing path | scanner loads reports/coverage.xml |
sensor log |
| Coverage measure | absent/low according to sensor result | reflects producer evidence | measures API/UI |
| Test execution | JUnit file exists/imports independently | same | tests/failure/time metrics |
| CE task | separate task ID | separate task ID | report-task.txt + CE API |
At least the first four predictions must be written before the broken analysis.
4. Execute the wrong-path → corrected-path experiment
- Create and commit the Lesson 2 fixture.
- Run pytest/pytest-cov and preserve both XML files plus producer logs/hashes.
-
Run scanner with
-Dsonar.python.coverage.reportPaths=reports/does-not-exist.xml. -
Preserve the broken scanner log and
report-task.txt. - Wait for/inspect the broken CE task; capture measures.
- Verify the source SHA and report hashes did not change.
-
Run scanner using the correct project property
reports/coverage.xml. - Preserve corrected scanner log, task ID, CE task, and measures.
-
Compare producer coverage percentage/line data with Sonar
coverage,lines_to_cover, anduncovered_lines. -
Inspect imported
tests, failures/errors/skips/time and duplication metrics without claiming they came from the coverage report.
5. Verification checklist
- Broken and corrected analyses use the exact same Git revision.
-
coverage.xmlandjunit.xmlhashes are identical across both scans. - Broken scanner log records the unresolved/missing coverage report path.
- Correct scanner log records successful Python coverage report processing.
-
Both scans have preserved, distinct
ceTaskIdvalues. - Correct CE task reaches SUCCESS before interpreting measures.
- Coverage changes without adding a coverage exclusion or source exclusion.
- No gate/profile/New Code definition changes were used to make the result look better.
- Test-execution measures are traced to JUnit/xUnit XML, not Cobertura XML.
- Duplication measures are traced to indexed source/CPD, not an external report.
6. Required evidence packet
evidence/
assumptions.md
server-version.txt
scanner-version.txt
python-tool-versions.txt
revision.txt
producer-command.txt
pytest-output.txt
coverage.xml
junit.xml
report-sha256.txt
sonar-project.properties.snapshot
broken-scan.log
broken-report-task.txt
broken-ce-task.json
broken-measures.json
correct-scan.log
correct-report-task.txt
correct-ce-task.json
correct-measures.json
import-delta.md
duplication-notes.md
external-issue-boundary.md
credential-record.md
cleanup.txt
Do not copy actual token values, cookies, unrelated environment dumps, proprietary source, or production reports into the packet.
7. Write the causal conclusion
The test producer generated Cobertura XML and xUnit XML successfully for revision X. The first SonarScanner run used a non-existent coverage path; its sensor log preserved the import failure while the source revision and report bytes remained unchanged. The second run changed only the coverage import path to the scanner-visible report and produced a new CE task whose persisted coverage matched the producer evidence. No source, coverage exclusion, gate, profile, or project-key change was used to alter the result. Test execution remained a separate imported evidence family, and duplication remained Sonar-computed CPD evidence.
That is stronger than “coverage is fixed.” It identifies the owning layer and controlled variable.
8. External analyzer boundary note
Document one of these:
- Not exercised: no external analyzer was needed; SARIF/generic paths were reviewed only.
- Optional SARIF exercised in separate project: preserve SARIF 2.1.0 bytes, producer/rule IDs, scanner import log, imported issue, and confirm it does not become a native profile rule.
Commercial features are not required. If your organization later uses paid analyzers/CI/platform integrations, reproduce the same provenance contract rather than substituting screenshots for artifacts.
9. Guarded cleanup and rollback
-
Copy
.scannerwork/report-task.txtand all producer/scanner evidence before deletion. - Revoke the disposable project-analysis token and optional Browse token.
- Delete the optional SARIF project only after its evidence is preserved.
-
Delete
academy-sq-ch14from SonarQube only if its exact key is confirmed to be the disposable lab key. - Remove the local fixture directories only after verifying the evidence packet exists elsewhere.
- Do not use bulk project deletion, database edits, search-index deletion, or global cleanup commands.
10. What this adds to a governed production model
Chapter 14 adds evidence provenance to the operating model. Coverage and external findings are trustworthy only when the producer, version, report format, report bytes, path mapping, source revision, scanner import, asynchronous task, and resulting project state can be reconstructed.
This prepares Chapter 15, where security findings and review workflows require even stricter distinctions between analyzer evidence, vulnerability claims, Security Hotspots, taint/dataflow findings, human review, and accepted residual risk.
Knowledge check
What is the controlled variable in the checkpoint?
The coverage import path. Source revision, report bytes, project key, profile, gate, and producer output stay fixed.
Why preserve both broken and corrected CE task IDs?
They prove two distinct asynchronous server processings and tie each persisted result to the corresponding scanner run.
Which evidence proves JUnit test count and coverage percentage are separate?
The two producer files plus separate Sonar import parameters/metric families: execution XML drives test metrics while coverage XML drives coverage metrics.
Why is adding sonar.coverage.exclusions not an
acceptable repair for a missing report?
It changes the coverage population instead of fixing report production/import and can game the metric.
What comes next?
Security Hotspots, vulnerabilities, taint analysis, and review workflow—where evidence classification and human review become central.
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.