test.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. * test.h - Michael Smith <mikesmiffy128@gmail.com>
  3. * I hereby dedicate the contents of this file to the public domain. In
  4. * jurisdictions with no public domain, go you your supreme court and get a
  5. * public domain.
  6. */
  7. /*
  8. * NOTE: This is a hacky black magic Windows port! If you only want Unix
  9. * support, the less-atrocious original resides in a Git repository:
  10. * https://gitlab.com/mikesmiffy128/test.h
  11. */
  12. #include <stdlib.h>
  13. #include <stdio.h>
  14. #include <stdbool.h>
  15. #include <string.h>
  16. #ifdef _WIN32
  17. #include <Windows.h>
  18. #else
  19. #include <unistd.h>
  20. #include <signal.h>
  21. #include <sys/wait.h>
  22. #include <sys/select.h>
  23. #endif
  24. #ifdef __clang__
  25. #define _TEST_SILENCE_CLANG \
  26. _Pragma("clang diagnostic push") \
  27. _Pragma("clang diagnostic ignored \"-Winitializer-overrides\"")
  28. #define _TEST_UNSILENCE_CLANG \
  29. _Pragma("clang diagnostic pop")
  30. #else
  31. #define _TEST_SILENCE_CLANG
  32. #define _TEST_UNSILENCE_CLANG
  33. #endif
  34. static struct _test_desc {
  35. char *desc;
  36. int default_flags;
  37. } _test_desc;
  38. static struct _test {
  39. char *desc;
  40. /* Optional attributes that can be set to further customise the test case */
  41. // Note: the first attribute here has to have a default value of 0 because
  42. // of __VA_ARGS__ requiring at least one argument
  43. int expected_exit; /* expected exit from forked child (255 is reserved!) */
  44. int flags; /* see flags below */
  45. int timeout; /* milisecond timeout on forked child (if forking) */
  46. bool (*_f)(void);
  47. struct _test *_next;
  48. } *_tests = 0, **_tests_tail = &_tests;
  49. static int _ntests = 0;
  50. /* Test flags - currently just NOFORK but you could add your own custom ones! */
  51. #define NOFORK 1
  52. #define _TEST_USE_DEFAULT_FLAGS -1 // indicator to use global default_flags
  53. #define _TEST_DEFAULT_TIMEOUT 1000 // 1s seems reasonable
  54. #define _TESTCAT1(a, b) a##b
  55. #define _TESTCAT(a, b) _TESTCAT1(a, b)
  56. #define _TESTSTR1(x) #x
  57. #define _TESTSTR(x) _TESTSTR1(x)
  58. #define TEST(desc_, ...) \
  59. static bool _TESTCAT(_test_f_, __LINE__)(void); \
  60. _TEST_SILENCE_CLANG \
  61. static struct _test _TESTCAT(_test_, __LINE__) = { \
  62. .flags = _TEST_USE_DEFAULT_FLAGS, \
  63. .timeout = _TEST_DEFAULT_TIMEOUT, \
  64. .desc = __FILE__":"_TESTSTR(__LINE__)": "desc_ __VA_OPT__(,) \
  65. __VA_ARGS__, \
  66. ._f = &_TESTCAT(_test_f_, __LINE__) \
  67. }; \
  68. _TEST_UNSILENCE_CLANG \
  69. /* constructor adds tests to the list tail to run them in order */ \
  70. __attribute__((constructor(100 + __LINE__))) \
  71. static void _TESTCAT(_test_init_, __LINE__)(void) { \
  72. if (_TESTCAT(_test_, __LINE__).flags == _TEST_USE_DEFAULT_FLAGS) { \
  73. _TESTCAT(_test_, __LINE__).flags = _test_desc.default_flags; \
  74. } \
  75. _TESTCAT(_test_, __LINE__)._next = *_tests_tail; \
  76. *_tests_tail = &_TESTCAT(_test_, __LINE__); \
  77. _tests_tail = &_TESTCAT(_test_, __LINE__)._next; \
  78. ++_ntests; \
  79. } \
  80. static bool _TESTCAT(_test_f_, __LINE__)(void)
  81. #ifdef _WIN32
  82. // since we can't fork, we CreateProcess ourselves and use WriteProcessMemory
  83. // to set this function pointer to call the test we want to call
  84. static volatile bool (*_test_entry_f)(void) = 0;
  85. unsigned short _test_exepath[MAX_PATH];
  86. #define _test_perror_win(thing) do { \
  87. char err[128]; \
  88. FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), \
  89. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err, sizeof(err), 0); \
  90. fprintf(stderr, thing ": %s\n", err); \
  91. return false; \
  92. } while (0)
  93. #else
  94. static sigset_t _test_sigmask = {0};
  95. #endif
  96. static bool _run_test(struct _test *t) {
  97. if (t->flags & NOFORK) return t->_f();
  98. #ifdef _WIN32
  99. STARTUPINFOW startinfo = {0};
  100. PROCESS_INFORMATION info;
  101. if (!CreateProcessW(_test_exepath, L"", 0, 0, 1, CREATE_SUSPENDED, 0, 0,
  102. &startinfo, &info)) {
  103. _test_perror_win("CreateProcess");
  104. }
  105. if (!WriteProcessMemory(info.hProcess, (void *)&_test_entry_f, &t->_f,
  106. sizeof(t->_f), 0)) {
  107. TerminateProcess(info.hProcess, -1);
  108. _test_perror_win("WriteProcessMemory");
  109. }
  110. ResumeThread(info.hThread);
  111. bool success;
  112. if (t->timeout) {
  113. if (WaitForSingleObject(info.hProcess, t->timeout) == WAIT_TIMEOUT) {
  114. TerminateProcess(info.hProcess, -1);
  115. fprintf(stderr, "child process timed out after %d milliseconds\n",
  116. t->timeout);
  117. success = false;
  118. goto r;
  119. }
  120. }
  121. else {
  122. WaitForSingleObject(info.hProcess, INFINITE);
  123. }
  124. unsigned long status;
  125. GetExitCodeProcess(info.hProcess, &status);
  126. success = status == t->expected_exit;
  127. r: CloseHandle(info.hProcess);
  128. CloseHandle(info.hThread);
  129. return success;
  130. #else
  131. pid_t pid = fork();
  132. if (pid == -1) {
  133. perror("fork");
  134. return false;
  135. }
  136. if (!pid) {
  137. bool ret = t->_f();
  138. if (!ret) exit(255);
  139. exit(t->expected_exit);
  140. }
  141. if (t->timeout) {
  142. struct timespec ts = { t->timeout / 1000, (t->timeout % 1000) * 1000000 };
  143. if (!pselect(0, 0, 0, 0, &ts, &_test_sigmask)) {
  144. // if pselect returned zero it must've timed out
  145. fprintf(stderr, "child process timed out after %d milliseconds\n",
  146. t->timeout);
  147. kill(pid, SIGKILL); // XXX should this be a less harsh signal?
  148. waitpid(pid, 0, 0); // still have to reap the zombie process
  149. return false;
  150. }
  151. }
  152. // either there was no timeout value or pselect errored meaning we should
  153. // have something to wait() on!
  154. int status;
  155. wait(&status);
  156. if (WIFEXITED(status)) {
  157. return WEXITSTATUS(status) == t->expected_exit;
  158. }
  159. else /* WIFSIGNALED(status) */ {
  160. fprintf(stderr, "child process killed by signal %d (%s)\n",
  161. WTERMSIG(status), strsignal(WTERMSIG(status)));
  162. return false;
  163. }
  164. #endif
  165. }
  166. #ifndef _WIN32
  167. static void _test_sigchld(int sig) {}
  168. #endif
  169. /*
  170. * Main test driver, does the important stuff
  171. */
  172. int main(void) {
  173. #ifdef _WIN32
  174. // if _test_entry_f points at something, we're the """forked""" child
  175. if (_test_entry_f) return !_test_entry_f();
  176. GetModuleFileNameW(0, _test_exepath, sizeof(_test_exepath) /
  177. sizeof(*_test_exepath));
  178. // make the output look correct
  179. void *con = GetStdHandle(STD_OUTPUT_HANDLE);
  180. unsigned long conmode;
  181. GetConsoleMode(con, &conmode);
  182. SetConsoleMode(con, conmode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
  183. #else
  184. // set up no-op SIGCHLD handling so we can (ab)use pselect() to do race-free
  185. // timeouts
  186. struct sigaction sa;
  187. sigfillset(&sa.sa_mask);
  188. sa.sa_flags = 0;
  189. sa.sa_handler = &_test_sigchld;
  190. sigaction(SIGCHLD, &sa, 0);
  191. sigaddset(&_test_sigmask, SIGCHLD);
  192. sigprocmask(SIG_BLOCK, &_test_sigmask, 0);
  193. sigemptyset(&_test_sigmask);
  194. #endif
  195. int thistest = 1;
  196. bool failed = false;
  197. for (struct _test *t = _tests; t; t = t->_next, ++thistest) {
  198. if (!_run_test(t)) {
  199. if (!failed) {
  200. fprintf(stderr, "\
  201. \x1b[1;31m==== TESTS FAILED ====\x1b[0m\n\
  202. Testing \x1b[36m%s\x1b[0m failed on the following cases:\n\
  203. ", _test_desc.desc);
  204. }
  205. failed = true;
  206. fprintf(stderr, "[%02d/%02d] %s\n", thistest, _ntests, t->desc);
  207. }
  208. }
  209. #ifdef _WIN32
  210. SetConsoleMode(con, conmode);
  211. #endif
  212. return !!failed;
  213. }
  214. // get any normal main()s out the way in the tested code
  215. #define main _main
  216. static struct _test_desc _test_desc = // user input follows this header
  217. // vi: sw=4 ts=4 noet tw=80 cc=80