/** * @file test_utils.h * @brief Minimal test harness for libft unit tests. * * Header-only: each test_*.c translation unit gets its own copy * of the counters and helpers via static linkage. * * Output is compact: only failures are printed individually. * Each section gets a one-line summary, and a totals line is * printed at the end. */ #ifndef TEST_UTILS_H #define TEST_UTILS_H #include #include #include #include #include #include /* ── global counters ──────────────────────────────────────────────── */ static int _s_pass = 0; static int _s_fail = 0; static const char *_s_section_name = NULL; static int _s_section_pass = 0; static int _s_section_fail = 0; static int _s_had_sections = 0; /* volatile: prevent dead-store elimination when the return value of the tested function is never read. */ static volatile size_t _s_sink; static volatile int _s_sink_i; /* ── sections and assertions ──────────────────────────────────────── */ /** Print the current section's summary line and reset its counters. */ static void _s_section_end (void) { if (_s_section_name == NULL) return; if (_s_section_fail > 0) printf (" %-16s %d passed, %d failed\n", _s_section_name, _s_section_pass, _s_section_fail); else printf (" %-16s %d passed\n", _s_section_name, _s_section_pass); _s_section_name = NULL; _s_section_pass = 0; _s_section_fail = 0; } /** Close the previous section (if any) and open a new one. */ static void _s_section (const char *name) { _s_section_end (); _s_section_name = name; _s_had_sections = 1; } /** Record a pass (@p ok true) or print FAIL and record a failure. */ static void _s_check (const char *label, int ok) { if (ok) { _s_pass++; _s_section_pass++; } else { printf (" FAIL %s\n", label); _s_fail++; _s_section_fail++; } } /** Pass if @p got == @p expected; on failure print both values. */ static void _s_check_eq (const char *label, size_t got, size_t expected) { if (got == expected) { _s_pass++; _s_section_pass++; } else { printf (" FAIL %s: got %zu, expected %zu\n", label, got, expected); _s_fail++; _s_section_fail++; } } /** Pass if @p got == @p expected (int variant); print both on failure. */ static void _s_check_eq_int (const char *label, int got, int expected) { if (got == expected) { _s_pass++; _s_section_pass++; } else { printf (" FAIL %s: got %d, expected %d\n", label, got, expected); _s_fail++; _s_section_fail++; } } /* ── crash detection ──────────────────────────────────────────────── */ /** Run @p fn in a child process; return 1 if it was killed by a signal. */ static int _s_crashes (void (*fn) (void)) { pid_t pid; int status; pid = fork (); if (pid == 0) { fn (); _exit (0); } waitpid (pid, &status, 0); return (WIFSIGNALED (status)); } static void _s_check_both_crash (const char *label, void (*ft_fn) (void), void (*libc_fn) (void)) { int ft_crashed; int libc_crashed; ft_crashed = _s_crashes (ft_fn); libc_crashed = _s_crashes (libc_fn); if (ft_crashed == libc_crashed) { _s_pass++; _s_section_pass++; } else { printf (" FAIL %s: ft_crashed=%d libc_crashed=%d\n", label, ft_crashed, libc_crashed); _s_fail++; _s_section_fail++; } } /* ── crash-wrapper macros ─────────────────────────────────────────── */ /* pointer-returning expression → _s_sink */ #define _S_CRASH(tag, expr) \ static void _s_crash_##tag (void) { _s_sink = (size_t)(expr); } /* int-returning expression → _s_sink_i */ #define _S_CRASH_I(tag, expr) \ static void _s_crash_##tag (void) { _s_sink_i = (expr); } /* void expression (e.g. ft_bzero) */ #define _S_CRASH_V(tag, expr) \ static void _s_crash_##tag (void) { (expr); } /* needs a local char buf[N] before the expression */ #define _S_CRASH_BUF(tag, bufsz, expr) \ static void _s_crash_##tag (void) \ { \ char buf[bufsz]; \ (void)buf; \ (expr); \ } /* ── int-range comparison helper ─────────────────────────────────── */ /* Compare ft(i) vs libc(i) for i in [-1..255]. If normalize is true, compares !!ft(i) == !!libc(i) (for is* funcs). Otherwise exact comparison (for toupper/tolower). */ static void _s_test_int_range (const char *name, int (*ft) (int), int (*libc) (int), int normalize) { int i; int ft_r; int libc_r; int ok; ok = 1; for (i = -1; i <= 255; i++) { if (normalize) { ft_r = !!ft (i); libc_r = !!libc (i); } else { ft_r = ft (i); libc_r = libc (i); } if (ft_r != libc_r) { char label[64]; snprintf (label, sizeof (label), "%s(%d): ft=%d libc=%d", name, i, ft_r, libc_r); _s_check (label, 0); ok = 0; } } if (ok) _s_check (name, 1); } /* ── helpers and output ───────────────────────────────────────────── */ #define _S_RAND_ITERS 50 /** Normalize to -1/0/1 for sign comparison (memcmp, strncmp). */ static int _s_sign (int x) { if (x > 0) return (1); if (x < 0) return (-1); return (0); } static void _s_print_results (void) { _s_section_end (); if (_s_fail > 0) printf (" %d passed, %d failed\n", _s_pass, _s_fail); else if (!_s_had_sections) printf (" %d passed\n", _s_pass); } #endif