xref: /freebsd/contrib/ntp/sntp/libevent/test/tinytest.c (revision a25439b68651d176ae05867f5090d45fd85e9f24)
12b15cb3dSCy Schubert /* tinytest.c -- Copyright 2009-2012 Nick Mathewson
22b15cb3dSCy Schubert  *
32b15cb3dSCy Schubert  * Redistribution and use in source and binary forms, with or without
42b15cb3dSCy Schubert  * modification, are permitted provided that the following conditions
52b15cb3dSCy Schubert  * are met:
62b15cb3dSCy Schubert  * 1. Redistributions of source code must retain the above copyright
72b15cb3dSCy Schubert  *    notice, this list of conditions and the following disclaimer.
82b15cb3dSCy Schubert  * 2. Redistributions in binary form must reproduce the above copyright
92b15cb3dSCy Schubert  *    notice, this list of conditions and the following disclaimer in the
102b15cb3dSCy Schubert  *    documentation and/or other materials provided with the distribution.
112b15cb3dSCy Schubert  * 3. The name of the author may not be used to endorse or promote products
122b15cb3dSCy Schubert  *    derived from this software without specific prior written permission.
132b15cb3dSCy Schubert  *
142b15cb3dSCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
152b15cb3dSCy Schubert  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
162b15cb3dSCy Schubert  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
172b15cb3dSCy Schubert  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
182b15cb3dSCy Schubert  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
192b15cb3dSCy Schubert  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
202b15cb3dSCy Schubert  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
212b15cb3dSCy Schubert  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
222b15cb3dSCy Schubert  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
232b15cb3dSCy Schubert  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
242b15cb3dSCy Schubert  */
252b15cb3dSCy Schubert #ifdef TINYTEST_LOCAL
262b15cb3dSCy Schubert #include "tinytest_local.h"
272b15cb3dSCy Schubert #endif
282b15cb3dSCy Schubert 
292b15cb3dSCy Schubert #include <stdio.h>
302b15cb3dSCy Schubert #include <stdlib.h>
312b15cb3dSCy Schubert #include <string.h>
322b15cb3dSCy Schubert #include <assert.h>
332b15cb3dSCy Schubert 
34*a25439b6SCy Schubert #ifndef NO_FORKING
35*a25439b6SCy Schubert 
362b15cb3dSCy Schubert #ifdef _WIN32
372b15cb3dSCy Schubert #include <windows.h>
382b15cb3dSCy Schubert #else
392b15cb3dSCy Schubert #include <sys/types.h>
402b15cb3dSCy Schubert #include <sys/wait.h>
412b15cb3dSCy Schubert #include <unistd.h>
422b15cb3dSCy Schubert #endif
432b15cb3dSCy Schubert 
442b15cb3dSCy Schubert #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
452b15cb3dSCy Schubert #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \
462b15cb3dSCy Schubert     __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070)
472b15cb3dSCy Schubert /* Workaround for a stupid bug in OSX 10.6 */
482b15cb3dSCy Schubert #define FORK_BREAKS_GCOV
492b15cb3dSCy Schubert #include <vproc.h>
502b15cb3dSCy Schubert #endif
512b15cb3dSCy Schubert #endif
522b15cb3dSCy Schubert 
53*a25439b6SCy Schubert #endif /* !NO_FORKING */
54*a25439b6SCy Schubert 
552b15cb3dSCy Schubert #ifndef __GNUC__
562b15cb3dSCy Schubert #define __attribute__(x)
572b15cb3dSCy Schubert #endif
582b15cb3dSCy Schubert 
592b15cb3dSCy Schubert #include "tinytest.h"
602b15cb3dSCy Schubert #include "tinytest_macros.h"
612b15cb3dSCy Schubert 
622b15cb3dSCy Schubert #define LONGEST_TEST_NAME 16384
632b15cb3dSCy Schubert 
642b15cb3dSCy Schubert static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/
652b15cb3dSCy Schubert static int n_ok = 0; /**< Number of tests that have passed */
662b15cb3dSCy Schubert static int n_bad = 0; /**< Number of tests that have failed. */
672b15cb3dSCy Schubert static int n_skipped = 0; /**< Number of tests that have been skipped. */
682b15cb3dSCy Schubert 
692b15cb3dSCy Schubert static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/
702b15cb3dSCy Schubert static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */
712b15cb3dSCy Schubert static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */
722b15cb3dSCy Schubert const char *verbosity_flag = "";
732b15cb3dSCy Schubert 
742b15cb3dSCy Schubert const struct testlist_alias_t *cfg_aliases=NULL;
752b15cb3dSCy Schubert 
762b15cb3dSCy Schubert enum outcome { SKIP=2, OK=1, FAIL=0 };
772b15cb3dSCy Schubert static enum outcome cur_test_outcome = 0;
782b15cb3dSCy Schubert const char *cur_test_prefix = NULL; /**< prefix of the current test group */
792b15cb3dSCy Schubert /** Name of the current test, if we haven't logged is yet. Used for --quiet */
802b15cb3dSCy Schubert const char *cur_test_name = NULL;
812b15cb3dSCy Schubert 
822b15cb3dSCy Schubert #ifdef _WIN32
832b15cb3dSCy Schubert /* Copy of argv[0] for win32. */
842b15cb3dSCy Schubert static char commandname[MAX_PATH+1];
852b15cb3dSCy Schubert #endif
862b15cb3dSCy Schubert 
872b15cb3dSCy Schubert static void usage(struct testgroup_t *groups, int list_groups)
882b15cb3dSCy Schubert   __attribute__((noreturn));
892b15cb3dSCy Schubert static int process_test_option(struct testgroup_t *groups, const char *test);
902b15cb3dSCy Schubert 
912b15cb3dSCy Schubert static enum outcome
922b15cb3dSCy Schubert testcase_run_bare_(const struct testcase_t *testcase)
932b15cb3dSCy Schubert {
942b15cb3dSCy Schubert 	void *env = NULL;
952b15cb3dSCy Schubert 	int outcome;
962b15cb3dSCy Schubert 	if (testcase->setup) {
972b15cb3dSCy Schubert 		env = testcase->setup->setup_fn(testcase);
982b15cb3dSCy Schubert 		if (!env)
992b15cb3dSCy Schubert 			return FAIL;
1002b15cb3dSCy Schubert 		else if (env == (void*)TT_SKIP)
1012b15cb3dSCy Schubert 			return SKIP;
1022b15cb3dSCy Schubert 	}
1032b15cb3dSCy Schubert 
1042b15cb3dSCy Schubert 	cur_test_outcome = OK;
1052b15cb3dSCy Schubert 	testcase->fn(env);
1062b15cb3dSCy Schubert 	outcome = cur_test_outcome;
1072b15cb3dSCy Schubert 
1082b15cb3dSCy Schubert 	if (testcase->setup) {
1092b15cb3dSCy Schubert 		if (testcase->setup->cleanup_fn(testcase, env) == 0)
1102b15cb3dSCy Schubert 			outcome = FAIL;
1112b15cb3dSCy Schubert 	}
1122b15cb3dSCy Schubert 
1132b15cb3dSCy Schubert 	return outcome;
1142b15cb3dSCy Schubert }
1152b15cb3dSCy Schubert 
1162b15cb3dSCy Schubert #define MAGIC_EXITCODE 42
1172b15cb3dSCy Schubert 
118*a25439b6SCy Schubert #ifndef NO_FORKING
119*a25439b6SCy Schubert 
1202b15cb3dSCy Schubert static enum outcome
1212b15cb3dSCy Schubert testcase_run_forked_(const struct testgroup_t *group,
1222b15cb3dSCy Schubert 		     const struct testcase_t *testcase)
1232b15cb3dSCy Schubert {
1242b15cb3dSCy Schubert #ifdef _WIN32
1252b15cb3dSCy Schubert 	/* Fork? On Win32?  How primitive!  We'll do what the smart kids do:
1262b15cb3dSCy Schubert 	   we'll invoke our own exe (whose name we recall from the command
1272b15cb3dSCy Schubert 	   line) with a command line that tells it to run just the test we
1282b15cb3dSCy Schubert 	   want, and this time without forking.
1292b15cb3dSCy Schubert 
1302b15cb3dSCy Schubert 	   (No, threads aren't an option.  The whole point of forking is to
1312b15cb3dSCy Schubert 	   share no state between tests.)
1322b15cb3dSCy Schubert 	 */
1332b15cb3dSCy Schubert 	int ok;
1342b15cb3dSCy Schubert 	char buffer[LONGEST_TEST_NAME+256];
1352b15cb3dSCy Schubert 	STARTUPINFOA si;
1362b15cb3dSCy Schubert 	PROCESS_INFORMATION info;
1372b15cb3dSCy Schubert 	DWORD exitcode;
1382b15cb3dSCy Schubert 
1392b15cb3dSCy Schubert 	if (!in_tinytest_main) {
1402b15cb3dSCy Schubert 		printf("\nERROR.  On Windows, testcase_run_forked_ must be"
1412b15cb3dSCy Schubert 		       " called from within tinytest_main.\n");
1422b15cb3dSCy Schubert 		abort();
1432b15cb3dSCy Schubert 	}
1442b15cb3dSCy Schubert 	if (opt_verbosity>0)
1452b15cb3dSCy Schubert 		printf("[forking] ");
1462b15cb3dSCy Schubert 
1472b15cb3dSCy Schubert 	snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s",
1482b15cb3dSCy Schubert 		 commandname, verbosity_flag, group->prefix, testcase->name);
1492b15cb3dSCy Schubert 
1502b15cb3dSCy Schubert 	memset(&si, 0, sizeof(si));
1512b15cb3dSCy Schubert 	memset(&info, 0, sizeof(info));
1522b15cb3dSCy Schubert 	si.cb = sizeof(si);
1532b15cb3dSCy Schubert 
1542b15cb3dSCy Schubert 	ok = CreateProcessA(commandname, buffer, NULL, NULL, 0,
1552b15cb3dSCy Schubert 			   0, NULL, NULL, &si, &info);
1562b15cb3dSCy Schubert 	if (!ok) {
1572b15cb3dSCy Schubert 		printf("CreateProcess failed!\n");
1582b15cb3dSCy Schubert 		return 0;
1592b15cb3dSCy Schubert 	}
1602b15cb3dSCy Schubert 	WaitForSingleObject(info.hProcess, INFINITE);
1612b15cb3dSCy Schubert 	GetExitCodeProcess(info.hProcess, &exitcode);
1622b15cb3dSCy Schubert 	CloseHandle(info.hProcess);
1632b15cb3dSCy Schubert 	CloseHandle(info.hThread);
1642b15cb3dSCy Schubert 	if (exitcode == 0)
1652b15cb3dSCy Schubert 		return OK;
1662b15cb3dSCy Schubert 	else if (exitcode == MAGIC_EXITCODE)
1672b15cb3dSCy Schubert 		return SKIP;
1682b15cb3dSCy Schubert 	else
1692b15cb3dSCy Schubert 		return FAIL;
1702b15cb3dSCy Schubert #else
1712b15cb3dSCy Schubert 	int outcome_pipe[2];
1722b15cb3dSCy Schubert 	pid_t pid;
1732b15cb3dSCy Schubert 	(void)group;
1742b15cb3dSCy Schubert 
1752b15cb3dSCy Schubert 	if (pipe(outcome_pipe))
1762b15cb3dSCy Schubert 		perror("opening pipe");
1772b15cb3dSCy Schubert 
1782b15cb3dSCy Schubert 	if (opt_verbosity>0)
1792b15cb3dSCy Schubert 		printf("[forking] ");
1802b15cb3dSCy Schubert 	pid = fork();
1812b15cb3dSCy Schubert #ifdef FORK_BREAKS_GCOV
1822b15cb3dSCy Schubert 	vproc_transaction_begin(0);
1832b15cb3dSCy Schubert #endif
1842b15cb3dSCy Schubert 	if (!pid) {
1852b15cb3dSCy Schubert 		/* child. */
1862b15cb3dSCy Schubert 		int test_r, write_r;
1872b15cb3dSCy Schubert 		char b[1];
1882b15cb3dSCy Schubert 		close(outcome_pipe[0]);
1892b15cb3dSCy Schubert 		test_r = testcase_run_bare_(testcase);
1902b15cb3dSCy Schubert 		assert(0<=(int)test_r && (int)test_r<=2);
1912b15cb3dSCy Schubert 		b[0] = "NYS"[test_r];
1922b15cb3dSCy Schubert 		write_r = (int)write(outcome_pipe[1], b, 1);
1932b15cb3dSCy Schubert 		if (write_r != 1) {
1942b15cb3dSCy Schubert 			perror("write outcome to pipe");
1952b15cb3dSCy Schubert 			exit(1);
1962b15cb3dSCy Schubert 		}
1972b15cb3dSCy Schubert 		exit(0);
1982b15cb3dSCy Schubert 		return FAIL; /* unreachable */
1992b15cb3dSCy Schubert 	} else {
2002b15cb3dSCy Schubert 		/* parent */
2012b15cb3dSCy Schubert 		int status, r;
2022b15cb3dSCy Schubert 		char b[1];
2032b15cb3dSCy Schubert 		/* Close this now, so that if the other side closes it,
2042b15cb3dSCy Schubert 		 * our read fails. */
2052b15cb3dSCy Schubert 		close(outcome_pipe[1]);
2062b15cb3dSCy Schubert 		r = (int)read(outcome_pipe[0], b, 1);
2072b15cb3dSCy Schubert 		if (r == 0) {
2082b15cb3dSCy Schubert 			printf("[Lost connection!] ");
2092b15cb3dSCy Schubert 			return 0;
2102b15cb3dSCy Schubert 		} else if (r != 1) {
2112b15cb3dSCy Schubert 			perror("read outcome from pipe");
2122b15cb3dSCy Schubert 		}
2132b15cb3dSCy Schubert 		waitpid(pid, &status, 0);
2142b15cb3dSCy Schubert 		close(outcome_pipe[0]);
2152b15cb3dSCy Schubert 		return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
2162b15cb3dSCy Schubert 	}
2172b15cb3dSCy Schubert #endif
2182b15cb3dSCy Schubert }
2192b15cb3dSCy Schubert 
220*a25439b6SCy Schubert #endif /* !NO_FORKING */
221*a25439b6SCy Schubert 
2222b15cb3dSCy Schubert int
2232b15cb3dSCy Schubert testcase_run_one(const struct testgroup_t *group,
2242b15cb3dSCy Schubert 		 const struct testcase_t *testcase)
2252b15cb3dSCy Schubert {
2262b15cb3dSCy Schubert 	enum outcome outcome;
2272b15cb3dSCy Schubert 
2282b15cb3dSCy Schubert 	if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) {
2292b15cb3dSCy Schubert 		if (opt_verbosity>0)
2302b15cb3dSCy Schubert 			printf("%s%s: %s\n",
2312b15cb3dSCy Schubert 			   group->prefix, testcase->name,
2322b15cb3dSCy Schubert 			   (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED");
2332b15cb3dSCy Schubert 		++n_skipped;
2342b15cb3dSCy Schubert 		return SKIP;
2352b15cb3dSCy Schubert 	}
2362b15cb3dSCy Schubert 
2372b15cb3dSCy Schubert 	if (opt_verbosity>0 && !opt_forked) {
2382b15cb3dSCy Schubert 		printf("%s%s: ", group->prefix, testcase->name);
2392b15cb3dSCy Schubert 	} else {
2402b15cb3dSCy Schubert 		if (opt_verbosity==0) printf(".");
2412b15cb3dSCy Schubert 		cur_test_prefix = group->prefix;
2422b15cb3dSCy Schubert 		cur_test_name = testcase->name;
2432b15cb3dSCy Schubert 	}
2442b15cb3dSCy Schubert 
245*a25439b6SCy Schubert #ifndef NO_FORKING
2462b15cb3dSCy Schubert 	if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
2472b15cb3dSCy Schubert 		outcome = testcase_run_forked_(group, testcase);
2482b15cb3dSCy Schubert 	} else {
249*a25439b6SCy Schubert #else
250*a25439b6SCy Schubert 	{
251*a25439b6SCy Schubert #endif
2522b15cb3dSCy Schubert 		outcome = testcase_run_bare_(testcase);
2532b15cb3dSCy Schubert 	}
2542b15cb3dSCy Schubert 
2552b15cb3dSCy Schubert 	if (outcome == OK) {
2562b15cb3dSCy Schubert 		++n_ok;
2572b15cb3dSCy Schubert 		if (opt_verbosity>0 && !opt_forked)
2582b15cb3dSCy Schubert 			puts(opt_verbosity==1?"OK":"");
2592b15cb3dSCy Schubert 	} else if (outcome == SKIP) {
2602b15cb3dSCy Schubert 		++n_skipped;
2612b15cb3dSCy Schubert 		if (opt_verbosity>0 && !opt_forked)
2622b15cb3dSCy Schubert 			puts("SKIPPED");
2632b15cb3dSCy Schubert 	} else {
2642b15cb3dSCy Schubert 		++n_bad;
2652b15cb3dSCy Schubert 		if (!opt_forked)
2662b15cb3dSCy Schubert 			printf("\n  [%s FAILED]\n", testcase->name);
2672b15cb3dSCy Schubert 	}
2682b15cb3dSCy Schubert 
2692b15cb3dSCy Schubert 	if (opt_forked) {
2702b15cb3dSCy Schubert 		exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
2712b15cb3dSCy Schubert 		return 1; /* unreachable */
2722b15cb3dSCy Schubert 	} else {
2732b15cb3dSCy Schubert 		return (int)outcome;
2742b15cb3dSCy Schubert 	}
2752b15cb3dSCy Schubert }
2762b15cb3dSCy Schubert 
2772b15cb3dSCy Schubert int
2782b15cb3dSCy Schubert tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag)
2792b15cb3dSCy Schubert {
2802b15cb3dSCy Schubert 	int i, j;
2812b15cb3dSCy Schubert 	size_t length = LONGEST_TEST_NAME;
2822b15cb3dSCy Schubert 	char fullname[LONGEST_TEST_NAME];
2832b15cb3dSCy Schubert 	int found=0;
2842b15cb3dSCy Schubert 	if (strstr(arg, ".."))
2852b15cb3dSCy Schubert 		length = strstr(arg,"..")-arg;
2862b15cb3dSCy Schubert 	for (i=0; groups[i].prefix; ++i) {
2872b15cb3dSCy Schubert 		for (j=0; groups[i].cases[j].name; ++j) {
2882b15cb3dSCy Schubert 			struct testcase_t *testcase = &groups[i].cases[j];
2892b15cb3dSCy Schubert 			snprintf(fullname, sizeof(fullname), "%s%s",
2902b15cb3dSCy Schubert 				 groups[i].prefix, testcase->name);
2912b15cb3dSCy Schubert 			if (!flag) { /* Hack! */
2922b15cb3dSCy Schubert 				printf("    %s", fullname);
2932b15cb3dSCy Schubert 				if (testcase->flags & TT_OFF_BY_DEFAULT)
2942b15cb3dSCy Schubert 					puts("   (Off by default)");
2952b15cb3dSCy Schubert 				else if (testcase->flags & TT_SKIP)
2962b15cb3dSCy Schubert 					puts("  (DISABLED)");
2972b15cb3dSCy Schubert 				else
2982b15cb3dSCy Schubert 					puts("");
2992b15cb3dSCy Schubert 			}
3002b15cb3dSCy Schubert 			if (!strncmp(fullname, arg, length)) {
3012b15cb3dSCy Schubert 				if (set)
3022b15cb3dSCy Schubert 					testcase->flags |= flag;
3032b15cb3dSCy Schubert 				else
3042b15cb3dSCy Schubert 					testcase->flags &= ~flag;
3052b15cb3dSCy Schubert 				++found;
3062b15cb3dSCy Schubert 			}
3072b15cb3dSCy Schubert 		}
3082b15cb3dSCy Schubert 	}
3092b15cb3dSCy Schubert 	return found;
3102b15cb3dSCy Schubert }
3112b15cb3dSCy Schubert 
3122b15cb3dSCy Schubert static void
3132b15cb3dSCy Schubert usage(struct testgroup_t *groups, int list_groups)
3142b15cb3dSCy Schubert {
3152b15cb3dSCy Schubert 	puts("Options are: [--verbose|--quiet|--terse] [--no-fork]");
3162b15cb3dSCy Schubert 	puts("  Specify tests by name, or using a prefix ending with '..'");
3172b15cb3dSCy Schubert 	puts("  To skip a test, prefix its name with a colon.");
3182b15cb3dSCy Schubert 	puts("  To enable a disabled test, prefix its name with a plus.");
3192b15cb3dSCy Schubert 	puts("  Use --list-tests for a list of tests.");
3202b15cb3dSCy Schubert 	if (list_groups) {
3212b15cb3dSCy Schubert 		puts("Known tests are:");
3222b15cb3dSCy Schubert 		tinytest_set_flag_(groups, "..", 1, 0);
3232b15cb3dSCy Schubert 	}
3242b15cb3dSCy Schubert 	exit(0);
3252b15cb3dSCy Schubert }
3262b15cb3dSCy Schubert 
3272b15cb3dSCy Schubert static int
3282b15cb3dSCy Schubert process_test_alias(struct testgroup_t *groups, const char *test)
3292b15cb3dSCy Schubert {
3302b15cb3dSCy Schubert 	int i, j, n, r;
3312b15cb3dSCy Schubert 	for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) {
3322b15cb3dSCy Schubert 		if (!strcmp(cfg_aliases[i].name, test)) {
3332b15cb3dSCy Schubert 			n = 0;
3342b15cb3dSCy Schubert 			for (j = 0; cfg_aliases[i].tests[j]; ++j) {
3352b15cb3dSCy Schubert 				r = process_test_option(groups, cfg_aliases[i].tests[j]);
3362b15cb3dSCy Schubert 				if (r<0)
3372b15cb3dSCy Schubert 					return -1;
3382b15cb3dSCy Schubert 				n += r;
3392b15cb3dSCy Schubert 			}
3402b15cb3dSCy Schubert 			return n;
3412b15cb3dSCy Schubert 		}
3422b15cb3dSCy Schubert 	}
3432b15cb3dSCy Schubert 	printf("No such test alias as @%s!",test);
3442b15cb3dSCy Schubert 	return -1;
3452b15cb3dSCy Schubert }
3462b15cb3dSCy Schubert 
3472b15cb3dSCy Schubert static int
3482b15cb3dSCy Schubert process_test_option(struct testgroup_t *groups, const char *test)
3492b15cb3dSCy Schubert {
3502b15cb3dSCy Schubert 	int flag = TT_ENABLED_;
3512b15cb3dSCy Schubert 	int n = 0;
3522b15cb3dSCy Schubert 	if (test[0] == '@') {
3532b15cb3dSCy Schubert 		return process_test_alias(groups, test + 1);
3542b15cb3dSCy Schubert 	} else if (test[0] == ':') {
3552b15cb3dSCy Schubert 		++test;
3562b15cb3dSCy Schubert 		flag = TT_SKIP;
3572b15cb3dSCy Schubert 	} else if (test[0] == '+') {
3582b15cb3dSCy Schubert 		++test;
3592b15cb3dSCy Schubert 		++n;
3602b15cb3dSCy Schubert 		if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) {
3612b15cb3dSCy Schubert 			printf("No such test as %s!\n", test);
3622b15cb3dSCy Schubert 			return -1;
3632b15cb3dSCy Schubert 		}
3642b15cb3dSCy Schubert 	} else {
3652b15cb3dSCy Schubert 		++n;
3662b15cb3dSCy Schubert 	}
3672b15cb3dSCy Schubert 	if (!tinytest_set_flag_(groups, test, 1, flag)) {
3682b15cb3dSCy Schubert 		printf("No such test as %s!\n", test);
3692b15cb3dSCy Schubert 		return -1;
3702b15cb3dSCy Schubert 	}
3712b15cb3dSCy Schubert 	return n;
3722b15cb3dSCy Schubert }
3732b15cb3dSCy Schubert 
3742b15cb3dSCy Schubert void
3752b15cb3dSCy Schubert tinytest_set_aliases(const struct testlist_alias_t *aliases)
3762b15cb3dSCy Schubert {
3772b15cb3dSCy Schubert 	cfg_aliases = aliases;
3782b15cb3dSCy Schubert }
3792b15cb3dSCy Schubert 
3802b15cb3dSCy Schubert int
3812b15cb3dSCy Schubert tinytest_main(int c, const char **v, struct testgroup_t *groups)
3822b15cb3dSCy Schubert {
3832b15cb3dSCy Schubert 	int i, j, n=0;
3842b15cb3dSCy Schubert 
3852b15cb3dSCy Schubert #ifdef _WIN32
3862b15cb3dSCy Schubert 	const char *sp = strrchr(v[0], '.');
3872b15cb3dSCy Schubert 	const char *extension = "";
3882b15cb3dSCy Schubert 	if (!sp || stricmp(sp, ".exe"))
3892b15cb3dSCy Schubert 		extension = ".exe"; /* Add an exe so CreateProcess will work */
3902b15cb3dSCy Schubert 	snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension);
3912b15cb3dSCy Schubert 	commandname[MAX_PATH]='\0';
3922b15cb3dSCy Schubert #endif
3932b15cb3dSCy Schubert 	for (i=1; i<c; ++i) {
3942b15cb3dSCy Schubert 		if (v[i][0] == '-') {
3952b15cb3dSCy Schubert 			if (!strcmp(v[i], "--RUNNING-FORKED")) {
3962b15cb3dSCy Schubert 				opt_forked = 1;
3972b15cb3dSCy Schubert 			} else if (!strcmp(v[i], "--no-fork")) {
3982b15cb3dSCy Schubert 				opt_nofork = 1;
3992b15cb3dSCy Schubert 			} else if (!strcmp(v[i], "--quiet")) {
4002b15cb3dSCy Schubert 				opt_verbosity = -1;
4012b15cb3dSCy Schubert 				verbosity_flag = "--quiet";
4022b15cb3dSCy Schubert 			} else if (!strcmp(v[i], "--verbose")) {
4032b15cb3dSCy Schubert 				opt_verbosity = 2;
4042b15cb3dSCy Schubert 				verbosity_flag = "--verbose";
4052b15cb3dSCy Schubert 			} else if (!strcmp(v[i], "--terse")) {
4062b15cb3dSCy Schubert 				opt_verbosity = 0;
4072b15cb3dSCy Schubert 				verbosity_flag = "--terse";
4082b15cb3dSCy Schubert 			} else if (!strcmp(v[i], "--help")) {
4092b15cb3dSCy Schubert 				usage(groups, 0);
4102b15cb3dSCy Schubert 			} else if (!strcmp(v[i], "--list-tests")) {
4112b15cb3dSCy Schubert 				usage(groups, 1);
4122b15cb3dSCy Schubert 			} else {
4132b15cb3dSCy Schubert 				printf("Unknown option %s.  Try --help\n",v[i]);
4142b15cb3dSCy Schubert 				return -1;
4152b15cb3dSCy Schubert 			}
4162b15cb3dSCy Schubert 		} else {
4172b15cb3dSCy Schubert 			int r = process_test_option(groups, v[i]);
4182b15cb3dSCy Schubert 			if (r<0)
4192b15cb3dSCy Schubert 				return -1;
4202b15cb3dSCy Schubert 			n += r;
4212b15cb3dSCy Schubert 		}
4222b15cb3dSCy Schubert 	}
4232b15cb3dSCy Schubert 	if (!n)
4242b15cb3dSCy Schubert 		tinytest_set_flag_(groups, "..", 1, TT_ENABLED_);
4252b15cb3dSCy Schubert 
426*a25439b6SCy Schubert #ifdef _IONBF
4272b15cb3dSCy Schubert 	setvbuf(stdout, NULL, _IONBF, 0);
428*a25439b6SCy Schubert #endif
4292b15cb3dSCy Schubert 
4302b15cb3dSCy Schubert 	++in_tinytest_main;
4312b15cb3dSCy Schubert 	for (i=0; groups[i].prefix; ++i)
4322b15cb3dSCy Schubert 		for (j=0; groups[i].cases[j].name; ++j)
4332b15cb3dSCy Schubert 			if (groups[i].cases[j].flags & TT_ENABLED_)
4342b15cb3dSCy Schubert 				testcase_run_one(&groups[i],
4352b15cb3dSCy Schubert 						 &groups[i].cases[j]);
4362b15cb3dSCy Schubert 
4372b15cb3dSCy Schubert 	--in_tinytest_main;
4382b15cb3dSCy Schubert 
4392b15cb3dSCy Schubert 	if (opt_verbosity==0)
4402b15cb3dSCy Schubert 		puts("");
4412b15cb3dSCy Schubert 
4422b15cb3dSCy Schubert 	if (n_bad)
4432b15cb3dSCy Schubert 		printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
4442b15cb3dSCy Schubert 		       n_bad+n_ok,n_skipped);
4452b15cb3dSCy Schubert 	else if (opt_verbosity >= 1)
4462b15cb3dSCy Schubert 		printf("%d tests ok.  (%d skipped)\n", n_ok, n_skipped);
4472b15cb3dSCy Schubert 
4482b15cb3dSCy Schubert 	return (n_bad == 0) ? 0 : 1;
4492b15cb3dSCy Schubert }
4502b15cb3dSCy Schubert 
4512b15cb3dSCy Schubert int
4522b15cb3dSCy Schubert tinytest_get_verbosity_(void)
4532b15cb3dSCy Schubert {
4542b15cb3dSCy Schubert 	return opt_verbosity;
4552b15cb3dSCy Schubert }
4562b15cb3dSCy Schubert 
4572b15cb3dSCy Schubert void
4582b15cb3dSCy Schubert tinytest_set_test_failed_(void)
4592b15cb3dSCy Schubert {
4602b15cb3dSCy Schubert 	if (opt_verbosity <= 0 && cur_test_name) {
4612b15cb3dSCy Schubert 		if (opt_verbosity==0) puts("");
4622b15cb3dSCy Schubert 		printf("%s%s: ", cur_test_prefix, cur_test_name);
4632b15cb3dSCy Schubert 		cur_test_name = NULL;
4642b15cb3dSCy Schubert 	}
4652b15cb3dSCy Schubert 	cur_test_outcome = 0;
4662b15cb3dSCy Schubert }
4672b15cb3dSCy Schubert 
4682b15cb3dSCy Schubert void
4692b15cb3dSCy Schubert tinytest_set_test_skipped_(void)
4702b15cb3dSCy Schubert {
4712b15cb3dSCy Schubert 	if (cur_test_outcome==OK)
4722b15cb3dSCy Schubert 		cur_test_outcome = SKIP;
4732b15cb3dSCy Schubert }
4742b15cb3dSCy Schubert 
475*a25439b6SCy Schubert char *
476*a25439b6SCy Schubert tinytest_format_hex_(const void *val_, unsigned long len)
477*a25439b6SCy Schubert {
478*a25439b6SCy Schubert 	const unsigned char *val = val_;
479*a25439b6SCy Schubert 	char *result, *cp;
480*a25439b6SCy Schubert 	size_t i;
481*a25439b6SCy Schubert 
482*a25439b6SCy Schubert 	if (!val)
483*a25439b6SCy Schubert 		return strdup("null");
484*a25439b6SCy Schubert 	if (!(result = malloc(len*2+1)))
485*a25439b6SCy Schubert 		return strdup("<allocation failure>");
486*a25439b6SCy Schubert 	cp = result;
487*a25439b6SCy Schubert 	for (i=0;i<len;++i) {
488*a25439b6SCy Schubert 		*cp++ = "0123456789ABCDEF"[val[i] >> 4];
489*a25439b6SCy Schubert 		*cp++ = "0123456789ABCDEF"[val[i] & 0x0f];
490*a25439b6SCy Schubert 	}
491*a25439b6SCy Schubert 	*cp = 0;
492*a25439b6SCy Schubert 	return result;
493*a25439b6SCy Schubert }
494