tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tinytest.c (15194B)


      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
     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
    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
    142 static unsigned int testcase_set_timeout_(void)
    143 {
    144 return alarm(opt_timeout);
    145 }
    146 
    147 static unsigned int testcase_reset_timeout_(void)
    148 {
    149 return alarm(0);
    150 }
    151 #endif
    152 
    153 static enum outcome
    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
    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
    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 }