easy unit testing for C in a single header file
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

552 lines
14KB

  1. #ifndef SIMPLE_TEST_H
  2. #define SIMPLE_TEST_H
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <stdbool.h>
  6. #include <stdint.h>
  7. #include <inttypes.h>
  8. #include <stdarg.h>
  9. #include <string.h>
  10. /**************************
  11. * CONFIGURATION MACROS *
  12. **************************/
  13. #ifndef SIMPLE_TEST_USE_COLOUR
  14. #define SIMPLE_TEST_USE_COLOUR true
  15. #endif
  16. #ifndef SIMPLE_TEST_PRINT_FUN
  17. #define SIMPLE_TEST_PRINT_FUN printf
  18. #endif
  19. /**************************************************
  20. * INTERNAL FUNCTIONALITY. DO NOT CALL DIRECTLY *
  21. **************************************************/
  22. enum simple_test_type {
  23. SIMPLE_TEST_BOOL,
  24. SIMPLE_TEST_INT,
  25. SIMPLE_TEST_UNSIGNED,
  26. SIMPLE_TEST_CHAR,
  27. SIMPLE_TEST_STRING,
  28. SIMPLE_TEST_POINTER,
  29. SIMPLE_TEST_UNKNOWN,
  30. SIMPLE_TEST_MISMATCH,
  31. };
  32. enum simple_test_cond {
  33. SIMPLE_TEST_TRUE,
  34. SIMPLE_TEST_FALSE,
  35. SIMPLE_TEST_EQ,
  36. SIMPLE_TEST_NEQ,
  37. SIMPLE_TEST_GT,
  38. SIMPLE_TEST_LT,
  39. SIMPLE_TEST_GEQ,
  40. SIMPLE_TEST_LEQ
  41. };
  42. static intmax_t simple_test_il, simple_test_ir;
  43. static uintmax_t simple_test_ul, simple_test_ur;
  44. static char *simple_test_sl, *simple_test_sr;
  45. static void *simple_test_pl, *simple_test_pr;
  46. static int simple_test_assert(enum simple_test_type t,
  47. enum simple_test_cond c, ...)
  48. {
  49. va_list ap;
  50. bool two = !(c == SIMPLE_TEST_TRUE || c == SIMPLE_TEST_FALSE);
  51. int r;
  52. va_start(ap, c);
  53. switch (t) {
  54. case SIMPLE_TEST_MISMATCH:
  55. return -2;
  56. case SIMPLE_TEST_BOOL:
  57. case SIMPLE_TEST_INT:
  58. case SIMPLE_TEST_CHAR:
  59. simple_test_il = va_arg(ap, intmax_t);
  60. if (two) simple_test_ir = va_arg(ap, intmax_t);
  61. switch (c) {
  62. case SIMPLE_TEST_TRUE:
  63. r = simple_test_il;
  64. goto end;
  65. case SIMPLE_TEST_FALSE:
  66. r = !simple_test_il;
  67. goto end;
  68. case SIMPLE_TEST_EQ:
  69. r = simple_test_il == simple_test_ir;
  70. goto end;
  71. case SIMPLE_TEST_NEQ:
  72. r = simple_test_il != simple_test_ir;
  73. goto end;
  74. case SIMPLE_TEST_GT:
  75. r = simple_test_il > simple_test_ir;
  76. goto end;
  77. case SIMPLE_TEST_LT:
  78. r = simple_test_il < simple_test_ir;
  79. goto end;
  80. case SIMPLE_TEST_GEQ:
  81. r = simple_test_il >= simple_test_ir;
  82. goto end;
  83. case SIMPLE_TEST_LEQ:
  84. r = simple_test_il <= simple_test_ir;
  85. goto end;
  86. }
  87. case SIMPLE_TEST_UNSIGNED:
  88. simple_test_ul = va_arg(ap, uintmax_t);
  89. if (two) simple_test_ur = va_arg(ap, uintmax_t);
  90. switch (c) {
  91. case SIMPLE_TEST_TRUE:
  92. r = simple_test_ul;
  93. goto end;
  94. case SIMPLE_TEST_FALSE:
  95. r = !simple_test_ul;
  96. goto end;
  97. case SIMPLE_TEST_EQ:
  98. r = simple_test_ul == simple_test_ur;
  99. goto end;
  100. case SIMPLE_TEST_NEQ:
  101. r = simple_test_ul != simple_test_ur;
  102. goto end;
  103. case SIMPLE_TEST_GT:
  104. r = simple_test_ul > simple_test_ur;
  105. goto end;
  106. case SIMPLE_TEST_LT:
  107. r = simple_test_ul < simple_test_ur;
  108. goto end;
  109. case SIMPLE_TEST_GEQ:
  110. r = simple_test_ul >= simple_test_ur;
  111. goto end;
  112. case SIMPLE_TEST_LEQ:
  113. r = simple_test_ul <= simple_test_ur;
  114. goto end;
  115. }
  116. case SIMPLE_TEST_STRING:
  117. simple_test_sl = va_arg(ap, char *);
  118. if (two) simple_test_sr = va_arg(ap, char *);
  119. switch (c) {
  120. case SIMPLE_TEST_TRUE:
  121. r = simple_test_sl != NULL;
  122. goto end;
  123. case SIMPLE_TEST_FALSE:
  124. r = simple_test_sl == NULL;
  125. goto end;
  126. case SIMPLE_TEST_EQ:
  127. if (simple_test_sl == NULL || simple_test_sr == NULL)
  128. r = simple_test_sl == simple_test_sr;
  129. else
  130. r = strcmp(simple_test_sl, simple_test_sr) ? 0 : 1;
  131. goto end;
  132. case SIMPLE_TEST_NEQ:
  133. if (simple_test_sl == NULL || simple_test_sr == NULL)
  134. r = simple_test_sl != simple_test_sr;
  135. else
  136. r = strcmp(simple_test_sl, simple_test_sr) ? 1 : 0;
  137. goto end;
  138. case SIMPLE_TEST_GT:
  139. if (simple_test_sl == NULL || simple_test_sr == NULL)
  140. r = false;
  141. else
  142. r = strcmp(simple_test_sl, simple_test_sr) > 0 ? 1 : 0;
  143. goto end;
  144. case SIMPLE_TEST_LT:
  145. if (simple_test_sl == NULL || simple_test_sr == NULL)
  146. r = false;
  147. else
  148. r = strcmp(simple_test_sl, simple_test_sr) < 0 ? 1 : 0;
  149. goto end;
  150. case SIMPLE_TEST_GEQ:
  151. if (simple_test_sl == NULL || simple_test_sr == NULL)
  152. r = false;
  153. else
  154. r = strcmp(simple_test_sl, simple_test_sr) >= 0 ? 1 : 0;
  155. goto end;
  156. case SIMPLE_TEST_LEQ:
  157. if (simple_test_sl == NULL || simple_test_sr == NULL)
  158. r = false;
  159. else
  160. r = strcmp(simple_test_sl, simple_test_sr) <= 0 ? 1 : 0;
  161. goto end;
  162. }
  163. case SIMPLE_TEST_POINTER:
  164. simple_test_pl = va_arg(ap, void *);
  165. if (two) simple_test_pr = va_arg(ap, void *);
  166. switch (c) {
  167. case SIMPLE_TEST_TRUE:
  168. r = simple_test_pl != NULL;
  169. goto end;
  170. case SIMPLE_TEST_FALSE:
  171. r = simple_test_pl == NULL;
  172. goto end;
  173. case SIMPLE_TEST_EQ:
  174. r = simple_test_pl == simple_test_pr;
  175. goto end;
  176. case SIMPLE_TEST_NEQ:
  177. r = simple_test_pl != simple_test_pr;
  178. goto end;
  179. case SIMPLE_TEST_GT:
  180. r = simple_test_pl > simple_test_pr;
  181. goto end;
  182. case SIMPLE_TEST_LT:
  183. r = simple_test_pl < simple_test_pr;
  184. goto end;
  185. case SIMPLE_TEST_GEQ:
  186. r = simple_test_pl >= simple_test_pr;
  187. goto end;
  188. case SIMPLE_TEST_LEQ:
  189. r = simple_test_pl <= simple_test_pr;
  190. goto end;
  191. }
  192. default:
  193. r = -1;
  194. }
  195. end:
  196. va_end(ap);
  197. return r;
  198. }
  199. static enum simple_test_type simple_test_type_resolve(enum simple_test_type t1,
  200. enum simple_test_type t2)
  201. {
  202. if (t1 != t2) {
  203. if ((t1 == SIMPLE_TEST_INT && t2 == SIMPLE_TEST_UNSIGNED)
  204. || (t1 == SIMPLE_TEST_UNSIGNED && t2 == SIMPLE_TEST_INT)
  205. || (t1 == SIMPLE_TEST_BOOL && t2 == SIMPLE_TEST_UNSIGNED)
  206. || (t1 == SIMPLE_TEST_UNSIGNED && t2 == SIMPLE_TEST_BOOL)) {
  207. return SIMPLE_TEST_UNSIGNED;
  208. } else if ((t1 == SIMPLE_TEST_BOOL && t2 == SIMPLE_TEST_INT)
  209. || (t1 == SIMPLE_TEST_INT && t2 == SIMPLE_TEST_BOOL)) {
  210. return SIMPLE_TEST_INT;
  211. } else if ((t1 == SIMPLE_TEST_INT && t2 == SIMPLE_TEST_CHAR)
  212. || (t1 == SIMPLE_TEST_CHAR && t2 == SIMPLE_TEST_INT)) {
  213. return SIMPLE_TEST_CHAR;
  214. } else if ((t1 == SIMPLE_TEST_CHAR && t2 == SIMPLE_TEST_UNSIGNED)
  215. || (t1 == SIMPLE_TEST_UNSIGNED && t2 == SIMPLE_TEST_CHAR)) {
  216. return SIMPLE_TEST_UNSIGNED;
  217. } else if ((t1 == SIMPLE_TEST_POINTER && t2 == SIMPLE_TEST_STRING)
  218. || (t1 == SIMPLE_TEST_STRING && t2 == SIMPLE_TEST_POINTER)) {
  219. return SIMPLE_TEST_POINTER;
  220. } else {
  221. return SIMPLE_TEST_MISMATCH;
  222. }
  223. }
  224. return t1;
  225. }
  226. #define SIMPLE_TEST_TYPE(a) \
  227. _Generic((a), \
  228. bool: SIMPLE_TEST_BOOL, \
  229. char: SIMPLE_TEST_CHAR, \
  230. int8_t: SIMPLE_TEST_INT, \
  231. int16_t: SIMPLE_TEST_INT, \
  232. int32_t: SIMPLE_TEST_INT, \
  233. int64_t: SIMPLE_TEST_INT, \
  234. uint8_t: SIMPLE_TEST_UNSIGNED, \
  235. uint16_t: SIMPLE_TEST_UNSIGNED, \
  236. uint32_t: SIMPLE_TEST_UNSIGNED, \
  237. uint64_t: SIMPLE_TEST_UNSIGNED, \
  238. char *: SIMPLE_TEST_STRING, \
  239. void *: SIMPLE_TEST_POINTER, \
  240. default: SIMPLE_TEST_UNKNOWN)
  241. #define SIMPLE_TEST_FAIL1(...) \
  242. do { \
  243. if (SIMPLE_TEST_USE_COLOUR) { \
  244. SIMPLE_TEST_PRINT_FUN( \
  245. "\x1B[1m%*c :: at line %d, \x1B[m\x1B[1;31mfail: \x1B[m", \
  246. simple_test_pad_width, ' ', __LINE__); \
  247. } else { \
  248. SIMPLE_TEST_PRINT_FUN( \
  249. "%*c :: at line %d, fail: ", \
  250. simple_test_pad_width, ' ', __LINE__); \
  251. } \
  252. SIMPLE_TEST_PRINT_FUN( \
  253. __VA_ARGS__); \
  254. SIMPLE_TEST_PRINT_FUN( \
  255. "\n"); \
  256. } while (0)
  257. #define SIMPLE_TEST_PRINT_VAL(w, s, t) \
  258. do { \
  259. if (SIMPLE_TEST_USE_COLOUR) { \
  260. SIMPLE_TEST_PRINT_FUN( \
  261. "\x1B[1m%*c :: ", simple_test_pad_width, ' '); \
  262. SIMPLE_TEST_PRINT_FUN( \
  263. "`%s` \x1B[m== \x1B[1m", s); \
  264. } else { \
  265. SIMPLE_TEST_PRINT_FUN( \
  266. "%*c :: ", simple_test_pad_width, ' '); \
  267. SIMPLE_TEST_PRINT_FUN( \
  268. "`%s` == ", s); \
  269. } \
  270. switch (t) { \
  271. case SIMPLE_TEST_BOOL: \
  272. SIMPLE_TEST_PRINT_FUN( \
  273. "%s", (w ? simple_test_il : simple_test_ir) \
  274. ? "true" : "false"); \
  275. break; \
  276. case SIMPLE_TEST_CHAR: \
  277. SIMPLE_TEST_PRINT_FUN( \
  278. "%c", w ? (char)simple_test_il : (char)simple_test_ir); \
  279. break; \
  280. case SIMPLE_TEST_INT: \
  281. SIMPLE_TEST_PRINT_FUN( \
  282. "%" PRIdMAX, w ? simple_test_il : simple_test_ir); \
  283. break; \
  284. case SIMPLE_TEST_UNSIGNED: \
  285. SIMPLE_TEST_PRINT_FUN( \
  286. "%" PRIuMAX, w ? simple_test_ul : simple_test_ur); \
  287. break; \
  288. case SIMPLE_TEST_STRING: \
  289. SIMPLE_TEST_PRINT_FUN( \
  290. ((w ? simple_test_sl : simple_test_sr) \
  291. ? "\"%s\"" : "%s"), w \
  292. ? simple_test_sl : simple_test_sr); \
  293. break; \
  294. case SIMPLE_TEST_POINTER: \
  295. SIMPLE_TEST_PRINT_FUN( \
  296. "%p", w ? simple_test_pl : simple_test_pr); \
  297. break; \
  298. default: \
  299. break; \
  300. } \
  301. if (SIMPLE_TEST_USE_COLOUR) { \
  302. SIMPLE_TEST_PRINT_FUN("\x1B[m\n"); \
  303. } else { \
  304. SIMPLE_TEST_PRINT_FUN("\n"); \
  305. } \
  306. } while (0)
  307. #define SIMPLE_TEST_FAIL2 \
  308. do { \
  309. simple_test_fail_count++; \
  310. goto simple_test_loop_end; \
  311. } while (0)
  312. #define SIMPLE_TEST_ERR(...) \
  313. do { \
  314. if (SIMPLE_TEST_USE_COLOUR) { \
  315. SIMPLE_TEST_PRINT_FUN( \
  316. "\x1B[1m%*c :: at line %d, \x1B[m\x1B[1;31merr: \x1B[m", \
  317. simple_test_pad_width, ' ', __LINE__); \
  318. } else { \
  319. SIMPLE_TEST_PRINT_FUN( \
  320. "%*c :: at line %d, err: ", \
  321. simple_test_pad_width, ' ', __LINE__); \
  322. } \
  323. SIMPLE_TEST_PRINT_FUN(__VA_ARGS__); \
  324. SIMPLE_TEST_PRINT_FUN("\n"); \
  325. if (SIMPLE_TEST_USE_COLOUR) { \
  326. SIMPLE_TEST_PRINT_FUN("\x1B[1;31mtesting aborted\x1B[m\n"); \
  327. } else { \
  328. SIMPLE_TEST_PRINT_FUN("testing aborted\n"); \
  329. } \
  330. exit(1); \
  331. } while (0)
  332. #define SIMPLE_TEST_PRINT_BUF_WIDTH 512
  333. #define SIMPLE_TEST_ASSERT1(t, c, s, a) \
  334. do { \
  335. switch ( simple_test_assert(t, c, (a)) ) { \
  336. case 1: break; \
  337. case 0: \
  338. SIMPLE_TEST_FAIL1(s " failed"); \
  339. SIMPLE_TEST_PRINT_VAL(0, #a, t); \
  340. SIMPLE_TEST_FAIL2; \
  341. break; \
  342. default: \
  343. SIMPLE_TEST_ERR("unrecognised type in assertion"); \
  344. } \
  345. } while (0)
  346. #define SIMPLE_TEST_ASSERT2(t1, t2, c, s, a, b) \
  347. do { \
  348. switch ( simple_test_assert(simple_test_type_resolve(t1, t2), \
  349. c, (a), (b)) ) { \
  350. case 1: break; \
  351. case 0: \
  352. SIMPLE_TEST_FAIL1(s " failed"); \
  353. SIMPLE_TEST_PRINT_VAL(0, #a, simple_test_type_resolve(t1, t2)); \
  354. SIMPLE_TEST_PRINT_VAL(1, #b, simple_test_type_resolve(t1, t2)); \
  355. SIMPLE_TEST_FAIL2; \
  356. break; \
  357. case -2: \
  358. SIMPLE_TEST_ERR("type mismatch in assertion"); \
  359. default: \
  360. SIMPLE_TEST_ERR("unrecognised type in assertion"); \
  361. } \
  362. } while (0)
  363. /******************
  364. * BASIC MACROS *
  365. ******************/
  366. #define TEARDOWN(f) \
  367. do { \
  368. simple_test_teardown = _Generic((f), \
  369. void(*)(void): (f), default: NULL); \
  370. if (simple_test_teardown == NULL) { \
  371. SIMPLE_TEST_ERR( \
  372. "wrongly-typed function passed to REGISTER_TEARDOWN"); \
  373. } \
  374. } while (0)
  375. /* must appear before all tests */
  376. #define BEGIN_TEST \
  377. int main(int argc, char **argv) \
  378. { \
  379. int simple_test_iterator; \
  380. int simple_test_pad_width = 0; \
  381. int simple_test_fail_count = 0; \
  382. char simple_test_print_buf[SIMPLE_TEST_PRINT_BUF_WIDTH]; \
  383. int simple_test_test_count = 0; \
  384. int simple_test_test_current = 1; \
  385. int simple_test_test_current_at; \
  386. int simple_test_pass_number = 0; \
  387. void (*simple_test_teardown)(void) = NULL; \
  388. do { \
  389. simple_test_test_current_at = 0; \
  390. if (simple_test_pass_number == 0) { \
  391. if (SIMPLE_TEST_USE_COLOUR) { \
  392. SIMPLE_TEST_PRINT_FUN( \
  393. "\x1B[1mstarting tests in " __FILE__ "...\n"); \
  394. } else { \
  395. SIMPLE_TEST_PRINT_FUN( \
  396. "starting tests in " __FILE__ "...\n"); \
  397. } \
  398. } else { \
  399. simple_test_pad_width = sprintf(simple_test_print_buf, \
  400. "%d", simple_test_test_count) + 1; \
  401. } \
  402. for (simple_test_iterator = 0; simple_test_pass_number == 0 && \
  403. simple_test_iterator < 1; simple_test_iterator++) { \
  404. (void)0; \
  405. #define TEST(description) \
  406. } \
  407. if (simple_test_teardown != NULL) { \
  408. simple_test_teardown(); \
  409. simple_test_teardown = NULL; \
  410. } \
  411. simple_test_test_current_at++; \
  412. if (simple_test_pass_number == 0) { \
  413. simple_test_test_count++; \
  414. } else if (simple_test_pass_number \
  415. == simple_test_test_current_at) { \
  416. if (SIMPLE_TEST_USE_COLOUR) { \
  417. SIMPLE_TEST_PRINT_FUN( \
  418. "\x1B[m%*d \x1B[1m:: \x1B[m\x1B[33m%s\x1B[m\n", \
  419. simple_test_pad_width, \
  420. simple_test_test_current++, description); \
  421. } else { \
  422. SIMPLE_TEST_PRINT_FUN( \
  423. "%*d :: %s\n", \
  424. simple_test_pad_width, \
  425. simple_test_test_current++, description); \
  426. } \
  427. /* must appear after all tests */
  428. #define END_TEST \
  429. } \
  430. if (simple_test_teardown != NULL) { \
  431. simple_test_teardown(); \
  432. simple_test_teardown = NULL; \
  433. } \
  434. if (simple_test_test_count == 0) { \
  435. SIMPLE_TEST_ERR("no tests defined"); \
  436. } \
  437. simple_test_loop_end: \
  438. (void)0; \
  439. } while (simple_test_pass_number++ < simple_test_test_count); \
  440. if (simple_test_fail_count) { \
  441. if (SIMPLE_TEST_USE_COLOUR) { \
  442. SIMPLE_TEST_PRINT_FUN( \
  443. "\x1B[1;31m%d of %d tests failed\x1B[m\n", \
  444. simple_test_fail_count, simple_test_test_count); \
  445. } else { \
  446. SIMPLE_TEST_PRINT_FUN( \
  447. "%d of %d tests failed\n", \
  448. simple_test_fail_count, simple_test_test_count); \
  449. } \
  450. return 1; \
  451. } else { \
  452. if (SIMPLE_TEST_USE_COLOUR) { \
  453. SIMPLE_TEST_PRINT_FUN("\x1B[1;32mall tests passed!\x1B[m\n"); \
  454. } else { \
  455. SIMPLE_TEST_PRINT_FUN("all tests passed!\n"); \
  456. } \
  457. return 0; \
  458. } \
  459. }
  460. #define ECHO(...) \
  461. do { \
  462. if (SIMPLE_TEST_USE_COLOUR) { \
  463. SIMPLE_TEST_PRINT_FUN( \
  464. "\x1B[1m%*c :: \x1B[m\x1B[34m", \
  465. simple_test_pad_width, ' '); \
  466. } else { \
  467. SIMPLE_TEST_PRINT_FUN( \
  468. "%*c :: ", \
  469. simple_test_pad_width, ' '); \
  470. } \
  471. SIMPLE_TEST_PRINT_FUN(__VA_ARGS__); \
  472. if (SIMPLE_TEST_USE_COLOUR) { \
  473. SIMPLE_TEST_PRINT_FUN("...\x1B[m\n"); \
  474. } else { \
  475. SIMPLE_TEST_PRINT_FUN("...\n"); \
  476. } \
  477. } while (0)
  478. /**********************
  479. * ASSERTION MACROS *
  480. **********************/
  481. #define ASSERT(a) \
  482. SIMPLE_TEST_ASSERT1(SIMPLE_TEST_TYPE((a)), SIMPLE_TEST_TRUE, "ASSERT", a)
  483. #define ASSERT_NOT(a) \
  484. SIMPLE_TEST_ASSERT1(SIMPLE_TEST_TYPE((a)), SIMPLE_TEST_FALSE, \
  485. "ASSERT_NOT", a)
  486. #define ASSERT_EQ(a, b) \
  487. SIMPLE_TEST_ASSERT2(SIMPLE_TEST_TYPE((a)), SIMPLE_TEST_TYPE((b)), \
  488. SIMPLE_TEST_EQ, "ASSERT_EQ", a, b)
  489. #define ASSERT_NEQ(a, b) \
  490. SIMPLE_TEST_ASSERT2(SIMPLE_TEST_TYPE((a)), SIMPLE_TEST_TYPE((b)), \
  491. SIMPLE_TEST_NEQ, "ASSERT_NEQ", a, b)
  492. #define ASSERT_GT(a, b) \
  493. SIMPLE_TEST_ASSERT2(SIMPLE_TEST_TYPE((a)), SIMPLE_TEST_TYPE((b)), \
  494. SIMPLE_TEST_GT, "ASSERT_GT", a, b)
  495. #define ASSERT_LT(a, b) \
  496. SIMPLE_TEST_ASSERT2(SIMPLE_TEST_TYPE((a)), SIMPLE_TEST_TYPE((b)), \
  497. SIMPLE_TEST_LT, "ASSERT_LT", a, b)
  498. #define ASSERT_GEQ(a, b) \
  499. SIMPLE_TEST_ASSERT2(SIMPLE_TEST_TYPE((a)), SIMPLE_TEST_TYPE((b)), \
  500. SIMPLE_TEST_GEQ, "ASSERT_GEQ", a, b)
  501. #define ASSERT_LEQ(a, b) \
  502. SIMPLE_TEST_ASSERT2(SIMPLE_TEST_TYPE((a)), SIMPLE_TEST_TYPE((b)), \
  503. SIMPLE_TEST_LEQ, "ASSERT_LEQ", a, b)
  504. #endif