cutest.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. /*
  2. * CUTest -- C/C++ Unit Test facility
  3. * <http://github.com/mity/cutest>
  4. *
  5. * Copyright (c) 2013-2014 Martin Mitas
  6. *
  7. * This library is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as published by
  9. * the Free Software Foundation; either version 2.1 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License
  18. * along with this library; if not, write to the Free Software Foundation,
  19. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #ifndef CUTEST_H__
  22. #define CUTEST_H__
  23. /************************
  24. *** Public interface ***
  25. ************************/
  26. /* By default, <cutest.h> provides the main program entry point (function
  27. * main()). However, if the test suite is composed of multiple source files
  28. * which include <cutest.h>, then this brings a problem of multiple main()
  29. * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all
  30. * compilation units but one.
  31. */
  32. /* Macro to specify list of unit tests in the suite.
  33. * The unit test implementation MUST provide list of unit tests it implements
  34. * with this macro:
  35. *
  36. * TEST_LIST = {
  37. * { "test1_name", test1_func_ptr },
  38. * { "test2_name", test2_func_ptr },
  39. * ...
  40. * { 0 }
  41. * };
  42. *
  43. * The list specifies names of each tests (must be unique) and pointer to
  44. * a function implementing it. The function does not take any arguments
  45. * and have no return values, i.e. the test functions should have this
  46. * prototype:
  47. *
  48. * void test_func(void);
  49. */
  50. #define TEST_LIST const struct test__ test_list__[]
  51. /* Macros for testing whether an unit test succeeds or fails. These macros
  52. * can be used arbitrarily in functions implementing the unit tests.
  53. *
  54. * If any condition fails throughout execution of a test, the test fails.
  55. *
  56. * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows
  57. * also to specify an error message to print out if the condition fails.
  58. * (It expects printf-like format string and its parameters). The macros
  59. * return non-zero (condition passes) or 0 (condition fails).
  60. *
  61. * That can be useful when more conditions should be checked only if some
  62. * preceding condition passes, as illustrated here:
  63. *
  64. * SomeStruct* ptr = allocate_some_struct();
  65. * if(TEST_CHECK(ptr != NULL)) {
  66. * TEST_CHECK(ptr->member1 < 100);
  67. * TEST_CHECK(ptr->member2 > 200);
  68. * }
  69. */
  70. #define TEST_CHECK_(cond,...) test_check__((cond), __FILE__, __LINE__, __VA_ARGS__)
  71. #define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, "%s", #cond)
  72. /**********************
  73. *** Implementation ***
  74. **********************/
  75. /* The unit test files should not rely on anything below. */
  76. #include <stdarg.h>
  77. #include <stdio.h>
  78. #include <stdlib.h>
  79. #include <string.h>
  80. #if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__)
  81. #define CUTEST_UNIX__ 1
  82. #include <errno.h>
  83. #include <unistd.h>
  84. #include <sys/types.h>
  85. #include <sys/wait.h>
  86. #include <signal.h>
  87. #endif
  88. #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
  89. #define CUTEST_WIN__ 1
  90. #include <windows.h>
  91. #include <io.h>
  92. #endif
  93. #ifdef __cplusplus
  94. #include <exception>
  95. #endif
  96. /* Note our global private identifiers end with '__' to minimize risk of clash
  97. * with the unit tests implementation. */
  98. #ifdef __cplusplus
  99. extern "C" {
  100. #endif
  101. struct test__ {
  102. const char* name;
  103. void (*func)(void);
  104. };
  105. extern const struct test__ test_list__[];
  106. extern int test_verbose_level__;
  107. extern const struct test__* test_current_unit__;
  108. extern int test_current_already_logged__;
  109. extern int test_current_failures__;
  110. extern int test_colorize__;
  111. #define CUTEST_COLOR_DEFAULT__ 0
  112. #define CUTEST_COLOR_GREEN__ 1
  113. #define CUTEST_COLOR_RED__ 2
  114. #define CUTEST_COLOR_DEFAULT_INTENSIVE__ 3
  115. #define CUTEST_COLOR_GREEN_INTENSIVE__ 4
  116. #define CUTEST_COLOR_RED_INTENSIVE__ 5
  117. size_t
  118. test_print_in_color(int color, const char* fmt, ...)
  119. {
  120. va_list args;
  121. char buffer[256];
  122. size_t n;
  123. va_start(args, fmt);
  124. vsnprintf(buffer, sizeof(buffer), fmt, args);
  125. va_end(args);
  126. buffer[sizeof(buffer)-1] = '\0';
  127. if(!test_colorize__) {
  128. return printf("%s", buffer);
  129. }
  130. #if defined CUTEST_UNIX__
  131. const char* col_str;
  132. switch(color) {
  133. case CUTEST_COLOR_GREEN__: col_str = "\e[0;32m"; break;
  134. case CUTEST_COLOR_RED__: col_str = "\e[0;31m"; break;
  135. case CUTEST_COLOR_GREEN_INTENSIVE__: col_str = "\e[1;32m"; break;
  136. case CUTEST_COLOR_RED_INTENSIVE__: col_str = "\e[1;30m"; break;
  137. case CUTEST_COLOR_DEFAULT_INTENSIVE__: col_str = "\e[1m"; break;
  138. default: col_str = "\e[0m"; break;
  139. }
  140. printf("%s", col_str);
  141. n = printf("%s", buffer);
  142. printf("\e[0m");
  143. return n;
  144. #elif defined CUTEST_WIN__
  145. HANDLE h;
  146. CONSOLE_SCREEN_BUFFER_INFO info;
  147. WORD attr;
  148. h = GetStdHandle(STD_OUTPUT_HANDLE);
  149. GetConsoleScreenBufferInfo(h, &info);
  150. switch(color) {
  151. case CUTEST_COLOR_GREEN__: attr = FOREGROUND_GREEN; break;
  152. case CUTEST_COLOR_RED__: attr = FOREGROUND_RED; break;
  153. case CUTEST_COLOR_GREEN_INTENSIVE__: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break;
  154. case CUTEST_COLOR_RED_INTENSIVE__: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
  155. case CUTEST_COLOR_DEFAULT_INTENSIVE__: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break;
  156. default: attr = 0; break;
  157. }
  158. if(attr != 0)
  159. SetConsoleTextAttribute(h, attr);
  160. n = printf("%s", buffer);
  161. SetConsoleTextAttribute(h, info.wAttributes);
  162. return n;
  163. #else
  164. n = printf("%s", buffer);
  165. return n;
  166. #endif
  167. }
  168. int
  169. test_check__(int cond, const char* file, int line, const char* fmt, ...)
  170. {
  171. const char *result_str;
  172. int result_color;
  173. int verbose_level;
  174. if(cond) {
  175. result_str = "ok";
  176. result_color = CUTEST_COLOR_GREEN__;
  177. verbose_level = 3;
  178. } else {
  179. if(!test_current_already_logged__ && test_current_unit__ != NULL) {
  180. printf("[ ");
  181. test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__, "FAILED");
  182. printf(" ]\n");
  183. }
  184. result_str = "failed";
  185. result_color = CUTEST_COLOR_RED__;
  186. verbose_level = 2;
  187. test_current_failures__++;
  188. test_current_already_logged__++;
  189. }
  190. if(test_verbose_level__ >= verbose_level) {
  191. size_t n = 0;
  192. va_list args;
  193. printf(" ");
  194. if(file != NULL)
  195. n += printf("%s:%d: Check ", file, line);
  196. va_start(args, fmt);
  197. n += vprintf(fmt, args);
  198. va_end(args);
  199. printf("... ");
  200. test_print_in_color(result_color, result_str);
  201. printf("\n");
  202. test_current_already_logged__++;
  203. }
  204. return (cond != 0);
  205. }
  206. #ifndef TEST_NO_MAIN
  207. static char* test_argv0__ = NULL;
  208. static int test_count__ = 0;
  209. static int test_no_exec__ = 0;
  210. static int test_no_summary__ = 0;
  211. static int test_skip_mode__ = 0;
  212. static int test_stat_failed_units__ = 0;
  213. static int test_stat_run_units__ = 0;
  214. const struct test__* test_current_unit__ = NULL;
  215. int test_current_already_logged__ = 0;
  216. int test_verbose_level__ = 2;
  217. int test_current_failures__ = 0;
  218. int test_colorize__ = 0;
  219. static void
  220. test_list_names__(void)
  221. {
  222. const struct test__* test;
  223. printf("Unit tests:\n");
  224. for(test = &test_list__[0]; test->func != NULL; test++)
  225. printf(" %s\n", test->name);
  226. }
  227. static const struct test__*
  228. test_by_name__(const char* name)
  229. {
  230. const struct test__* test;
  231. for(test = &test_list__[0]; test->func != NULL; test++) {
  232. if(strcmp(test->name, name) == 0)
  233. return test;
  234. }
  235. return NULL;
  236. }
  237. static int
  238. test_do_run__(const struct test__* test)
  239. {
  240. test_current_unit__ = test;
  241. test_current_failures__ = 0;
  242. test_current_already_logged__ = 0;
  243. if(test_verbose_level__ >= 3) {
  244. test_print_in_color(CUTEST_COLOR_DEFAULT_INTENSIVE__, "Test %s:\n", test->name);
  245. test_current_already_logged__++;
  246. } else if(test_verbose_level__ >= 1) {
  247. size_t n;
  248. char spaces[32];
  249. n = test_print_in_color(CUTEST_COLOR_DEFAULT_INTENSIVE__, "Test %s... ", test->name);
  250. memset(spaces, ' ', sizeof(spaces));
  251. if(n < sizeof(spaces))
  252. printf("%.*s", (int) (sizeof(spaces) - n), spaces);
  253. } else {
  254. test_current_already_logged__ = 1;
  255. }
  256. #ifdef __cplusplus
  257. try {
  258. #endif
  259. test->func();
  260. #ifdef __cplusplus
  261. } catch(std::exception& e) {
  262. const char* what = e.what();
  263. if(what != NULL)
  264. test_check__(0, NULL, 0, "Threw std::exception: %s", what);
  265. else
  266. test_check__(0, NULL, 0, "Threw std::exception");
  267. } catch(...) {
  268. test_check__(0, NULL, 0, "Threw an exception");
  269. }
  270. #endif
  271. if(test_verbose_level__ >= 3) {
  272. switch(test_current_failures__) {
  273. case 0: test_print_in_color(CUTEST_COLOR_GREEN_INTENSIVE__, " All conditions have passed.\n\n"); break;
  274. case 1: test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__, " One condition has FAILED.\n\n"); break;
  275. default: test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__, " %d conditions have FAILED.\n\n", test_current_failures__); break;
  276. }
  277. } else if(test_verbose_level__ >= 1 && test_current_failures__ == 0) {
  278. printf("[ ");
  279. test_print_in_color(CUTEST_COLOR_GREEN_INTENSIVE__, "OK");
  280. printf(" ]\n");
  281. }
  282. test_current_unit__ = NULL;
  283. return (test_current_failures__ == 0) ? 0 : -1;
  284. }
  285. static void
  286. test_error__(const char* fmt, ...)
  287. {
  288. va_list args;
  289. if(!test_current_already_logged__ && test_current_unit__ != NULL) {
  290. printf("[ ");
  291. test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__, "FAILED");
  292. printf(" ]\n");
  293. }
  294. if(test_verbose_level__ < 2)
  295. return;
  296. test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__, " Error: ");
  297. va_start(args, fmt);
  298. vprintf(fmt, args);
  299. va_end(args);
  300. printf("\n");
  301. }
  302. static void
  303. test_run__(const struct test__* test)
  304. {
  305. int failed = 1;
  306. test_current_unit__ = test;
  307. test_current_already_logged__ = 0;
  308. if(!test_no_exec__) {
  309. #if defined(CUTEST_UNIX__)
  310. pid_t pid;
  311. int exit_code;
  312. pid = fork();
  313. if(pid == (pid_t)-1) {
  314. test_error__("Cannot fork. %s [%d]", strerror(errno), errno);
  315. failed = 1;
  316. } else if(pid == 0) {
  317. failed = (test_do_run__(test) != 0);
  318. exit(failed ? 1 : 0);
  319. } else {
  320. waitpid(pid, &exit_code, 0);
  321. if(WIFEXITED(exit_code)) {
  322. switch(WEXITSTATUS(exit_code)) {
  323. case 0: failed = 0; break; /* test has passed. */
  324. case 1: /* noop */ break; /* "normal" failure. */
  325. default: test_error__("Unexpected exit code [%d]", WEXITSTATUS(exit_code));
  326. }
  327. } else if(WIFSIGNALED(exit_code)) {
  328. char tmp[32];
  329. const char* signame;
  330. switch(WTERMSIG(exit_code)) {
  331. case SIGINT: signame = "SIGINT"; break;
  332. case SIGHUP: signame = "SIGHUP"; break;
  333. case SIGQUIT: signame = "SIGQUIT"; break;
  334. case SIGABRT: signame = "SIGABRT"; break;
  335. case SIGKILL: signame = "SIGKILL"; break;
  336. case SIGSEGV: signame = "SIGSEGV"; break;
  337. case SIGILL: signame = "SIGILL"; break;
  338. case SIGTERM: signame = "SIGTERM"; break;
  339. default: sprintf(tmp, "signal %d", WTERMSIG(exit_code)); signame = tmp; break;
  340. }
  341. test_error__("Test interrupted by %s", signame);
  342. } else {
  343. test_error__("Test ended in an unexpected way [%d]", exit_code);
  344. }
  345. }
  346. #elif defined(CUTEST_WIN__)
  347. char buffer[512] = {0};
  348. STARTUPINFOA startupInfo = {0};
  349. PROCESS_INFORMATION processInfo;
  350. DWORD exitCode;
  351. _snprintf(buffer, sizeof(buffer)-1,
  352. "%s --no-exec --no-summary --verbose=%d --color=%s -- \"%s\"",
  353. test_argv0__, test_verbose_level__,
  354. test_colorize__ ? "always" : "never", test->name);
  355. startupInfo.cb = sizeof(STARTUPINFO);
  356. if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) {
  357. WaitForSingleObject(processInfo.hProcess, INFINITE);
  358. GetExitCodeProcess(processInfo.hProcess, &exitCode);
  359. CloseHandle(processInfo.hThread);
  360. CloseHandle(processInfo.hProcess);
  361. failed = (exitCode != 0);
  362. } else {
  363. test_error__("Cannot create unit test subprocess [%ld].", GetLastError());
  364. failed = 1;
  365. }
  366. #else
  367. failed = (test_do_run__(test) != 0);
  368. #endif
  369. } else {
  370. failed = (test_do_run__(test) != 0);
  371. }
  372. test_current_unit__ = NULL;
  373. test_stat_run_units__++;
  374. if(failed)
  375. test_stat_failed_units__++;
  376. }
  377. #if defined(CUTEST_WIN__)
  378. static LONG CALLBACK
  379. test_exception_filter__(EXCEPTION_POINTERS *ptrs)
  380. {
  381. test_error__("Unhandled SEH exception %08lx at %p.",
  382. ptrs->ExceptionRecord->ExceptionCode,
  383. ptrs->ExceptionRecord->ExceptionAddress);
  384. fflush(stdout);
  385. fflush(stderr);
  386. return EXCEPTION_EXECUTE_HANDLER;
  387. }
  388. #endif
  389. static void
  390. test_help__(void)
  391. {
  392. printf("Usage: %s [options] [test...]\n", test_argv0__);
  393. printf("Run the specified unit tests; or if the option '--skip' is used, run all\n");
  394. printf("tests in the suite but those listed. By default, if no tests are specified\n");
  395. printf("on the command line, all unit tests in the suite are run.\n");
  396. printf("\n");
  397. printf("Options:\n");
  398. printf(" -s, --skip Execute all unit tests but the listed ones\n");
  399. printf(" --no-exec Do not execute unit tests as child processes\n");
  400. printf(" --no-summary Suppress printing of test results summary\n");
  401. printf(" -l, --list List unit tests in the suite and exit\n");
  402. printf(" -v, --verbose Enable more verbose output\n");
  403. printf(" --verbose=LEVEL Set verbose level to LEVEL (small integer)\n");
  404. printf(" --color=WHEN Enable colorized output (WHEN is one of 'auto', 'always', 'never')\n");
  405. printf(" -h, --help Display this help and exit\n");
  406. printf("\n");
  407. test_list_names__();
  408. }
  409. int
  410. main(int argc, char** argv)
  411. {
  412. const struct test__** tests = NULL;
  413. int i, j, n = 0;
  414. int seen_double_dash = 0;
  415. test_argv0__ = argv[0];
  416. #if defined CUTEST_UNIX__
  417. test_colorize__ = isatty(fileno(stdout));
  418. #elif defined CUTEST_WIN__
  419. test_colorize__ = _isatty(_fileno(stdout));
  420. #else
  421. test_colorize__ = 0;
  422. #endif
  423. /* Parse options */
  424. for(i = 1; i < argc; i++) {
  425. if(seen_double_dash || argv[i][0] != '-') {
  426. tests = (const struct test__**) realloc(tests, (n+1) * sizeof(const struct test__*));
  427. if(tests == NULL) {
  428. fprintf(stderr, "Out of memory.\n");
  429. exit(2);
  430. }
  431. tests[n] = test_by_name__(argv[i]);
  432. if(tests[n] == NULL) {
  433. fprintf(stderr, "%s: Unrecognized unit test '%s'\n", argv[0], argv[i]);
  434. fprintf(stderr, "Try '%s --list' for list of unit tests.\n", argv[0]);
  435. exit(2);
  436. }
  437. n++;
  438. } else if(strcmp(argv[i], "--") == 0) {
  439. seen_double_dash = 1;
  440. } else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
  441. test_help__();
  442. exit(0);
  443. } else if(strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) {
  444. test_verbose_level__++;
  445. } else if(strncmp(argv[i], "--verbose=", 10) == 0) {
  446. test_verbose_level__ = atoi(argv[i] + 10);
  447. } else if(strcmp(argv[i], "--color=auto") == 0) {
  448. /* noop (set from above) */
  449. } else if(strcmp(argv[i], "--color=always") == 0 || strcmp(argv[i], "--color") == 0) {
  450. test_colorize__ = 1;
  451. } else if(strcmp(argv[i], "--color=never") == 0) {
  452. test_colorize__ = 0;
  453. } else if(strcmp(argv[i], "--skip") == 0 || strcmp(argv[i], "-s") == 0) {
  454. test_skip_mode__ = 1;
  455. } else if(strcmp(argv[i], "--no-exec") == 0) {
  456. test_no_exec__ = 1;
  457. } else if(strcmp(argv[i], "--no-summary") == 0) {
  458. test_no_summary__ = 1;
  459. } else if(strcmp(argv[i], "--list") == 0 || strcmp(argv[i], "-l") == 0) {
  460. test_list_names__();
  461. exit(0);
  462. } else {
  463. fprintf(stderr, "%s: Unrecognized option '%s'\n", argv[0], argv[i]);
  464. fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
  465. exit(2);
  466. }
  467. }
  468. #if defined(CUTEST_WIN__)
  469. SetUnhandledExceptionFilter(test_exception_filter__);
  470. #endif
  471. /* Count all test units */
  472. test_count__ = 0;
  473. for(i = 0; test_list__[i].func != NULL; i++)
  474. test_count__++;
  475. /* Run the tests */
  476. if(n == 0) {
  477. /* Run all tests */
  478. for(i = 0; test_list__[i].func != NULL; i++)
  479. test_run__(&test_list__[i]);
  480. } else if(!test_skip_mode__) {
  481. /* Run the listed tests */
  482. for(i = 0; i < n; i++)
  483. test_run__(tests[i]);
  484. } else {
  485. /* Run all tests except those listed */
  486. int is_skipped;
  487. for(i = 0; test_list__[i].func != NULL; i++) {
  488. is_skipped = 0;
  489. for(j = 0; j < n; j++) {
  490. if(tests[j] == &test_list__[i]) {
  491. is_skipped = 1;
  492. break;
  493. }
  494. }
  495. if(!is_skipped)
  496. test_run__(&test_list__[i]);
  497. }
  498. }
  499. /* Write a summary */
  500. if(!test_no_summary__ && test_verbose_level__ >= 1) {
  501. test_print_in_color(CUTEST_COLOR_DEFAULT_INTENSIVE__, "\nSummary:\n");
  502. if(test_verbose_level__ >= 3) {
  503. printf(" Count of all unit tests: %4d\n", test_count__);
  504. printf(" Count of run unit tests: %4d\n", test_stat_run_units__);
  505. printf(" Count of failed unit tests: %4d\n", test_stat_failed_units__);
  506. printf(" Count of skipped unit tests: %4d\n", test_count__ - test_stat_run_units__);
  507. }
  508. if(test_stat_failed_units__ == 0) {
  509. test_print_in_color(CUTEST_COLOR_GREEN_INTENSIVE__,
  510. " SUCCESS: All unit tests have passed.\n");
  511. } else {
  512. test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__,
  513. " FAILED: %d of %d unit tests have failed.\n",
  514. test_stat_failed_units__, test_stat_run_units__);
  515. }
  516. }
  517. if(tests != NULL)
  518. free(tests);
  519. return (test_stat_failed_units__ == 0) ? 0 : 1;
  520. }
  521. #endif /* #ifndef TEST_NO_MAIN */
  522. #ifdef __cplusplus
  523. } /* extern "C" */
  524. #endif
  525. #endif /* #ifndef CUTEST_H__ */