feat: add coverage and command doc rule

Add Coverage.cmake with gcovr integration, detecting Clang vs
GCC and wrapping llvm-cov gcov in a build-dir script to handle
paths with spaces on Windows. Update README and AGENTS.md with
the new one-command-per-block documentation rule.
This commit is contained in:
2026-05-10 00:53:03 +02:00
parent cd19cc14c0
commit 8e0f4fc8d0
6 changed files with 166 additions and 16 deletions
+1
View File
@@ -2,6 +2,7 @@
build build
build-* build-*
cmake-build-* cmake-build-*
coverage
# Editors # Editors
.vscode .vscode
+45 -5
View File
@@ -12,13 +12,44 @@
### Commands ### Commands
Configure (default):
```sh ```sh
cmake -S . -B build -G Ninja cmake -S . -B build -G Ninja
ninja -C build # build ```
ninja -C build check # run tests (full Unity output, colored)
ninja -C build test # run tests (CTest summary only) Configure with coverage:
./build/main # run (Linux/macOS) ```sh
./build/main.exe # run (Windows) cmake -S . -B build-cov -G Ninja -DENABLE_COVERAGE=ON
```
Build:
```sh
ninja -C build
```
Run tests — full Unity output, colored:
```sh
ninja -C build check
```
Run tests — CTest summary only:
```sh
ninja -C build test
```
Run tests and generate coverage HTML:
```sh
ninja -C build-cov coverage
```
Run (Linux/macOS):
```sh
./build/main
```
Run (Windows):
```sh
./build/main.exe
``` ```
### Dependencies ### Dependencies
@@ -114,6 +145,15 @@ in HH:MM:SS.mmm format, updating every 10ms with color output.
`-` or numbered lists, fenced code blocks with language hints `-` or numbered lists, fenced code blocks with language hints
(```` ```cpp ````, ```` ```sh ````). (```` ```cpp ````, ```` ```sh ````).
- Keep examples concise, up-to-date, and self-documenting. - Keep examples concise, up-to-date, and self-documenting.
- Each shell command gets its own fenced code block — do **not** combine
multiple commands into one block. Precede each block with a short
plain-text label describing what the command does:
Setup build:
```sh
cmake -S . -B build -G Ninja
```
- This file (`AGENTS.md`) follows its own rules. - This file (`AGENTS.md`) follows its own rules.
## Source Layout ## Source Layout
+2
View File
@@ -9,6 +9,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/deps")
include(Platform) include(Platform)
include(Flags) include(Flags)
include(Coverage)
# Example Library # Example Library
add_subdirectory(ctdd) add_subdirectory(ctdd)
+48 -11
View File
@@ -9,38 +9,75 @@ manual installation required beyond the tools listed below.
## Requirements ## Requirements
| Tool | Purpose | | Tool | Purpose |
| ------------------- | --------------------- | | ------------ | ----------------------------------------------------- |
| CMake ≥ 3.21 | Build system | | CMake ≥ 3.21 | Build system |
| Ninja | Build backend | | Ninja | Build backend |
| C23 compiler | GCC 14+, Clang 18+ | | C23 compiler | GCC 14+, Clang 18+ |
| Ruby ≥ 3.0 | CMock mock generation | | Ruby ≥ 3.0 | CMock mock generation |
| gcovr ≥ 6.0 | Coverage reports — optional (`uv tool install gcovr`) |
## Build ## Build
Configure:
```sh ```sh
cmake -S . -B build -G Ninja cmake -S . -B build -G Ninja
```
Build:
```sh
ninja -C build ninja -C build
``` ```
## Test ## Test
Full Unity output with colors:
```sh ```sh
ninja -C build check # full Unity output, colored ninja -C build check
ninja -C build test # CTest summary only ```
CTest summary only:
```sh
ninja -C build test
``` ```
`check` builds all suites and runs CTest with `--output-on-failure`, `check` builds all suites and runs CTest with `--output-on-failure`,
so assertion-level detail appears on any failure without running so assertion-level detail appears on any failure without running
binaries by hand. binaries by hand.
## Run ## Coverage
Configure with coverage instrumentation:
```sh ```sh
./build/main.exe # Windows cmake -S . -B build-cov -G Ninja -DENABLE_COVERAGE=ON
./build/main # Linux / macOS
``` ```
Generate the HTML report:
```sh
ninja -C build-cov coverage
```
Open `build-cov/coverage/index.html` in a browser to view results.
Only `ctdd/` source files are measured — Unity, CMock, and generated
mock files are excluded. Requires GCC or Clang with gcov support, and
`gcovr` on `PATH`.
> **Windows note:** requires GCC (e.g. `scoop install gcc`) or a Clang
> build that includes compiler-rt. A custom Clang without compiler-rt
> will fail at link time.
## Run
Windows:
```sh
./build/main.exe
```
Linux / macOS:
```sh
./build/main
```
## Adding a new module (TDD workflow) ## Adding a new module (TDD workflow)
+46
View File
@@ -0,0 +1,46 @@
# ==============================================================================
# Coverage
# ==============================================================================
# gcov-based coverage via --coverage, reported by gcovr.
# Works with GCC and Clang on Linux and macOS out of the box.
# Windows requires GCC (e.g. MinGW via scoop install gcc) or a Clang build
# that includes compiler-rt (clang_rt.profile).
#
# Requires: gcovr on PATH (install via: uv tool install gcovr)
# Usage: cmake -DENABLE_COVERAGE=ON ... then ninja coverage
# Report: build/coverage/index.html
# ==============================================================================
option(ENABLE_COVERAGE "Build with coverage instrumentation" OFF)
if (ENABLE_COVERAGE)
if (NOT CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
message(FATAL_ERROR "ENABLE_COVERAGE requires GCC or Clang")
endif()
find_program(GCOVR_EXE gcovr REQUIRED)
if (CMAKE_C_COMPILER_ID MATCHES "Clang")
find_program(LLVM_COV_EXE llvm-cov REQUIRED)
# gcovr needs a single-token gcov executable. Wrap llvm-cov gcov in a
# script placed in the build dir (guaranteed no spaces in path).
if (WIN32)
set(GCOV_EXECUTABLE "${CMAKE_BINARY_DIR}/llvm-gcov.bat")
file(WRITE "${GCOV_EXECUTABLE}" "@echo off\r\n\"${LLVM_COV_EXE}\" gcov %*\r\n")
else()
set(GCOV_EXECUTABLE "${CMAKE_BINARY_DIR}/llvm-gcov.sh")
file(WRITE "${GCOV_EXECUTABLE}" "#!/bin/sh\nexec \"${LLVM_COV_EXE}\" gcov \"$@\"\n")
file(CHMOD "${GCOV_EXECUTABLE}"
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
endif()
message(STATUS "Coverage: enabled (gcovr: ${GCOVR_EXE}, gcov: ${LLVM_COV_EXE} gcov)")
else()
set(GCOV_EXECUTABLE "gcov")
message(STATUS "Coverage: enabled (gcovr: ${GCOVR_EXE})")
endif()
add_compile_options(--coverage -O0 -g)
add_link_options(--coverage)
endif()
+24
View File
@@ -67,3 +67,27 @@ add_custom_target(check
USES_TERMINAL USES_TERMINAL
DEPENDS ${TEST_TARGETS} DEPENDS ${TEST_TARGETS}
) )
if (ENABLE_COVERAGE)
set(COVERAGE_DIR "${CMAKE_BINARY_DIR}/coverage")
add_custom_target(coverage
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
COMMAND ${CMAKE_COMMAND} -E make_directory "${COVERAGE_DIR}"
COMMAND ${GCOVR_EXE}
--gcov-executable "${GCOV_EXECUTABLE}"
--root "${CMAKE_SOURCE_DIR}"
--filter "${CMAKE_SOURCE_DIR}/ctdd/"
--exclude "${CMAKE_SOURCE_DIR}/tests/"
--exclude ".*Mock.*"
--exclude ".*unity.*"
--exclude ".*cmock.*"
--html-details "${COVERAGE_DIR}/index.html"
--txt
--print-summary
"${CMAKE_BINARY_DIR}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
USES_TERMINAL
DEPENDS ${TEST_TARGETS}
COMMENT "Coverage report: ${COVERAGE_DIR}/index.html"
)
endif()