Capability 008: C++ Builds and Coverage¶
Status: Accepted
Context¶
A C++ artifact is built locally for three purposes:
- a debug build for the IDE and the debugger, which may also run on a target to investigate a problem,
- a coverage-instrumented build for the automated tests, and
- an optimized release build. Each has its own compiler flags.
The build system keeps the flags of a build directory, so two purposes sharing one directory leak flags into each other: the debug executable writes coverage data wherever it runs, and stale coverage data from an earlier run breaks the tests after a code change.
The compiler instruments the coverage build. Every process of that build adds its counts to the coverage data next to its object files. A scenario that starts the executable of the coverage build therefore counts like a programmed test. This capability defines the builds and how the C++ profiles measure the figure of capability 011.
References¶
- Capability 004: Rules and Tools
- Capability 006: Generated Pipelines
- Capability 011: Test Kinds and Coverage
Details¶
Builds:
| Build | Directory | Configuration | Used by |
|---|---|---|---|
| Debug | build/debug, build/Debug for a library |
Debug symbols, no optimization, no instrumentation | build, test, run, lint |
| Coverage | build/coverage |
As Debug, plus coverage instrumentation | coverage-build, bdd-test, coverage |
| Release | build/release, build/Release for a library |
Optimized, no debug symbols, no instrumentation | release-build, release-test |
The coverage build uses the dependencies of the debug build. The container images build their release executable on their own and are not affected.
Coverage:
| Aspect | Value |
|---|---|
| Metric | Lines |
| Counted | The programmed tests and the scenarios, both run against the coverage build |
| Excluded | src/main.cpp, test/, the build directories, and the dependency cache |
Functional Requirements¶
- Every C++ profile builds each build of Details in its own directory, and only the targets listed there use it.
- No target adds coverage instrumentation to the debug or the release build.
coverage-builddeletes the coverage data of earlier runs, so thatbdd-testandcoveragecount the current run only.coverageruns the programmed tests andbdd-testagainst the coverage build and reports their counts together, as Details defines.- No C++ profile has a
release-coveragetarget or pipeline job.release-testruns the tests against the release build.
Non-Functional Requirements¶
None.
Technical Requirements¶
- CMake with Conan builds, Catch2 runs the programmed tests, pytest-bdd the scenarios, and gcovr reports the coverage. The testing guideline prescribes the test tools, and the C++ CI images provide all of them.
Error Handling¶
None.
Out of Scope¶
- Sanitizers and their build option
- What capability 011 defines for every language
Acceptance Criteria¶
- Every C++ profile: the
Makefilepasses coverage flags to the build system only in the recipe ofcoverage-build, and only forbuild/coverage. - Every C++ profile:
bdd-testandcoveragereadbuild/coverage.build,test, andrunread the debug directory. - Every C++ profile: the recipe of
coverage-builddeletes the coverage data (.gcdafiles) belowbuild/coverage, andcoveragesearches coverage data inbuild/coverageonly. - Every C++ profile:
coveragedepends onbdd-testand excludes the paths of Details. - Every C++ profile on both forges: no
release-coveragetarget in theMakefile, norelease-coveragejob or step in the pipeline. - Generated C++ service,
make coverage, a source file changed,make coverageagain: both runs pass. Afterwardsmake buildproduces an executable without coverage instrumentation (manual testtest/manual/generated_coverage.md). - Generated C++ service, a function reached only by a scenario:
make coveragereports it as covered (manual testtest/manual/generated_coverage.md).
Decisions¶
- Three builds in three directories, not two. Debug and coverage serve different ends: an instrumented executable writes coverage data wherever it runs, which is unwanted where the debug executable investigates a problem. Consequence: a developer who both tests and debugs builds the sources twice.
- No coverage of the release build.
release-testalready shows that the tests pass with optimization. A second coverage figure of optimized code, whose lines the compiler merges and reorders, adds nothing. - The scenarios run the coverage build.
Its executable counts wherever it runs, so no step collects counts from the scenarios.
Consequence:
bdd-testbuilds the coverage build first, andcoveragedepends onbdd-test.