diff options
Diffstat (limited to 'tests/inc/test_utils.h')
| -rw-r--r-- | tests/inc/test_utils.h | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/tests/inc/test_utils.h b/tests/inc/test_utils.h new file mode 100644 index 0000000..0cb6655 --- /dev/null +++ b/tests/inc/test_utils.h @@ -0,0 +1,246 @@ +/** + * @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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> +#include <time.h> +#include <unistd.h> + +/* ── 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 |
