Skip to content

Instantly share code, notes, and snippets.

@Qix-
Last active April 1, 2022 14:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Qix-/11319a07a37d447ee8518e21c1a48a4b to your computer and use it in GitHub Desktop.
Save Qix-/11319a07a37d447ee8518e21c1a48a4b to your computer and use it in GitHub Desktop.
Tiny test suite for C
/*
NOTE: I'm just including this here since it was
NOTE: useful for another project. There are some
NOTE: project-specific things in here; customize
NOTE: INIT_TEST, END_TEST and TEST to suite your
NOTE: needs.
*/
#ifndef TEST_SUITE_H
#define TEST_SUITE_H
#pragma once
/*
Tiny test suite.
Josh Junon, 2022.
This header file is released
into the public domain.
Credit is appreciated but
not required.
USAGE:
Define tests with
TEST(name) { body }
which should perform tests via
assert(some != expression);
then, in `int main()', list which ones
PASS(name);
or instead are expected to
FAIL(name, "with this assert");
FAIL()'s second parameter must be
a string, and can optionally start
and/or end with '*' to indicate
that the assert message should start
or end with a string.
'*' in the middle of the string is
treated literally (it is not a glob
nor a regular expression). They're
only honored at the beginning or end.
*/
#include <setjmp.h>
#include <stdio.h>
#include <string.h>
static jmp_buf assertbuf;
#ifdef assert
#undef assert
#endif
#define STRIZE_(_x)#_x
#define STRIZE(_x)STRIZE_(_x)
static const char * last_assert_msg = NULL;
static const char * last_assert = NULL;
#define assert(_expr) \
do { \
if (!(_expr)) { \
last_assert_msg = #_expr; \
last_assert = "assertion error: " STRIZE(__LINE__) ": " #_expr; \
longjmp(assertbuf, 1); \
} \
} while(0)
#define INIT_TEST \
test_ht_t Htest; \
ht_t *H = &Htest.ht; \
Htest.resize_calls = 0; \
Htest.last_resize_ht = NULL; \
Htest.last_resize_bytes = 0; \
Htest.ht.base = NULL; \
Htest.ht.length = 0; \
Htest.ht.width = 0; \
Htest.ht.resize = &notify_ht_resize
#define END_TEST \
do { \
if (H->base) { \
free(H->base); \
H->base = NULL; \
H->length = 0; \
} \
} while (0)
#define PASS(_name) \
do { \
INIT_TEST; \
if (setjmp(assertbuf)) { \
status = 1; \
fprintf(stderr, "\x1b[91;1mFAIL\x1b[m\n%s\n\n", last_assert); \
} else { \
last_assert = NULL; \
last_assert_msg = NULL; \
fputs(#_name " ... ", stderr); \
(_name)(H, &Htest); \
fputs("\x1b[92;1mOK\x1b[m\n", stderr); \
} \
END_TEST; \
} while (0)
#define FAIL(_name, _with) \
do { \
INIT_TEST; \
if (setjmp(assertbuf)) { \
const char *test_with = (_with); \
size_t test_with_len = strlen(test_with); \
size_t last_assert_msg_len = strlen(last_assert_msg); \
if (last_assert_msg == NULL) { \
fputs("\x1b[91;1mFAIL (asserted with NULL/empty message)\x1b[m\n", stderr); \
status = 1; \
} else { \
int passed = 0; \
if (test_with_len == 0) { \
passed = !*last_assert_msg; \
} else if ( \
(test_with_len == 1 && *test_with == '*') \
|| (test_with_len == 2 && test_with[0] == '*' && test_with[1] == '*') \
) { \
passed = 1; \
} else if (test_with[0] == '*' && test_with[test_with_len - 1] == '*') { \
char *copied = malloc(test_with_len - 1); \
if (copied == NULL) { \
fputs("\x1b[92;1mCRITICAL: FAILED TO MALLOC TEST STRING\x1b[m", stderr); \
fputs("\x1b[92;1mSomething went horribly wrong (are you out of memory?)\x1b[m", stderr); \
} else { \
strncpy(copied, &test_with[1], test_with_len - 2); \
passed = strstr(last_assert_msg, copied) != NULL; \
free(copied); \
} \
} else if (test_with[0] == '*') { \
passed = ( \
(test_with_len - 1) <= last_assert_msg_len \
&& strcmp(&test_with[1], &last_assert_msg[last_assert_msg_len - (test_with_len - 1)]) == 0 \
); \
} else if (test_with[test_with_len - 1] == '*') { \
passed = strncmp(test_with, last_assert_msg, test_with_len - 1) == 0; \
} else { \
passed = strcmp(test_with, last_assert_msg) == 0; \
} \
if (passed) { \
fprintf(stderr, "\x1b[92;1mOK (failed successfully)\x1b[m\n"); \
} else { \
fprintf( \
stderr, \
"\x1b[91;1mFAIL (wrong assert message)\x1b[m\n%s\n\n" \
"\x1b[31mgot: %s\x1b[m\n\x1b[32mexpected: %s\x1b[m" \
"\n\x1b[2mFAIL() on line %d\x1b[m\n\n", \
last_assert, \
last_assert_msg, \
(_with), \
__LINE__ \
); \
status = 1; \
} \
} \
} else { \
last_assert = NULL; \
last_assert_msg = NULL; \
fputs(#_name " ... ", stderr); \
(_name)(H, &Htest); \
fputs("\x1b[91;1mFAIL (expected assert)\x1b[m\n", stderr); \
status = 1; \
} \
END_TEST; \
} while (0)
#define TEST(_name) static void _name(ht_t *H, test_ht_t *Htest)
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment