1 /* tinytest.c -- Copyright 2009-2012 Nick Mathewson
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions
5 * are met:
6 * 1. Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * 2. Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * 3. The name of the author may not be used to endorse or promote products
12 * derived from this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25 #ifdef TINYTEST_LOCAL
26 #include "tinytest_local.h"
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <assert.h>
33
34 #ifndef NO_FORKING
35
36 #ifdef _WIN32
37 #include <windows.h>
38 #else
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <unistd.h>
42 #endif
43
44 #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
45 #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \
46 __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070)
47 /* Workaround for a stupid bug in OSX 10.6 */
48 #define FORK_BREAKS_GCOV
49 #include <vproc.h>
50 #endif
51 #endif
52
53 #endif /* !NO_FORKING */
54
55 #ifndef __GNUC__
56 #define __attribute__(x)
57 #endif
58
59 #include "tinytest.h"
60 #include "tinytest_macros.h"
61
62 #define LONGEST_TEST_NAME 16384
63 #define DEFAULT_TESTCASE_TIMEOUT 30U
64 #define MAGIC_EXITCODE 42
65
66 static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/
67 static int n_ok = 0; /**< Number of tests that have passed */
68 static int n_bad = 0; /**< Number of tests that have failed. */
69 static int n_skipped = 0; /**< Number of tests that have been skipped. */
70
71 static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/
72 static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */
73 static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */
74 static unsigned int opt_timeout = DEFAULT_TESTCASE_TIMEOUT; /**< Timeout for every test (using alarm()) */
75 const char *verbosity_flag = "";
76
77 const struct testlist_alias_t *cfg_aliases=NULL;
78
79 enum outcome { SKIP=2, OK=1, FAIL=0 };
80 static enum outcome cur_test_outcome = 0;
81 const char *cur_test_prefix = NULL; /**< prefix of the current test group */
82 /** Name of the current test, if we haven't logged is yet. Used for --quiet */
83 const char *cur_test_name = NULL;
84
85 static void usage(struct testgroup_t *groups, int list_groups)
86 __attribute__((noreturn));
87 static int process_test_option(struct testgroup_t *groups, const char *test);
88
89 #ifdef _WIN32
90 /* Copy of argv[0] for win32. */
91 static char commandname[MAX_PATH+1];
92
93 struct timeout_thread_args {
94 const testcase_fn *fn;
95 void *env;
96 };
97
98 static DWORD WINAPI
timeout_thread_proc_(LPVOID arg)99 timeout_thread_proc_(LPVOID arg)
100 {
101 struct timeout_thread_args *args = arg;
102 (*(args->fn))(args->env);
103 ExitThread(cur_test_outcome == FAIL ? 1 : 0);
104 }
105
106 static enum outcome
testcase_run_in_thread_(const struct testcase_t * testcase,void * env)107 testcase_run_in_thread_(const struct testcase_t *testcase, void *env)
108 {
109 /* We will never run testcase in a new thread when the
110 timeout is set to zero */
111 assert(opt_timeout);
112 DWORD ret, tid;
113 HANDLE handle;
114 struct timeout_thread_args args = {
115 &(testcase->fn),
116 env
117 };
118
119 handle =CreateThread(NULL, 0, timeout_thread_proc_,
120 (LPVOID)&args, 0, &tid);
121 ret = WaitForSingleObject(handle, opt_timeout * 1000U);
122 if (ret == WAIT_OBJECT_0) {
123 ret = 0;
124 if (!GetExitCodeThread(handle, &ret)) {
125 printf("GetExitCodeThread failed\n");
126 ret = 1;
127 }
128 } else if (ret == WAIT_TIMEOUT) {
129 printf("timeout\n");
130 } else {
131 printf("Wait failed\n");
132 }
133 CloseHandle(handle);
134 if (ret == 0)
135 return OK;
136 else if (ret == MAGIC_EXITCODE)
137 return SKIP;
138 else
139 return FAIL;
140 }
141 #else
testcase_set_timeout_(void)142 static unsigned int testcase_set_timeout_(void)
143 {
144 return alarm(opt_timeout);
145 }
146
testcase_reset_timeout_(void)147 static unsigned int testcase_reset_timeout_(void)
148 {
149 return alarm(0);
150 }
151 #endif
152
153 static enum outcome
testcase_run_bare_(const struct testcase_t * testcase)154 testcase_run_bare_(const struct testcase_t *testcase)
155 {
156 void *env = NULL;
157 int outcome;
158 if (testcase->setup) {
159 env = testcase->setup->setup_fn(testcase);
160 if (!env)
161 return FAIL;
162 else if (env == (void*)TT_SKIP)
163 return SKIP;
164 }
165
166 cur_test_outcome = OK;
167 {
168 if (opt_timeout) {
169 #ifdef _WIN32
170 cur_test_outcome = testcase_run_in_thread_(testcase, env);
171 #else
172 testcase_set_timeout_();
173 testcase->fn(env);
174 testcase_reset_timeout_();
175 #endif
176 } else {
177 testcase->fn(env);
178 }
179 }
180 outcome = cur_test_outcome;
181
182 if (testcase->setup) {
183 if (testcase->setup->cleanup_fn(testcase, env) == 0)
184 outcome = FAIL;
185 }
186
187 return outcome;
188 }
189
190
191 #ifndef NO_FORKING
192
193 static enum outcome
testcase_run_forked_(const struct testgroup_t * group,const struct testcase_t * testcase)194 testcase_run_forked_(const struct testgroup_t *group,
195 const struct testcase_t *testcase)
196 {
197 #ifdef _WIN32
198 /* Fork? On Win32? How primitive! We'll do what the smart kids do:
199 we'll invoke our own exe (whose name we recall from the command
200 line) with a command line that tells it to run just the test we
201 want, and this time without forking.
202
203 (No, threads aren't an option. The whole point of forking is to
204 share no state between tests.)
205 */
206 int ok;
207 char buffer[LONGEST_TEST_NAME+256];
208 STARTUPINFOA si;
209 PROCESS_INFORMATION info;
210 DWORD ret;
211
212 if (!in_tinytest_main) {
213 printf("\nERROR. On Windows, testcase_run_forked_ must be"
214 " called from within tinytest_main.\n");
215 abort();
216 }
217 if (opt_verbosity>0)
218 printf("[forking] ");
219
220 snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s --timeout 0 %s%s",
221 commandname, verbosity_flag, group->prefix, testcase->name);
222
223 memset(&si, 0, sizeof(si));
224 memset(&info, 0, sizeof(info));
225 si.cb = sizeof(si);
226
227 ok = CreateProcessA(commandname, buffer, NULL, NULL, 0,
228 0, NULL, NULL, &si, &info);
229 if (!ok) {
230 printf("CreateProcess failed!\n");
231 return FAIL;
232 }
233 ret = WaitForSingleObject(info.hProcess,
234 (opt_timeout ? opt_timeout * 1000U : INFINITE));
235
236 if (ret == WAIT_OBJECT_0) {
237 GetExitCodeProcess(info.hProcess, &ret);
238 } else if (ret == WAIT_TIMEOUT) {
239 printf("timeout\n");
240 } else {
241 printf("Wait failed\n");
242 }
243 CloseHandle(info.hProcess);
244 CloseHandle(info.hThread);
245 if (ret == 0)
246 return OK;
247 else if (ret == MAGIC_EXITCODE)
248 return SKIP;
249 else
250 return FAIL;
251 #else
252 int outcome_pipe[2];
253 pid_t pid;
254 (void)group;
255
256 if (pipe(outcome_pipe))
257 perror("opening pipe");
258
259 if (opt_verbosity>0)
260 printf("[forking] ");
261 pid = fork();
262 #ifdef FORK_BREAKS_GCOV
263 vproc_transaction_begin(0);
264 #endif
265 if (!pid) {
266 /* child. */
267 int test_r, write_r;
268 char b[1];
269 close(outcome_pipe[0]);
270 test_r = testcase_run_bare_(testcase);
271 assert(0<=(int)test_r && (int)test_r<=2);
272 b[0] = "NYS"[test_r];
273 write_r = (int)write(outcome_pipe[1], b, 1);
274 if (write_r != 1) {
275 perror("write outcome to pipe");
276 exit(1);
277 }
278 exit(0);
279 return FAIL; /* unreachable */
280 } else {
281 /* parent */
282 int status, r, exitcode;
283 char b[1];
284 /* Close this now, so that if the other side closes it,
285 * our read fails. */
286 close(outcome_pipe[1]);
287 r = (int)read(outcome_pipe[0], b, 1);
288 if (r == 0) {
289 printf("[Lost connection!] ");
290 return FAIL;
291 } else if (r != 1) {
292 perror("read outcome from pipe");
293 }
294 waitpid(pid, &status, 0);
295 exitcode = WEXITSTATUS(status);
296 close(outcome_pipe[0]);
297 if (opt_verbosity>1)
298 printf("%s%s: exited with %i (%i)\n", group->prefix, testcase->name, exitcode, status);
299 if (exitcode != 0)
300 {
301 printf("[atexit failure!] ");
302 return FAIL;
303 }
304 return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
305 }
306 #endif
307 }
308
309 #endif /* !NO_FORKING */
310
311 int
testcase_run_one(const struct testgroup_t * group,const struct testcase_t * testcase)312 testcase_run_one(const struct testgroup_t *group,
313 const struct testcase_t *testcase)
314 {
315 enum outcome outcome;
316
317 if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) {
318 if (opt_verbosity>0)
319 printf("%s%s: %s\n",
320 group->prefix, testcase->name,
321 (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED");
322 ++n_skipped;
323 return SKIP;
324 }
325
326 if (opt_verbosity>0 && !opt_forked) {
327 printf("%s%s: ", group->prefix, testcase->name);
328 } else {
329 if (opt_verbosity==0) printf(".");
330 cur_test_prefix = group->prefix;
331 cur_test_name = testcase->name;
332 }
333
334 #ifndef NO_FORKING
335 if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
336 outcome = testcase_run_forked_(group, testcase);
337 } else {
338 #else
339 {
340 #endif
341 outcome = testcase_run_bare_(testcase);
342 }
343
344 if (outcome == OK) {
345 if (opt_verbosity>0 && !opt_forked)
346 puts(opt_verbosity==1?"OK":"");
347 } else if (outcome == SKIP) {
348 if (opt_verbosity>0 && !opt_forked)
349 puts("SKIPPED");
350 } else {
351 if (!opt_forked)
352 printf("\n [%s FAILED]\n", testcase->name);
353 }
354
355 if (opt_forked) {
356 exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
357 return 1; /* unreachable */
358 } else {
359 return (int)outcome;
360 }
361 }
362
363 int
364 tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag)
365 {
366 int i, j;
367 size_t length = LONGEST_TEST_NAME;
368 char fullname[LONGEST_TEST_NAME];
369 int found=0;
370 if (strstr(arg, ".."))
371 length = strstr(arg,"..")-arg;
372 for (i=0; groups[i].prefix; ++i) {
373 for (j=0; groups[i].cases[j].name; ++j) {
374 struct testcase_t *testcase = &groups[i].cases[j];
375 snprintf(fullname, sizeof(fullname), "%s%s",
376 groups[i].prefix, testcase->name);
377 if (!flag) { /* Hack! */
378 printf(" %s", fullname);
379 if (testcase->flags & TT_OFF_BY_DEFAULT)
380 puts(" (Off by default)");
381 else if (testcase->flags & TT_SKIP)
382 puts(" (DISABLED)");
383 else
384 puts("");
385 }
386 if (!strncmp(fullname, arg, length)) {
387 if (set)
388 testcase->flags |= flag;
389 else
390 testcase->flags &= ~flag;
391 ++found;
392 }
393 }
394 }
395 return found;
396 }
397
398 static void
399 usage(struct testgroup_t *groups, int list_groups)
400 {
401 puts("Options are: [--verbose|--quiet|--terse] [--no-fork] [--timeout <sec>]");
402 puts(" Specify tests by name, or using a prefix ending with '..'");
403 puts(" To skip a test, prefix its name with a colon.");
404 puts(" To enable a disabled test, prefix its name with a plus.");
405 puts(" Use --list-tests for a list of tests.");
406 if (list_groups) {
407 puts("Known tests are:");
408 tinytest_set_flag_(groups, "..", 1, 0);
409 }
410 exit(0);
411 }
412
413 static int
414 process_test_alias(struct testgroup_t *groups, const char *test)
415 {
416 int i, j, n, r;
417 for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) {
418 if (!strcmp(cfg_aliases[i].name, test)) {
419 n = 0;
420 for (j = 0; cfg_aliases[i].tests[j]; ++j) {
421 r = process_test_option(groups, cfg_aliases[i].tests[j]);
422 if (r<0)
423 return -1;
424 n += r;
425 }
426 return n;
427 }
428 }
429 printf("No such test alias as @%s!",test);
430 return -1;
431 }
432
433 static int
434 process_test_option(struct testgroup_t *groups, const char *test)
435 {
436 int flag = TT_ENABLED_;
437 int n = 0;
438 if (test[0] == '@') {
439 return process_test_alias(groups, test + 1);
440 } else if (test[0] == ':') {
441 ++test;
442 flag = TT_SKIP;
443 } else if (test[0] == '+') {
444 ++test;
445 ++n;
446 if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) {
447 printf("No such test as %s!\n", test);
448 return -1;
449 }
450 } else {
451 ++n;
452 }
453 if (!tinytest_set_flag_(groups, test, 1, flag)) {
454 printf("No such test as %s!\n", test);
455 return -1;
456 }
457 return n;
458 }
459
460 void
461 tinytest_set_aliases(const struct testlist_alias_t *aliases)
462 {
463 cfg_aliases = aliases;
464 }
465
466 int
467 tinytest_main(int c, const char **v, struct testgroup_t *groups)
468 {
469 int i, j, n=0;
470
471 #ifdef _WIN32
472 const char *sp = strrchr(v[0], '.');
473 const char *extension = "";
474 if (!sp || stricmp(sp, ".exe"))
475 extension = ".exe"; /* Add an exe so CreateProcess will work */
476 snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension);
477 commandname[MAX_PATH]='\0';
478 #endif
479 for (i=1; i<c; ++i) {
480 if (v[i][0] == '-') {
481 if (!strcmp(v[i], "--RUNNING-FORKED")) {
482 opt_forked = 1;
483 } else if (!strcmp(v[i], "--no-fork")) {
484 opt_nofork = 1;
485 } else if (!strcmp(v[i], "--quiet")) {
486 opt_verbosity = -1;
487 verbosity_flag = "--quiet";
488 } else if (!strcmp(v[i], "--verbose")) {
489 opt_verbosity = 2;
490 verbosity_flag = "--verbose";
491 } else if (!strcmp(v[i], "--terse")) {
492 opt_verbosity = 0;
493 verbosity_flag = "--terse";
494 } else if (!strcmp(v[i], "--help")) {
495 usage(groups, 0);
496 } else if (!strcmp(v[i], "--list-tests")) {
497 usage(groups, 1);
498 } else if (!strcmp(v[i], "--timeout")) {
499 ++i;
500 if (i >= c) {
501 fprintf(stderr, "--timeout requires argument\n");
502 return -1;
503 }
504 opt_timeout = (unsigned)atoi(v[i]);
505 } else {
506 fprintf(stderr, "Unknown option %s. Try --help\n", v[i]);
507 return -1;
508 }
509 } else {
510 int r = process_test_option(groups, v[i]);
511 if (r<0)
512 return -1;
513 n += r;
514 }
515 }
516 if (!n)
517 tinytest_set_flag_(groups, "..", 1, TT_ENABLED_);
518
519 #ifdef _IONBF
520 setvbuf(stdout, NULL, _IONBF, 0);
521 #endif
522
523 ++in_tinytest_main;
524 for (i = 0; groups[i].prefix; ++i) {
525 struct testgroup_t *group = &groups[i];
526 for (j = 0; group->cases[j].name; ++j) {
527 struct testcase_t *testcase = &group->cases[j];
528 int test_attempts = 3;
529 int test_ret_err;
530
531 if (!(testcase->flags & TT_ENABLED_))
532 continue;
533
534 for (;;) {
535 test_ret_err = testcase_run_one(group, testcase);
536
537 if (test_ret_err == OK)
538 break;
539 if (!(testcase->flags & TT_RETRIABLE))
540 break;
541 printf("\n [RETRYING %s (%i)]\n", testcase->name, test_attempts);
542 if (!test_attempts--)
543 break;
544 }
545
546 switch (test_ret_err) {
547 case OK: ++n_ok; break;
548 case SKIP: ++n_skipped; break;
549 default: ++n_bad; break;
550 }
551 }
552 }
553
554 --in_tinytest_main;
555
556 if (opt_verbosity==0)
557 puts("");
558
559 if (n_bad)
560 printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
561 n_bad+n_ok,n_skipped);
562 else if (opt_verbosity >= 1)
563 printf("%d tests ok. (%d skipped)\n", n_ok, n_skipped);
564
565 return (n_bad == 0) ? 0 : 1;
566 }
567
568 int
569 tinytest_get_verbosity_(void)
570 {
571 return opt_verbosity;
572 }
573
574 void
575 tinytest_set_test_failed_(void)
576 {
577 if (opt_verbosity <= 0 && cur_test_name) {
578 if (opt_verbosity==0) puts("");
579 printf("%s%s: ", cur_test_prefix, cur_test_name);
580 cur_test_name = NULL;
581 }
582 cur_test_outcome = FAIL;
583 }
584
585 void
586 tinytest_set_test_skipped_(void)
587 {
588 if (cur_test_outcome==OK)
589 cur_test_outcome = SKIP;
590 }
591
592 char *
593 tinytest_format_hex_(const void *val_, unsigned long len)
594 {
595 const unsigned char *val = val_;
596 char *result, *cp;
597 size_t i;
598
599 if (!val)
600 return strdup("null");
601 if (!(result = malloc(len*2+1)))
602 return strdup("<allocation failure>");
603 cp = result;
604 for (i=0;i<len;++i) {
605 *cp++ = "0123456789ABCDEF"[val[i] >> 4];
606 *cp++ = "0123456789ABCDEF"[val[i] & 0x0f];
607 }
608 *cp = 0;
609 return result;
610 }
611