15. A minimal test harness for C
C has no built-in test framework; most real projects either pull in something like Unity/Check/CMocka, or — for smaller libraries — write 30 lines of macros once and reuse them forever. Here's that 30 lines, because understanding it demystifies every "real" framework:
// test.h
#ifndef TEST_H
#define TEST_H
#include <stdio.h>
static int tests_run = 0, tests_failed = 0;
#define ASSERT_EQ(actual, expected) do { \
tests_run++; \
if ((actual) != (expected)) { \
tests_failed++; \
fprintf(stderr, "FAIL %s:%d: expected %ld, got %ld\n", \
__FILE__, __LINE__, (long)(expected), (long)(actual)); \
} \
} while (0)
#define TEST_SUMMARY() do { \
printf("%d/%d tests passed\n", tests_run - tests_failed, tests_run); \
return tests_failed ? 1 : 0; \
} while (0)
#endif
// test_add.c
#include "test.h"
int add(int a, int b) { return a + b; }
int main(void) {
ASSERT_EQ(add(2, 2), 4);
ASSERT_EQ(add(-1, 1), 0);
ASSERT_EQ(add(0, 0), 1); // intentionally wrong, to see a FAIL line
TEST_SUMMARY();
}
do { ... } while (0) wrapping a multi-statement macro is the standard
trick that makes ASSERT_EQ(x, y); behave like a single statement inside
an if without a trailing-semicolon or dangling-else bug — you'll see
this exact pattern in nearly every C macro that expands to more than one
expression.