60#define TEST_TIMEOUT_S 10
64static int test_failures_ = 0;
65static int test_checks_ = 0;
66static int test_cur_failed_ = 0;
67static int test_argc_ = 0;
68static char **test_argv_ = NULL;
69static const char *test_running_ =
"";
70static bool test_color_ =
false;
74static long test_live_allocs_ = 0;
75static long test_alloc_budget_ = -1;
81MULE_UNUSED
static void test_fail_alloc_after(
long n) {
82 test_alloc_budget_ = n;
87static bool test_alloc_allowed_(
void) {
88 if (test_alloc_budget_ == 0)
return false;
89 if (test_alloc_budget_ > 0) test_alloc_budget_--;
93MULE_UNUSED
static void *test_malloc_(
size_t n) {
94 if (!test_alloc_allowed_())
return NULL;
96 if (p != NULL) test_live_allocs_++;
100MULE_UNUSED
static void *test_calloc_(
size_t count,
size_t n) {
101 if (!test_alloc_allowed_())
return NULL;
102 void *p = calloc(count, n);
103 if (p != NULL) test_live_allocs_++;
107MULE_UNUSED
static void *test_realloc_(
void *old,
size_t n) {
108 if (!test_alloc_allowed_())
return NULL;
109 void *p = realloc(old, n);
110 if (p != NULL && old == NULL) test_live_allocs_++;
114MULE_UNUSED
static void test_free_(
void *p) {
115 if (p != NULL) test_live_allocs_--;
119#define malloc test_malloc_
120#define calloc test_calloc_
121#define realloc test_realloc_
122#define free test_free_
126static void test_fail_at_(
const char *file,
int line) {
127 test_cur_failed_ = 1;
131 printf(
" %s:%d: ", file, line);
135static void test_print_bytes_(
const char *p,
size_t n) {
141 for (
size_t i = 0; i < n; i++) {
142 unsigned char c = (
unsigned char)p[i];
143 if (c >= 0x20 && c < 0x7f && c !=
'"' && c !=
'\\')
146 printf(
"\\x%02x", c);
151MULE_UNUSED
static bool test_check_(
bool ok,
const char *file,
int line,
155 test_fail_at_(file, line);
156 printf(
"CHECK(%s) failed\n", expr);
161MULE_UNUSED
static bool test_eq_int_(
long long a,
long long b,
const char *file,
162 int line,
const char *ea,
const char *eb) {
165 test_fail_at_(file, line);
166 printf(
"%s == %s failed (%lld vs %lld)\n", ea, eb, a, b);
171MULE_UNUSED
static bool test_eq_str_(
const char *a,
const char *b,
172 const char *file,
int line,
const char *ea,
174 bool ok = (a == NULL || b == NULL) ? a == b : strcmp(a, b) == 0;
177 test_fail_at_(file, line);
178 printf(
"%s == %s failed (", ea, eb);
179 test_print_bytes_(a, a ? strlen(a) : 0);
181 test_print_bytes_(b, b ? strlen(b) : 0);
187MULE_UNUSED
static bool test_eq_mem_(
const char *a,
size_t alen,
const char *b,
188 size_t blen,
const char *file,
int line,
189 const char *ea,
const char *eb) {
190 bool ok = alen == blen && (alen == 0 || memcmp(a, b, alen) == 0);
193 test_fail_at_(file, line);
194 printf(
"%s == %s failed (", ea, eb);
195 test_print_bytes_(a, alen);
197 test_print_bytes_(b, blen);
206#define TEST(name) static void name(void)
209#define CHECK(cond) ((void)test_check_((cond), __FILE__, __LINE__, #cond))
212#define REQUIRE(cond) \
214 if (!test_check_((cond), __FILE__, __LINE__, #cond)) return; \
218#define CHECK_EQ_INT(a, b) \
219 ((void)test_eq_int_((long long)(a), (long long)(b), __FILE__, __LINE__, \
224#define CHECK_EQ_STR(a, b) \
225 ((void)test_eq_str_((a), (b), __FILE__, __LINE__, #a, #b))
229#define CHECK_EQ_MEM(a, alen, b, blen) \
230 ((void)test_eq_mem_((a), (alen), (b), (blen), __FILE__, __LINE__, #a, #b))
236static void test_timeout_(
int sig) {
238 static const char plain[] =
"[HANG] ";
241 write(STDOUT_FILENO, color,
sizeof color - 1);
243 write(STDOUT_FILENO, plain,
sizeof plain - 1);
244 write(STDOUT_FILENO, test_running_, strlen(test_running_));
245 write(STDOUT_FILENO,
"\n", 1);
252MULE_UNUSED
static void test_init(
int argc,
char **argv) {
256 signal(SIGALRM, test_timeout_);
260static void test_print_result_(
const char *name) {
261 const char *tag = test_cur_failed_ ?
"FAIL" :
" OK ";
266 printf(
"[%s] %s\n", tag, name);
269MULE_UNUSED
static int test_report_(
const char *file) {
271 printf(
"%s%s: %d check(s), %d failing test(s)%s\n", on, file, test_checks_,
272 test_failures_, test_color_ ?
LOG_RESET :
"");
273 return test_failures_ != 0;
276static bool test_selected_(
const char *name) {
277 if (test_argc_ <= 1)
return true;
278 for (
int i = 1; i < test_argc_; i++) {
279 if (strcmp(test_argv_[i], name) == 0)
return true;
290 if (!test_selected_(#name)) break; \
291 long live_before_ = test_live_allocs_; \
292 test_cur_failed_ = 0; \
293 test_running_ = #name; \
294 alarm(TEST_TIMEOUT_S); \
297 test_alloc_budget_ = -1; \
298 if (test_live_allocs_ != live_before_) { \
299 test_cur_failed_ = 1; \
300 printf(" %ld allocation(s) leaked\n", \
301 test_live_allocs_ - live_before_); \
303 if (test_cur_failed_) test_failures_++; \
304 test_print_result_(#name); \
309#define TEST_REPORT() test_report_(__FILE__)
#define LOG_YELLOW
Yellow text.
#define LOG_GREEN
Green text.
#define LOG_RESET
Back to the terminal's default style.
bool log_color_enabled(FILE *f)
True when colour escapes written to f will render as colour.