1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
#ifndef TEST_FRAMEWORK_H
#define TEST_FRAMEWORK_H
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
typedef struct
{
const char* current_suite;
const char* current_test;
const char* current_test_file_stack[16];
const char* current_test_func_stack[16];
uint32_t current_test_line_stack[16];
uint32_t current_test_stack_index;
int current_test_fail;
uint32_t fail_count;
uint32_t run_count;
time_t time_start;
} test_framework_state_t;
extern test_framework_state_t __test_framework_state;
extern const char* test_framework_basename(const char* path);
#define TEST_FRAMEWORK_DECLARATIONS() \
test_framework_state_t __test_framework_state; \
\
const char* test_framework_basename(const char* path) { \
const char* last_slash = path; \
while (*path) { \
if (*path == '/' || *path == '\\') last_slash = path + 1; \
++path; \
} \
return last_slash; \
}
#define TEST_FRAMEWORK_INIT() \
memset(&__test_framework_state, 0, sizeof(__test_framework_state)); \
__test_framework_state.time_start = time(0)
#define TEST_FRAMEWORK_SHUTDOWN() \
printf("\nDone. %d/%d passed in %u seconds\n", \
__test_framework_state.run_count - __test_framework_state.fail_count, __test_framework_state.run_count, \
(unsigned)(time(0) - __test_framework_state.time_start))
#define TEST_FRAMEWORK_PASSED() (__test_framework_state.fail_count == 0)
#define TEST_SUITE_BEGIN() \
__test_framework_state.current_suite = __func__; \
printf("%s ", __test_framework_state.current_suite); \
fflush(stdout)
#define TEST_SUITE_END() __test_framework_state.current_suite = 0; \
printf("\n"); \
fflush(stdout)
#define TEST_PUSH_CURRENT_LINE(func_name) \
__test_framework_state.current_test_file_stack[__test_framework_state.current_test_stack_index] = __FILE__; \
__test_framework_state.current_test_line_stack[__test_framework_state.current_test_stack_index] = __LINE__; \
__test_framework_state.current_test_func_stack[__test_framework_state.current_test_stack_index] = func_name; \
++__test_framework_state.current_test_stack_index;
#define TEST_POP_CURRENT_LINE() --__test_framework_state.current_test_stack_index;
#define TEST_INIT() \
__test_framework_state.current_test_stack_index = 0; \
TEST_PUSH_CURRENT_LINE(__func__); \
__test_framework_state.current_test_fail = 0; \
++__test_framework_state.run_count; \
printf("."); \
fflush(stdout);
#define TEST(func) \
__test_framework_state.current_test = #func; \
TEST_INIT() \
func();
#define TEST_PARAMS1(func, p1) \
__test_framework_state.current_test = #func "(" #p1 ")"; \
TEST_INIT() \
func(p1);
#define TEST_PARAMS2(func, p1, p2) \
__test_framework_state.current_test = #func "(" #p1 ", " #p2 ")"; \
TEST_INIT() \
func(p1, p2);
#define TEST_PARAMS3(func, p1, p2, p3) \
__test_framework_state.current_test = #func "(" #p1 ", " #p2 ", " #p3 ")"; \
TEST_INIT() \
func(p1, p2, p3);
#define TEST_PARAMS4(func, p1, p2, p3, p4) \
__test_framework_state.current_test = #func "(" #p1 ", " #p2 ", " #p3 ", " #p4 ")"; \
TEST_INIT() \
func(p1, p2, p3, p4);
#define TEST_PARAMS5(func, p1, p2, p3, p4, p5) \
__test_framework_state.current_test = #func "(" #p1 ", " #p2 ", " #p3 ", " #p4 ", " #p5 ")"; \
TEST_INIT() \
func(p1, p2, p3, p4, p5);
#define TEST_PARAMS6(func, p1, p2, p3, p4, p5, p6) \
__test_framework_state.current_test = #func "(" #p1 ", " #p2 ", " #p3 ", " #p4 ", " #p5 ", " #p6 ")"; \
TEST_INIT() \
func(p1, p2, p3, p4, p5, p6);
#define TEST_PARAMS7(func, p1, p2, p3, p4, p5, p6, p7) \
__test_framework_state.current_test = #func "(" #p1 ", " #p2 ", " #p3 ", " #p4 ", " #p5 ", " #p6 ", " #p7 ")"; \
TEST_INIT() \
func(p1, p2, p3, p4, p5, p6, p7);
#define TEST_PARAMS8(func, p1, p2, p3, p4, p5, p6, p7, p8) \
__test_framework_state.current_test = #func "(" #p1 ", " #p2 ", " #p3 ", " #p4 ", " #p5 ", " #p6 ", " #p7 ", " #p8 ")"; \
TEST_INIT() \
func(p1, p2, p3, p4, p5, p6, p7, p8);
#define TEST_PARAMS9(func, p1, p2, p3, p4, p5, p6, p7, p8, p9) \
__test_framework_state.current_test = #func "(" #p1 ", " #p2 ", " #p3 ", " #p4 ", " #p5 ", " #p6 ", " #p7 ", " #p8 ", " #p9 ")"; \
TEST_INIT() \
func(p1, p2, p3, p4, p5, p6, p7, p8, p9);
#define ASSERT_HELPER(func_call, func_name) { \
TEST_PUSH_CURRENT_LINE(func_name); \
func_call; \
TEST_POP_CURRENT_LINE(); \
if (__test_framework_state.current_test_fail) \
return; \
}
#define ASSERT_MESSAGE(message, ...) { \
uint32_t __stack_index; \
if (!__test_framework_state.current_test_fail) { \
__test_framework_state.current_test_fail = 1; \
++__test_framework_state.fail_count; \
fprintf(stderr, "\n* %s/%s (%s:%d)", __test_framework_state.current_suite, __test_framework_state.current_test, \
test_framework_basename(__test_framework_state.current_test_file_stack[0]), __test_framework_state.current_test_line_stack[0]); \
} \
for (__stack_index = 1; __stack_index < __test_framework_state.current_test_stack_index; ++__stack_index) { \
fprintf(stderr, "\n via %s (%s:%d)", \
__test_framework_state.current_test_func_stack[__stack_index], \
test_framework_basename(__test_framework_state.current_test_file_stack[__stack_index]), \
__test_framework_state.current_test_line_stack[__stack_index]); \
} \
fprintf(stderr, "\n "); \
fprintf(stderr, message "\n", ## __VA_ARGS__); \
fflush(stderr); \
}
#define ASSERT_FAIL(message, ...) { ASSERT_MESSAGE(message, ## __VA_ARGS__); return; }
#define ASSERT_COMPARE(value, compare, expected, type, format) { \
type __v = (type)(value); \
type __e = (type)(expected); \
if (!(__v compare __e)) { \
ASSERT_FAIL("Expected: " #value " " #compare " " #expected " (%s:%d)\n Found: " format " " #compare " " format, \
test_framework_basename(__FILE__), __LINE__, __v, __e); \
} \
}
#define ASSERT_NUM_EQUALS(value, expected) ASSERT_COMPARE(value, ==, expected, int, "%d")
#define ASSERT_NUM_NOT_EQUALS(value, expected) ASSERT_COMPARE(value, !=, expected, int, "%d")
#define ASSERT_NUM_GREATER(value, expected) ASSERT_COMPARE(value, >, expected, int, "%d")
#define ASSERT_NUM_GREATER_EQUALS(value, expected) ASSERT_COMPARE(value, >=, expected, int, "%d")
#define ASSERT_NUM_LESS(value, expected) ASSERT_COMPARE(value, <, expected, int, "%d")
#define ASSERT_NUM_LESS_EQUALS(value, expected) ASSERT_COMPARE(value, <=, expected, int, "%d")
#define ASSERT_FLOAT_EQUALS(value, expected) { \
float __v = (float)value; \
float __e = (float)expected; \
double diff = (__v > __e) ? ((double)__v - (double)__e) : ((double)__e - (double)__v); \
if (diff >= 0.0000002) { /* FLT_EPSILON is ~1.19e-7 */ \
ASSERT_FAIL("Expected: " #value " = " #expected " (%s:%d)\n Found: %f = %f", \
test_framework_basename(__FILE__), __LINE__, __v, __e); \
} \
}
/* TODO: figure out some way to detect c89 so we can use int64_t and %lld on non-c89 builds */
#define ASSERT_NUM64_EQUALS(value, expected) ASSERT_COMPARE(value, ==, expected, int, "%d")
#define ASSERT_TRUE(value) ASSERT_NUM_NOT_EQUALS(value, 0)
#define ASSERT_FALSE(value) ASSERT_NUM_EQUALS(value, 0)
#define ASSERT_UNUM_EQUALS(value, expected) ASSERT_COMPARE(value, ==, expected, unsigned, "%u")
#define ASSERT_DBL_EQUALS(value, expected) ASSERT_COMPARE(value, ==, expected, double, "%g")
#define ASSERT_PTR_EQUALS(value, expected) ASSERT_COMPARE(value, ==, expected, void*, "%p")
#define ASSERT_PTR_NOT_NULL(value) ASSERT_COMPARE(value, !=, NULL, void*, "%p")
#define ASSERT_PTR_NULL(value) ASSERT_COMPARE(value, ==, NULL, void*, "%p")
#define ASSERT_STR_EQUALS(value, expected) { \
const char* __v = (const char*)(value); \
const char* __e = (const char*)(expected); \
if (strcmp(__v, __e) != 0) { \
ASSERT_FAIL( "String mismatch for: " #value " (%s:%d)\n Expected: %s\n Found: %s", test_framework_basename(__FILE__), __LINE__, __e, __v); \
}}
#define ASSERT_STR_NOT_EQUALS(value, expected) { \
const char* __v = (const char*)(value); \
const char* __e = (const char*)(expected); \
if (strcmp(__v, __e) == 0) { \
ASSERT_FAIL( "String match for: " #value " (%s:%d)\n Found, but not expected: %s", test_framework_basename(__FILE__), __LINE__, __v); \
}}
#endif /* TEST_FRAMEWORK_H */
|