From 8e0f4fc8d03efcdfe2689eee285768f8222fdeb8 Mon Sep 17 00:00:00 2001 From: portersky <24420859+portersky@users.noreply.github.com> Date: Sun, 10 May 2026 00:53:03 +0200 Subject: [PATCH] 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. --- .gitignore | 1 + AGENTS.md | 50 +++++++++++++++++++++++++++++++++---- CMakeLists.txt | 2 ++ README.md | 59 +++++++++++++++++++++++++++++++++++--------- deps/Coverage.cmake | 46 ++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 24 ++++++++++++++++++ 6 files changed, 166 insertions(+), 16 deletions(-) create mode 100644 deps/Coverage.cmake diff --git a/.gitignore b/.gitignore index 71d8e2a..07103c1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build build-* cmake-build-* +coverage # Editors .vscode diff --git a/AGENTS.md b/AGENTS.md index 334a055..9003aeb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,13 +12,44 @@ ### Commands +Configure (default): ```sh 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) -./build/main # run (Linux/macOS) -./build/main.exe # run (Windows) +``` + +Configure with coverage: +```sh +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 @@ -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 (```` ```cpp ````, ```` ```sh ````). - 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. ## Source Layout diff --git a/CMakeLists.txt b/CMakeLists.txt index 9153b43..b7ce035 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/deps") include(Platform) include(Flags) +include(Coverage) + # Example Library add_subdirectory(ctdd) diff --git a/README.md b/README.md index 1143329..79482bd 100644 --- a/README.md +++ b/README.md @@ -9,38 +9,75 @@ manual installation required beyond the tools listed below. ## Requirements -| Tool | Purpose | -| ------------------- | --------------------- | -| CMake ≥ 3.21 | Build system | -| Ninja | Build backend | -| C23 compiler | GCC 14+, Clang 18+ | -| Ruby ≥ 3.0 | CMock mock generation | +| Tool | Purpose | +| ------------ | ----------------------------------------------------- | +| CMake ≥ 3.21 | Build system | +| Ninja | Build backend | +| C23 compiler | GCC 14+, Clang 18+ | +| Ruby ≥ 3.0 | CMock mock generation | +| gcovr ≥ 6.0 | Coverage reports — optional (`uv tool install gcovr`) | ## Build +Configure: ```sh cmake -S . -B build -G Ninja +``` + +Build: +```sh ninja -C build ``` ## Test +Full Unity output with colors: ```sh -ninja -C build check # full Unity output, colored -ninja -C build test # CTest summary only +ninja -C build check +``` + +CTest summary only: +```sh +ninja -C build test ``` `check` builds all suites and runs CTest with `--output-on-failure`, so assertion-level detail appears on any failure without running binaries by hand. -## Run +## Coverage +Configure with coverage instrumentation: ```sh -./build/main.exe # Windows -./build/main # Linux / macOS +cmake -S . -B build-cov -G Ninja -DENABLE_COVERAGE=ON ``` +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) diff --git a/deps/Coverage.cmake b/deps/Coverage.cmake new file mode 100644 index 0000000..67fbb08 --- /dev/null +++ b/deps/Coverage.cmake @@ -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() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 57709ad..19af94e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -67,3 +67,27 @@ add_custom_target(check USES_TERMINAL 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()