portersky 8e0f4fc8d0 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.
2026-05-10 00:53:03 +02:00
2026-05-09 20:43:32 +02:00
2026-05-09 20:32:55 +02:00
2026-05-09 20:43:32 +02:00

ctdd

A C23 project wired for test-driven development using Unity and CMock.

All dependencies are fetched automatically via CMake FetchContent — no 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
gcovr ≥ 6.0 Coverage reports — optional (uv tool install gcovr)

Build

Configure:

cmake -S . -B build -G Ninja

Build:

ninja -C build

Test

Full Unity output with colors:

ninja -C build check

CTest summary only:

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.

Coverage

Configure with coverage instrumentation:

cmake -S . -B build-cov -G Ninja -DENABLE_COVERAGE=ON

Generate the HTML report:

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:

./build/main.exe

Linux / macOS:

./build/main

Adding a new module (TDD workflow)

1. Write the header

// ctdd/counter.h
#pragma once
int counter_increment(int value);

2. Write a failing test

// tests/test_counter.c
#include "unity.h"
#include "ctdd/counter.h"

void setUp(void) {}
void tearDown(void) {}

void test_increment_adds_one(void) {
    TEST_ASSERT_EQUAL_INT(2, counter_increment(1));
}

int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_increment_adds_one);
    return UNITY_END();
}

3. Register the test in tests/CMakeLists.txt

add_executable(test_counter test_counter.c)
target_include_directories(test_counter PRIVATE "${CMAKE_SOURCE_DIR}")
target_link_libraries(test_counter PRIVATE ctdd_counter Unity::Unity)
target_compile_features(test_counter PRIVATE c_std_23)
add_test(NAME test_counter COMMAND test_counter)
list(APPEND TEST_TARGETS test_counter)

4. Add a stub, confirm RED, then implement GREEN

Stub ctdd/counter.c with return 0;, run tests to see the failure, then implement the real logic and confirm they pass.

Mocking a dependency

If your module calls an external function (e.g. log_message), generate a mock from its header and add it to the test target:

# tests/CMakeLists.txt
add_executable(test_mymodule test_mymodule.c)
target_link_libraries(test_mymodule PRIVATE ctdd_mymodule Unity::Unity CMock::CMock)
target_compile_features(test_mymodule PRIVATE c_std_23)
cmock_generate_mock(test_mymodule "${CMAKE_SOURCE_DIR}/ctdd/logger.h")
add_test(NAME test_mymodule COMMAND test_mymodule)

CMock generates Mocklogger.h into the build directory. Include it and use the generated API in your test:

#include "Mocklogger.h"

void setUp(void)    { Mocklogger_Init(); }
void tearDown(void) { Mocklogger_Verify(); Mocklogger_Destroy(); }

void test_something_logs(void) {
    log_message_Expect("expected string");
    my_function_under_test();
}

The _Expect call records the expected argument. CMock verifies the actual argument matches at call time using string comparison, and Mocklogger_Verify confirms the expected number of calls were made.

S
Description
No description provided
Readme MIT 71 KiB
Languages
C 58.8%
CMake 41.2%