From ff88f48fa2a3455627274ddb6f1b7cf7a5cbc26a Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Thu, 30 Jul 2026 01:25:26 -0400 Subject: [PATCH] Add CTest coverage reporting Instrument libakgl in opt-in coverage builds, use CTest fixtures to reset counters and emit Cobertura XML and detailed HTML reports, and upload those reports from Gitea CI. Document the local coverage workflow for contributors. Co-authored-by: Codex (GPT-5) --- .gitea/workflows/ci.yaml | 13 ++++++++++-- AGENTS.md | 4 +++- CMakeLists.txt | 46 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 689a88a..96cc00f 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -14,7 +14,7 @@ jobs: run: | sudo apt-get update -y sudo apt-get install -y \ - cmake doxygen gcc pkg-config \ + cmake doxygen gcc gcovr pkg-config \ libasound2-dev libfreetype-dev libharfbuzz-dev \ libpng-dev libtiff-dev libwebp-dev \ libudev-dev libx11-dev libxcursor-dev libxext-dev \ @@ -22,7 +22,9 @@ jobs: libxss-dev libxtst-dev - name: Configure and build run: | - cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Debug \ + -DAKGL_COVERAGE=ON cmake --build build --parallel - name: Build API documentation run: doxygen Doxyfile @@ -43,6 +45,13 @@ jobs: detailed_summary: true include_passed: true fail_on_failure: 'true' + - name: Upload coverage reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: code-coverage + path: build/coverage/ + if-no-files-found: warn mutation_test: runs-on: ubuntu-latest diff --git a/AGENTS.md b/AGENTS.md index 91c8462..df3ee9f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,6 +28,8 @@ Run one test while iterating, for example `ctest --test-dir build -R sprite --ou Run mutation testing with `cmake --build build --target mutation`. For a quick smoke run, use `scripts/mutation_test.py --target src/tilemap.c --max-mutants 10`; the harness mutates only a scratch copy and excludes the intentionally failing character test. +Generate HTML and Cobertura coverage reports with `cmake -S . -B build-coverage -DAKGL_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug`, then build and run CTest. Reports are written to `build-coverage/coverage/`; this mode requires `gcovr` and GCC or Clang. + ## Coding Style & Naming Conventions Follow the surrounding C style: braces on the next line for function bodies, spaces inside control-flow parentheses, and short, focused functions. Preserve the indentation of the file being edited; older files contain both spaces and tabs. Public symbols use the `akgl_` prefix, public types use `akgl_TypeName`, and constants/macros use `AKGL_UPPER_SNAKE_CASE`. Name matching header/source pairs by feature, such as `include/akgl/sprite.h` and `src/sprite.c`. No repository-wide formatter or linter is configured, so avoid unrelated formatting churn. @@ -39,7 +41,7 @@ Follow the surrounding C style: braces on the next line for function bodies, spa ## Testing Guidelines -Tests use simple executable return codes and are registered through CTest in `CMakeLists.txt`; there is no declared coverage threshold. Add focused tests as `tests/.c`, create a matching `test_` target, and register it with `add_test`. Put reusable fixtures in `tests/assets/` and keep paths compatible with tests launched from the build tree. +Tests use simple executable return codes and are registered through CTest in `CMakeLists.txt`; there is no declared coverage threshold. Add focused tests as `tests/.c`, create a matching `test_` target, and register it with `add_test`. Put reusable fixtures in `tests/assets/` and keep paths compatible with tests launched from the build tree. Coverage mode wraps the suite in a CTest fixture so counters are reset before tests and reports are generated afterward. ## Commit & Pull Request Guidelines diff --git a/CMakeLists.txt b/CMakeLists.txt index f86c2ae..511fcce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,14 @@ cmake_minimum_required(VERSION 3.10) project(akgl LANGUAGES C) include(CTest) +option(AKGL_COVERAGE "Instrument libakgl and generate coverage reports with CTest" OFF) + +if(AKGL_COVERAGE) + if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + message(FATAL_ERROR "AKGL_COVERAGE requires GCC or Clang") + endif() + find_program(GCOVR_EXECUTABLE gcovr REQUIRED) +endif() if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) @@ -141,6 +149,44 @@ target_include_directories(akgl PUBLIC deps/semver/ ) +if(AKGL_COVERAGE) + target_compile_options(akgl PRIVATE --coverage -O0 -g) + target_link_options(akgl PRIVATE --coverage) + + set(AKGL_COVERAGE_DIR "${CMAKE_CURRENT_BINARY_DIR}/coverage") + file(MAKE_DIRECTORY "${AKGL_COVERAGE_DIR}") + + add_test( + NAME coverage_reset + COMMAND ${GCOVR_EXECUTABLE} + --root "${CMAKE_CURRENT_SOURCE_DIR}" + --object-directory "${CMAKE_CURRENT_BINARY_DIR}" + --delete + ) + set_tests_properties(coverage_reset PROPERTIES FIXTURES_SETUP akgl_coverage) + set_tests_properties( + actor bitmasks character registry sprite staticstring tilemap util semver_unit + PROPERTIES FIXTURES_REQUIRED akgl_coverage + ) + + add_test( + NAME coverage_report + COMMAND ${GCOVR_EXECUTABLE} + --root "${CMAKE_CURRENT_SOURCE_DIR}" + --object-directory "${CMAKE_CURRENT_BINARY_DIR}" + --filter "${CMAKE_CURRENT_SOURCE_DIR}/src/" + --xml-pretty + --xml "${AKGL_COVERAGE_DIR}/coverage.xml" + --html-details "${AKGL_COVERAGE_DIR}/index.html" + ) + set_tests_properties( + coverage_report + PROPERTIES + FIXTURES_CLEANUP akgl_coverage + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + ) +endif() + target_link_libraries(akgl PUBLIC SDL3::SDL3