cbase 1.50.0
C/C++ Static Template
Loading...
Searching...
No Matches
test.h
Go to the documentation of this file.
1
64
65#ifndef TEST_H
66#define TEST_H
67
68#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <time.h>
72
73#if defined(_WIN32)
74# include <windows.h>
75
76static int
77_tr_enable_vt(HANDLE h)
78{
79 DWORD mode = 0;
80 if (!GetConsoleMode(h, &mode)) {
81 return 0;
82 }
83 return SetConsoleMode(h, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0;
84}
85
86static int _tr_colors_enabled = -1;
87
88static int
89_tr_use_colors(void)
90{
91 if (_tr_colors_enabled < 0) {
92 _tr_colors_enabled = _tr_enable_vt(GetStdHandle(STD_OUTPUT_HANDLE));
93 }
94 return _tr_colors_enabled;
95}
96#else
97static int
98_tr_use_colors(void)
99{
100 return 1;
101}
102#endif
103
104#define TR_RESET (_tr_use_colors() ? "\x1b[0m" : "")
105#define TR_BOLD (_tr_use_colors() ? "\x1b[1m" : "")
106#define TR_DIM (_tr_use_colors() ? "\x1b[2m" : "")
107#define TR_GREEN (_tr_use_colors() ? "\x1b[32m" : "")
108#define TR_RED (_tr_use_colors() ? "\x1b[31m" : "")
109#define TR_YELLOW (_tr_use_colors() ? "\x1b[33m" : "")
110#define TR_WHITE (_tr_use_colors() ? "\x1b[97m" : "")
111
112/*
113 * Failure Capture Buffer
114 *
115 * EXPECT() fires inside the test function. Rather than printing immediately
116 * (which interleaves with PASS/FAIL lines), failures are buffered and then
117 * printed indented beneath the failing test name.
118 */
119
120#define TR_MAX_FAILURES_PER_TEST 16
121#define TR_FAILURE_MSG_LEN 256
122
123typedef struct {
124 char msg[TR_FAILURE_MSG_LEN];
126
127static _TrFailureMsg _tr_fail_buf[TR_MAX_FAILURES_PER_TEST];
128static int _tr_fail_count = 0;
129
130static void
131_tr_record_failure(const char *file, int line, const char *expr)
132{
133 if (TR_MAX_FAILURES_PER_TEST <= _tr_fail_count) {
134 return;
135 }
136
137 /* Strip to filename only. */
138 const char *p = file;
139 const char *slash = strrchr(file, '/');
140 const char *bslash = strrchr(file, '\\');
141 if (slash && slash > p) {
142 p = slash + 1;
143 }
144 if (bslash && bslash > p) {
145 p = bslash + 1;
146 }
147
148 snprintf(_tr_fail_buf[_tr_fail_count].msg, TR_FAILURE_MSG_LEN, "%s:%d %s", p, line, expr);
149 _tr_fail_count++;
150}
151
152static int _tr_total = 0;
153static int _tr_passed = 0;
154static int _tr_failed = 0;
155static int _tr_suite_total = 0;
156static int _tr_suite_failed = 0;
157
161#define EXPECT(cond) \
162 do { \
163 if (!(cond)) { \
164 _tr_record_failure(__FILE__, __LINE__, #cond); \
165 _tr_failed++; \
166 } \
167 } while (0)
168
175#define EXPECT_FATAL(cond) \
176 do { \
177 if (!(cond)) { \
178 _tr_record_failure(__FILE__, __LINE__, #cond); \
179 _tr_failed++; \
180 printf(" %s[FATAL]%s %s:%d: %s\n", \
181 TR_RED, \
182 TR_RESET, \
183 __FILE__, \
184 __LINE__, \
185 #cond); \
186 abort(); \
187 } \
188 } while (0)
189
193#define EXPECT_STR8_EQ(a, b) EXPECT(str8_match((a), (b)))
194
195/*
196 * @defgroup test_framework Test Framework
197 * @{
198 */
199
205#define TEST_CASE(name) static void test_##name(void)
206
207typedef void (*_TrTestFn)(void);
208
223static void
224_tr_run_test(_TrTestFn fn, const char *name)
225{
226 _tr_fail_count = 0;
227 int fails_before = _tr_failed;
228
229 clock_t t0 = clock();
230 fn();
231 clock_t t1 = clock();
232 long ms = (long)(((double)(t1 - t0) / CLOCKS_PER_SEC) * 1000.0);
233
234 _tr_total++;
235 _tr_suite_total++;
236
237 if (_tr_failed == fails_before) {
238 _tr_passed++;
239 printf(" %sPASS%s %-34s %s(%ldms)%s\n",
240 TR_GREEN,
241 TR_RESET,
242 name,
243 TR_DIM,
244 ms,
245 TR_RESET);
246 } else {
247 _tr_suite_failed++;
248 printf(" %sFAIL%s %-34s %s(%ldms)%s\n",
249 TR_RED,
250 TR_RESET,
251 name,
252 TR_DIM,
253 ms,
254 TR_RESET);
255 for (int i = 0; i < _tr_fail_count; i++) {
256 printf(" %s%s%s\n", TR_YELLOW, _tr_fail_buf[i].msg, TR_RESET);
257 }
258 }
259}
260
269static void
270_tr_suite_begin(const char *name)
271{
272 _tr_suite_total = 0;
273 _tr_suite_failed = 0;
274 printf("\n %s%s%s\n", TR_BOLD, name, TR_RESET);
275}
276
284static void
286{ /* reserved for future suite-level summary */
287}
288
293static void
294tr_begin(const char *title)
295{
296 printf("\n%s %s%s\n", TR_BOLD, title, TR_RESET);
297}
298
303static int
305{
306 printf("\n%s Results%s\n", TR_BOLD, TR_RESET);
307 printf(" %s----------------------------------------%s\n", TR_DIM, TR_RESET);
308 printf(" %s%d tests%s %s%d passed%s %s%d failed%s\n\n",
309 TR_BOLD,
310 _tr_total,
311 TR_RESET,
312 TR_GREEN,
313 _tr_passed,
314 TR_RESET,
315 _tr_failed > 0 ? TR_RED : TR_DIM,
316 _tr_failed,
317 TR_RESET);
318 return (_tr_failed > 0) ? 1 : 0;
319}
320
330#define BEGIN_SUITE(name) \
331 do { \
332 _tr_suite_begin(name);
333
335#define END_SUITE \
336 _tr_suite_end(); \
337 } \
338 while (0)
339
346#define RUN_TEST(name) _tr_run_test(test_##name, #name)
347
349
350#endif // TEST_H
static void _tr_suite_begin(const char *name)
Prints the suite header and resets per-suite counters.
Definition test.h:270
static int tr_end(void)
Print the results summary. Return value is the process exit code.
Definition test.h:304
static void _tr_run_test(_TrTestFn fn, const char *name)
Executes a single test function, times it, and prints the result.
Definition test.h:224
static void tr_begin(const char *title)
Print the top-level banner. Call once at the start of main().
Definition test.h:294
static void _tr_suite_end(void)
Called after all tests in a suite have run.
Definition test.h:285