> ## Documentation Index
> Fetch the complete documentation index at: https://docs.roboticks.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Code Coverage

> Wire gcov/lcov for C++ via colcon and coverage.py for Python so code coverage uploads alongside JUnit and surfaces on the matrix.

# Code coverage

Roboticks ingests **code coverage** (gcov/lcov for C++, `coverage.py` for Python) alongside JUnit XML and displays it in the test-run detail UI. Coverage is one of the headline numbers on every Check Run, but it answers a different question than [requirement coverage](/traceability/coverage).

<Warning>
  **Code coverage and requirement coverage are different.** Code coverage measures *what fraction of your source lines were executed by tests*. Requirement coverage measures *what fraction of your requirements have at least one passing test*. A repo can have 100% line coverage and 30% requirement coverage. They are independent dimensions.
</Warning>

## What we ingest

| Source  | Format                             | Tool                                   |
| ------- | ---------------------------------- | -------------------------------------- |
| Python  | `coverage.py` XML (`coverage.xml`) | `pip install coverage` or `pytest-cov` |
| C++     | lcov info (`coverage.info`)        | gcov + `lcov --capture`                |
| Generic | Cobertura XML                      | Any tool that emits Cobertura          |

The runner auto-discovers files named `coverage.xml`, `coverage.info`, or `cobertura.xml` in the workspace after tests complete and uploads them.

## Python coverage

Add `pytest-cov` to your dev dependencies:

```toml theme={null}
# pyproject.toml
[project.optional-dependencies]
test = ["pytest", "pytest-cov", "roboticks[mcap]"]
```

Run pytest with `--cov`:

```bash theme={null}
pytest --cov=my_package --cov-report=xml:coverage.xml --cov-report=term tests/
```

The runner finds `coverage.xml` and ships it. Locally, the same file works for any coverage UI.

## C++ coverage (colcon)

Coverage in a colcon workspace is a two-step dance: build with coverage flags, then capture after `colcon test`.

```bash theme={null}
# 1. Build with coverage
colcon build \
  --cmake-args -DCMAKE_CXX_FLAGS="--coverage -O0" \
               -DCMAKE_C_FLAGS="--coverage -O0" \
               -DCMAKE_EXE_LINKER_FLAGS="--coverage"

# 2. Run tests
colcon test --packages-select my_pkg

# 3. Capture
lcov --capture --directory build/ \
     --output-file coverage.info \
     --no-external \
     --base-directory $(pwd)

# 4. (Optional) Strip third-party noise
lcov --remove coverage.info '*/test/*' '/usr/*' '*/install/*' \
     --output-file coverage.info
```

Roboticks ships a CI-friendly wrapper:

```bash theme={null}
rbtk coverage capture --workspace .
```

That single command runs the lcov dance with project-aware exclusions.

## In CI

GitHub Actions step that produces both pytest and gtest coverage:

```yaml theme={null}
- name: Run tests with coverage
  run: |
    pytest --cov=my_pkg --cov-report=xml:coverage.xml
    colcon build --cmake-args -DCMAKE_CXX_FLAGS="--coverage"
    colcon test
    rbtk coverage capture --workspace .

- name: Upload to Roboticks
  uses: roboticks-io/upload-action@v1
  with:
    junit: 'test_results/**/*.xml'
    coverage: |
      coverage.xml
      coverage.info
```

When you're using the GitHub App, the upload happens automatically — the action above is for CI systems where you call the API yourself. See [CI recipes](/testing/ci-recipes).

## Where coverage surfaces

| Surface       | What you see                                                             |
| ------------- | ------------------------------------------------------------------------ |
| Check Run     | "Coverage 78.4% (+1.2%)" on the PR head                                  |
| Run detail    | Per-file gutter highlighting (covered / partially / uncovered)           |
| Matrix UI     | Coverage column per source file, joinable to requirement coverage        |
| Evidence pack | Coverage summary table per release, with file-level breakdown in the ZIP |

## Coverage gates

Configure a coverage floor in `roboticks.yaml`:

```yaml theme={null}
coverage:
  fail_under: 80          # PR fails if combined coverage < 80%
  per_file_floor: 60      # PR fails if any file < 60%
  ignore:
    - "**/generated/**"
    - "**/third_party/**"
```

The Check Run respects the gate. The matrix annotates each requirement's confirming tests with their per-file coverage.

## Coverage is not enough

A 95% line coverage number with 40% requirement coverage means "we run a lot of code, but we have not proven the requirements". An auditor will accept the latter and ignore the former. Use coverage to find dead code; use [requirement coverage](/traceability/coverage) to ship.

## Next

<CardGroup cols={2}>
  <Card title="Requirement coverage" icon="diagram-project" href="/traceability/coverage">
    The dimension that pays the audit bill.
  </Card>

  <Card title="CI recipes" icon="gear" href="/testing/ci-recipes">
    Wire coverage capture into GitHub Actions, Jenkins, CircleCI, BuildKite.
  </Card>

  <Card title="Check Runs" icon="github" href="/github-app/check-runs">
    The PR-side surface for both coverage numbers.
  </Card>

  <Card title="Matrix UI" icon="table-cells" href="/traceability/matrix">
    How coverage joins requirements visually.
  </Card>
</CardGroup>
