xref: /netbsd-src/external/bsd/libevent/dist/test/tinytest.c (revision 657871a79c9a2060a6255a242fa1a1ef76b56ec6)
1*657871a7Schristos /*	$NetBSD: tinytest.c,v 1.1.1.3 2021/04/07 02:43:15 christos Exp $	*/
26ecf6635Schristos /* tinytest.c -- Copyright 2009-2012 Nick Mathewson
36ecf6635Schristos  *
46ecf6635Schristos  * Redistribution and use in source and binary forms, with or without
56ecf6635Schristos  * modification, are permitted provided that the following conditions
66ecf6635Schristos  * are met:
76ecf6635Schristos  * 1. Redistributions of source code must retain the above copyright
86ecf6635Schristos  *    notice, this list of conditions and the following disclaimer.
96ecf6635Schristos  * 2. Redistributions in binary form must reproduce the above copyright
106ecf6635Schristos  *    notice, this list of conditions and the following disclaimer in the
116ecf6635Schristos  *    documentation and/or other materials provided with the distribution.
126ecf6635Schristos  * 3. The name of the author may not be used to endorse or promote products
136ecf6635Schristos  *    derived from this software without specific prior written permission.
146ecf6635Schristos  *
156ecf6635Schristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
166ecf6635Schristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
176ecf6635Schristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
186ecf6635Schristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
196ecf6635Schristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
206ecf6635Schristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
216ecf6635Schristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
226ecf6635Schristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
236ecf6635Schristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
246ecf6635Schristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
256ecf6635Schristos  */
26805a1ce9Schristos #ifdef TINYTEST_LOCAL
27805a1ce9Schristos #include "tinytest_local.h"
28805a1ce9Schristos #endif
296ecf6635Schristos 
306ecf6635Schristos #include <stdio.h>
316ecf6635Schristos #include <stdlib.h>
326ecf6635Schristos #include <string.h>
336ecf6635Schristos #include <assert.h>
346ecf6635Schristos 
35805a1ce9Schristos #ifndef NO_FORKING
366ecf6635Schristos 
37805a1ce9Schristos #ifdef _WIN32
386ecf6635Schristos #include <windows.h>
396ecf6635Schristos #else
406ecf6635Schristos #include <sys/types.h>
416ecf6635Schristos #include <sys/wait.h>
426ecf6635Schristos #include <unistd.h>
436ecf6635Schristos #endif
446ecf6635Schristos 
456ecf6635Schristos #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
466ecf6635Schristos #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \
476ecf6635Schristos     __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070)
486ecf6635Schristos /* Workaround for a stupid bug in OSX 10.6 */
496ecf6635Schristos #define FORK_BREAKS_GCOV
506ecf6635Schristos #include <vproc.h>
516ecf6635Schristos #endif
526ecf6635Schristos #endif
536ecf6635Schristos 
54805a1ce9Schristos #endif /* !NO_FORKING */
55805a1ce9Schristos 
566ecf6635Schristos #ifndef __GNUC__
576ecf6635Schristos #define __attribute__(x)
586ecf6635Schristos #endif
596ecf6635Schristos 
606ecf6635Schristos #include "tinytest.h"
616ecf6635Schristos #include "tinytest_macros.h"
626ecf6635Schristos 
636ecf6635Schristos #define LONGEST_TEST_NAME 16384
64*657871a7Schristos #define DEFAULT_TESTCASE_TIMEOUT 30U
65*657871a7Schristos #define MAGIC_EXITCODE 42
666ecf6635Schristos 
676ecf6635Schristos static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/
686ecf6635Schristos static int n_ok = 0; /**< Number of tests that have passed */
696ecf6635Schristos static int n_bad = 0; /**< Number of tests that have failed. */
706ecf6635Schristos static int n_skipped = 0; /**< Number of tests that have been skipped. */
716ecf6635Schristos 
726ecf6635Schristos static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/
736ecf6635Schristos static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */
746ecf6635Schristos static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */
75*657871a7Schristos static unsigned int opt_timeout = DEFAULT_TESTCASE_TIMEOUT; /**< Timeout for every test (using alarm()) */
766ecf6635Schristos const char *verbosity_flag = "";
776ecf6635Schristos 
78805a1ce9Schristos const struct testlist_alias_t *cfg_aliases=NULL;
79805a1ce9Schristos 
806ecf6635Schristos enum outcome { SKIP=2, OK=1, FAIL=0 };
816ecf6635Schristos static enum outcome cur_test_outcome = 0;
826ecf6635Schristos const char *cur_test_prefix = NULL; /**< prefix of the current test group */
836ecf6635Schristos /** Name of the current test, if we haven't logged is yet. Used for --quiet */
846ecf6635Schristos const char *cur_test_name = NULL;
856ecf6635Schristos 
866ecf6635Schristos static void usage(struct testgroup_t *groups, int list_groups)
876ecf6635Schristos 	__attribute__((noreturn));
88805a1ce9Schristos static int process_test_option(struct testgroup_t *groups, const char *test);
896ecf6635Schristos 
90*657871a7Schristos #ifdef _WIN32
91*657871a7Schristos /* Copy of argv[0] for win32. */
92*657871a7Schristos static char commandname[MAX_PATH+1];
93*657871a7Schristos 
94*657871a7Schristos struct timeout_thread_args {
95*657871a7Schristos 	const testcase_fn *fn;
96*657871a7Schristos 	void *env;
97*657871a7Schristos };
98*657871a7Schristos 
99*657871a7Schristos static DWORD WINAPI
timeout_thread_proc_(LPVOID arg)100*657871a7Schristos timeout_thread_proc_(LPVOID arg)
101*657871a7Schristos {
102*657871a7Schristos 	struct timeout_thread_args *args = arg;
103*657871a7Schristos 	(*(args->fn))(args->env);
104*657871a7Schristos 	ExitThread(cur_test_outcome == FAIL ? 1 : 0);
105*657871a7Schristos }
106*657871a7Schristos 
107*657871a7Schristos static enum outcome
testcase_run_in_thread_(const struct testcase_t * testcase,void * env)108*657871a7Schristos testcase_run_in_thread_(const struct testcase_t *testcase, void *env)
109*657871a7Schristos {
110*657871a7Schristos 	/* We will never run testcase in a new thread when the
111*657871a7Schristos 	timeout is set to zero */
112*657871a7Schristos 	assert(opt_timeout);
113*657871a7Schristos 	DWORD ret, tid;
114*657871a7Schristos 	HANDLE handle;
115*657871a7Schristos 	struct timeout_thread_args args = {
116*657871a7Schristos 		&(testcase->fn),
117*657871a7Schristos 		env
118*657871a7Schristos 	};
119*657871a7Schristos 
120*657871a7Schristos 	handle =CreateThread(NULL, 0, timeout_thread_proc_,
121*657871a7Schristos 		(LPVOID)&args, 0, &tid);
122*657871a7Schristos 	ret = WaitForSingleObject(handle, opt_timeout * 1000U);
123*657871a7Schristos 	if (ret == WAIT_OBJECT_0) {
124*657871a7Schristos 		ret = 0;
125*657871a7Schristos 		if (!GetExitCodeThread(handle, &ret)) {
126*657871a7Schristos 			printf("GetExitCodeThread failed\n");
127*657871a7Schristos 			ret = 1;
128*657871a7Schristos 		}
129*657871a7Schristos 	} else if (ret == WAIT_TIMEOUT)	{
130*657871a7Schristos 		printf("timeout\n");
131*657871a7Schristos 	} else {
132*657871a7Schristos 		printf("Wait failed\n");
133*657871a7Schristos 	}
134*657871a7Schristos 	CloseHandle(handle);
135*657871a7Schristos 	if (ret == 0)
136*657871a7Schristos 		return OK;
137*657871a7Schristos 	else if (ret == MAGIC_EXITCODE)
138*657871a7Schristos 		return SKIP;
139*657871a7Schristos 	else
140*657871a7Schristos 		return FAIL;
141*657871a7Schristos }
142*657871a7Schristos #else
testcase_set_timeout_(void)143*657871a7Schristos static unsigned int testcase_set_timeout_(void)
144*657871a7Schristos {
145*657871a7Schristos 	return alarm(opt_timeout);
146*657871a7Schristos }
147*657871a7Schristos 
testcase_reset_timeout_(void)148*657871a7Schristos static unsigned int testcase_reset_timeout_(void)
149*657871a7Schristos {
150*657871a7Schristos 	return alarm(0);
151*657871a7Schristos }
152*657871a7Schristos #endif
153*657871a7Schristos 
1546ecf6635Schristos static enum outcome
testcase_run_bare_(const struct testcase_t * testcase)155805a1ce9Schristos testcase_run_bare_(const struct testcase_t *testcase)
1566ecf6635Schristos {
1576ecf6635Schristos 	void *env = NULL;
1586ecf6635Schristos 	int outcome;
1596ecf6635Schristos 	if (testcase->setup) {
1606ecf6635Schristos 		env = testcase->setup->setup_fn(testcase);
1616ecf6635Schristos 		if (!env)
1626ecf6635Schristos 			return FAIL;
1636ecf6635Schristos 		else if (env == (void*)TT_SKIP)
1646ecf6635Schristos 			return SKIP;
1656ecf6635Schristos 	}
1666ecf6635Schristos 
1676ecf6635Schristos 	cur_test_outcome = OK;
168*657871a7Schristos 	{
169*657871a7Schristos 		if (opt_timeout) {
170*657871a7Schristos #ifdef _WIN32
171*657871a7Schristos 			cur_test_outcome = testcase_run_in_thread_(testcase, env);
172*657871a7Schristos #else
173*657871a7Schristos 			testcase_set_timeout_();
1746ecf6635Schristos 			testcase->fn(env);
175*657871a7Schristos 			testcase_reset_timeout_();
176*657871a7Schristos #endif
177*657871a7Schristos 		} else {
178*657871a7Schristos 			testcase->fn(env);
179*657871a7Schristos 		}
180*657871a7Schristos 	}
1816ecf6635Schristos 	outcome = cur_test_outcome;
1826ecf6635Schristos 
1836ecf6635Schristos 	if (testcase->setup) {
1846ecf6635Schristos 		if (testcase->setup->cleanup_fn(testcase, env) == 0)
1856ecf6635Schristos 			outcome = FAIL;
1866ecf6635Schristos 	}
1876ecf6635Schristos 
1886ecf6635Schristos 	return outcome;
1896ecf6635Schristos }
1906ecf6635Schristos 
1916ecf6635Schristos 
192805a1ce9Schristos #ifndef NO_FORKING
193805a1ce9Schristos 
1946ecf6635Schristos static enum outcome
testcase_run_forked_(const struct testgroup_t * group,const struct testcase_t * testcase)195805a1ce9Schristos testcase_run_forked_(const struct testgroup_t *group,
1966ecf6635Schristos 		     const struct testcase_t *testcase)
1976ecf6635Schristos {
198805a1ce9Schristos #ifdef _WIN32
1996ecf6635Schristos 	/* Fork? On Win32?  How primitive!  We'll do what the smart kids do:
2006ecf6635Schristos 	   we'll invoke our own exe (whose name we recall from the command
2016ecf6635Schristos 	   line) with a command line that tells it to run just the test we
2026ecf6635Schristos 	   want, and this time without forking.
2036ecf6635Schristos 
2046ecf6635Schristos 	   (No, threads aren't an option.  The whole point of forking is to
2056ecf6635Schristos 	   share no state between tests.)
2066ecf6635Schristos 	 */
2076ecf6635Schristos 	int ok;
2086ecf6635Schristos 	char buffer[LONGEST_TEST_NAME+256];
2096ecf6635Schristos 	STARTUPINFOA si;
2106ecf6635Schristos 	PROCESS_INFORMATION info;
211*657871a7Schristos 	DWORD ret;
2126ecf6635Schristos 
2136ecf6635Schristos 	if (!in_tinytest_main) {
214805a1ce9Schristos 		printf("\nERROR.  On Windows, testcase_run_forked_ must be"
2156ecf6635Schristos 		       " called from within tinytest_main.\n");
2166ecf6635Schristos 		abort();
2176ecf6635Schristos 	}
2186ecf6635Schristos 	if (opt_verbosity>0)
2196ecf6635Schristos 		printf("[forking] ");
2206ecf6635Schristos 
221*657871a7Schristos 	snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s --timeout 0 %s%s",
2226ecf6635Schristos 		 commandname, verbosity_flag, group->prefix, testcase->name);
2236ecf6635Schristos 
2246ecf6635Schristos 	memset(&si, 0, sizeof(si));
2256ecf6635Schristos 	memset(&info, 0, sizeof(info));
2266ecf6635Schristos 	si.cb = sizeof(si);
2276ecf6635Schristos 
2286ecf6635Schristos 	ok = CreateProcessA(commandname, buffer, NULL, NULL, 0,
2296ecf6635Schristos 			   0, NULL, NULL, &si, &info);
2306ecf6635Schristos 	if (!ok) {
2316ecf6635Schristos 		printf("CreateProcess failed!\n");
232*657871a7Schristos 		return FAIL;
2336ecf6635Schristos 	}
234*657871a7Schristos 	ret = WaitForSingleObject(info.hProcess,
235*657871a7Schristos 		(opt_timeout ? opt_timeout * 1000U : INFINITE));
236*657871a7Schristos 
237*657871a7Schristos 	if (ret == WAIT_OBJECT_0) {
238*657871a7Schristos 		GetExitCodeProcess(info.hProcess, &ret);
239*657871a7Schristos 	} else if (ret == WAIT_TIMEOUT) {
240*657871a7Schristos 		printf("timeout\n");
241*657871a7Schristos 	} else {
242*657871a7Schristos 		printf("Wait failed\n");
243*657871a7Schristos 	}
2446ecf6635Schristos 	CloseHandle(info.hProcess);
2456ecf6635Schristos 	CloseHandle(info.hThread);
246*657871a7Schristos 	if (ret == 0)
2476ecf6635Schristos 		return OK;
248*657871a7Schristos 	else if (ret == MAGIC_EXITCODE)
2496ecf6635Schristos 		return SKIP;
2506ecf6635Schristos 	else
2516ecf6635Schristos 		return FAIL;
2526ecf6635Schristos #else
2536ecf6635Schristos 	int outcome_pipe[2];
2546ecf6635Schristos 	pid_t pid;
2556ecf6635Schristos 	(void)group;
2566ecf6635Schristos 
2576ecf6635Schristos 	if (pipe(outcome_pipe))
2586ecf6635Schristos 		perror("opening pipe");
2596ecf6635Schristos 
2606ecf6635Schristos 	if (opt_verbosity>0)
2616ecf6635Schristos 		printf("[forking] ");
2626ecf6635Schristos 	pid = fork();
2636ecf6635Schristos #ifdef FORK_BREAKS_GCOV
2646ecf6635Schristos 	vproc_transaction_begin(0);
2656ecf6635Schristos #endif
2666ecf6635Schristos 	if (!pid) {
2676ecf6635Schristos 		/* child. */
2686ecf6635Schristos 		int test_r, write_r;
2696ecf6635Schristos 		char b[1];
2706ecf6635Schristos 		close(outcome_pipe[0]);
271805a1ce9Schristos 		test_r = testcase_run_bare_(testcase);
2726ecf6635Schristos 		assert(0<=(int)test_r && (int)test_r<=2);
2736ecf6635Schristos 		b[0] = "NYS"[test_r];
2746ecf6635Schristos 		write_r = (int)write(outcome_pipe[1], b, 1);
2756ecf6635Schristos 		if (write_r != 1) {
2766ecf6635Schristos 			perror("write outcome to pipe");
2776ecf6635Schristos 			exit(1);
2786ecf6635Schristos 		}
2796ecf6635Schristos 		exit(0);
2806ecf6635Schristos 		return FAIL; /* unreachable */
2816ecf6635Schristos 	} else {
2826ecf6635Schristos 		/* parent */
283*657871a7Schristos 		int status, r, exitcode;
2846ecf6635Schristos 		char b[1];
2856ecf6635Schristos 		/* Close this now, so that if the other side closes it,
2866ecf6635Schristos 		 * our read fails. */
2876ecf6635Schristos 		close(outcome_pipe[1]);
2886ecf6635Schristos 		r = (int)read(outcome_pipe[0], b, 1);
2896ecf6635Schristos 		if (r == 0) {
2906ecf6635Schristos 			printf("[Lost connection!] ");
291*657871a7Schristos 			return FAIL;
2926ecf6635Schristos 		} else if (r != 1) {
2936ecf6635Schristos 			perror("read outcome from pipe");
2946ecf6635Schristos 		}
2956ecf6635Schristos 		waitpid(pid, &status, 0);
296*657871a7Schristos 		exitcode = WEXITSTATUS(status);
2976ecf6635Schristos 		close(outcome_pipe[0]);
298*657871a7Schristos 		if (opt_verbosity>1)
299*657871a7Schristos 			printf("%s%s: exited with %i (%i)\n", group->prefix, testcase->name, exitcode, status);
300*657871a7Schristos 		if (exitcode != 0)
301*657871a7Schristos 		{
302*657871a7Schristos 			printf("[atexit failure!] ");
303*657871a7Schristos 			return FAIL;
304*657871a7Schristos 		}
3056ecf6635Schristos 		return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
3066ecf6635Schristos 	}
3076ecf6635Schristos #endif
3086ecf6635Schristos }
3096ecf6635Schristos 
310805a1ce9Schristos #endif /* !NO_FORKING */
311805a1ce9Schristos 
3126ecf6635Schristos int
testcase_run_one(const struct testgroup_t * group,const struct testcase_t * testcase)3136ecf6635Schristos testcase_run_one(const struct testgroup_t *group,
3146ecf6635Schristos 		 const struct testcase_t *testcase)
3156ecf6635Schristos {
3166ecf6635Schristos 	enum outcome outcome;
3176ecf6635Schristos 
318805a1ce9Schristos 	if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) {
3196ecf6635Schristos 		if (opt_verbosity>0)
320805a1ce9Schristos 			printf("%s%s: %s\n",
321805a1ce9Schristos 			   group->prefix, testcase->name,
322805a1ce9Schristos 			   (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED");
3236ecf6635Schristos 		++n_skipped;
3246ecf6635Schristos 		return SKIP;
3256ecf6635Schristos 	}
3266ecf6635Schristos 
3276ecf6635Schristos 	if (opt_verbosity>0 && !opt_forked) {
3286ecf6635Schristos 		printf("%s%s: ", group->prefix, testcase->name);
3296ecf6635Schristos 	} else {
3306ecf6635Schristos 		if (opt_verbosity==0) printf(".");
3316ecf6635Schristos 		cur_test_prefix = group->prefix;
3326ecf6635Schristos 		cur_test_name = testcase->name;
3336ecf6635Schristos 	}
3346ecf6635Schristos 
335805a1ce9Schristos #ifndef NO_FORKING
3366ecf6635Schristos 	if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
337805a1ce9Schristos 		outcome = testcase_run_forked_(group, testcase);
3386ecf6635Schristos 	} else {
339805a1ce9Schristos #else
340805a1ce9Schristos 	{
341805a1ce9Schristos #endif
342805a1ce9Schristos 		outcome = testcase_run_bare_(testcase);
3436ecf6635Schristos 	}
3446ecf6635Schristos 
3456ecf6635Schristos 	if (outcome == OK) {
3466ecf6635Schristos 		if (opt_verbosity>0 && !opt_forked)
3476ecf6635Schristos 			puts(opt_verbosity==1?"OK":"");
3486ecf6635Schristos 	} else if (outcome == SKIP) {
3496ecf6635Schristos 		if (opt_verbosity>0 && !opt_forked)
3506ecf6635Schristos 			puts("SKIPPED");
3516ecf6635Schristos 	} else {
3526ecf6635Schristos 		if (!opt_forked)
3536ecf6635Schristos 			printf("\n  [%s FAILED]\n", testcase->name);
3546ecf6635Schristos 	}
3556ecf6635Schristos 
3566ecf6635Schristos 	if (opt_forked) {
3576ecf6635Schristos 		exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
3586ecf6635Schristos 		return 1; /* unreachable */
3596ecf6635Schristos 	} else {
3606ecf6635Schristos 		return (int)outcome;
3616ecf6635Schristos 	}
3626ecf6635Schristos }
3636ecf6635Schristos 
3646ecf6635Schristos int
365805a1ce9Schristos tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag)
3666ecf6635Schristos {
3676ecf6635Schristos 	int i, j;
3686ecf6635Schristos 	size_t length = LONGEST_TEST_NAME;
3696ecf6635Schristos 	char fullname[LONGEST_TEST_NAME];
3706ecf6635Schristos 	int found=0;
3716ecf6635Schristos 	if (strstr(arg, ".."))
3726ecf6635Schristos 		length = strstr(arg,"..")-arg;
3736ecf6635Schristos 	for (i=0; groups[i].prefix; ++i) {
3746ecf6635Schristos 		for (j=0; groups[i].cases[j].name; ++j) {
375805a1ce9Schristos 			struct testcase_t *testcase = &groups[i].cases[j];
3766ecf6635Schristos 			snprintf(fullname, sizeof(fullname), "%s%s",
377805a1ce9Schristos 				 groups[i].prefix, testcase->name);
378805a1ce9Schristos 			if (!flag) { /* Hack! */
379805a1ce9Schristos 				printf("    %s", fullname);
380805a1ce9Schristos 				if (testcase->flags & TT_OFF_BY_DEFAULT)
381805a1ce9Schristos 					puts("   (Off by default)");
382805a1ce9Schristos 				else if (testcase->flags & TT_SKIP)
383805a1ce9Schristos 					puts("  (DISABLED)");
384805a1ce9Schristos 				else
385805a1ce9Schristos 					puts("");
386805a1ce9Schristos 			}
3876ecf6635Schristos 			if (!strncmp(fullname, arg, length)) {
388805a1ce9Schristos 				if (set)
389805a1ce9Schristos 					testcase->flags |= flag;
390805a1ce9Schristos 				else
391805a1ce9Schristos 					testcase->flags &= ~flag;
3926ecf6635Schristos 				++found;
3936ecf6635Schristos 			}
3946ecf6635Schristos 		}
3956ecf6635Schristos 	}
3966ecf6635Schristos 	return found;
3976ecf6635Schristos }
3986ecf6635Schristos 
3996ecf6635Schristos static void
4006ecf6635Schristos usage(struct testgroup_t *groups, int list_groups)
4016ecf6635Schristos {
402*657871a7Schristos 	puts("Options are: [--verbose|--quiet|--terse] [--no-fork] [--timeout <sec>]");
4036ecf6635Schristos 	puts("  Specify tests by name, or using a prefix ending with '..'");
404805a1ce9Schristos 	puts("  To skip a test, prefix its name with a colon.");
405805a1ce9Schristos 	puts("  To enable a disabled test, prefix its name with a plus.");
4066ecf6635Schristos 	puts("  Use --list-tests for a list of tests.");
4076ecf6635Schristos 	if (list_groups) {
4086ecf6635Schristos 		puts("Known tests are:");
409805a1ce9Schristos 		tinytest_set_flag_(groups, "..", 1, 0);
4106ecf6635Schristos 	}
4116ecf6635Schristos 	exit(0);
4126ecf6635Schristos }
4136ecf6635Schristos 
414805a1ce9Schristos static int
415805a1ce9Schristos process_test_alias(struct testgroup_t *groups, const char *test)
416805a1ce9Schristos {
417805a1ce9Schristos 	int i, j, n, r;
418805a1ce9Schristos 	for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) {
419805a1ce9Schristos 		if (!strcmp(cfg_aliases[i].name, test)) {
420805a1ce9Schristos 			n = 0;
421805a1ce9Schristos 			for (j = 0; cfg_aliases[i].tests[j]; ++j) {
422805a1ce9Schristos 				r = process_test_option(groups, cfg_aliases[i].tests[j]);
423805a1ce9Schristos 				if (r<0)
424805a1ce9Schristos 					return -1;
425805a1ce9Schristos 				n += r;
426805a1ce9Schristos 			}
427805a1ce9Schristos 			return n;
428805a1ce9Schristos 		}
429805a1ce9Schristos 	}
430805a1ce9Schristos 	printf("No such test alias as @%s!",test);
431805a1ce9Schristos 	return -1;
432805a1ce9Schristos }
433805a1ce9Schristos 
434805a1ce9Schristos static int
435805a1ce9Schristos process_test_option(struct testgroup_t *groups, const char *test)
436805a1ce9Schristos {
437805a1ce9Schristos 	int flag = TT_ENABLED_;
438805a1ce9Schristos 	int n = 0;
439805a1ce9Schristos 	if (test[0] == '@') {
440805a1ce9Schristos 		return process_test_alias(groups, test + 1);
441805a1ce9Schristos 	} else if (test[0] == ':') {
442805a1ce9Schristos 		++test;
443805a1ce9Schristos 		flag = TT_SKIP;
444805a1ce9Schristos 	} else if (test[0] == '+') {
445805a1ce9Schristos 		++test;
446805a1ce9Schristos 		++n;
447805a1ce9Schristos 		if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) {
448805a1ce9Schristos 			printf("No such test as %s!\n", test);
449805a1ce9Schristos 			return -1;
450805a1ce9Schristos 		}
451805a1ce9Schristos 	} else {
452805a1ce9Schristos 		++n;
453805a1ce9Schristos 	}
454805a1ce9Schristos 	if (!tinytest_set_flag_(groups, test, 1, flag)) {
455805a1ce9Schristos 		printf("No such test as %s!\n", test);
456805a1ce9Schristos 		return -1;
457805a1ce9Schristos 	}
458805a1ce9Schristos 	return n;
459805a1ce9Schristos }
460805a1ce9Schristos 
461805a1ce9Schristos void
462805a1ce9Schristos tinytest_set_aliases(const struct testlist_alias_t *aliases)
463805a1ce9Schristos {
464805a1ce9Schristos 	cfg_aliases = aliases;
465805a1ce9Schristos }
466805a1ce9Schristos 
4676ecf6635Schristos int
4686ecf6635Schristos tinytest_main(int c, const char **v, struct testgroup_t *groups)
4696ecf6635Schristos {
4706ecf6635Schristos 	int i, j, n=0;
4716ecf6635Schristos 
472805a1ce9Schristos #ifdef _WIN32
4736ecf6635Schristos 	const char *sp = strrchr(v[0], '.');
4746ecf6635Schristos 	const char *extension = "";
4756ecf6635Schristos 	if (!sp || stricmp(sp, ".exe"))
4766ecf6635Schristos 		extension = ".exe"; /* Add an exe so CreateProcess will work */
4776ecf6635Schristos 	snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension);
4786ecf6635Schristos 	commandname[MAX_PATH]='\0';
4796ecf6635Schristos #endif
4806ecf6635Schristos 	for (i=1; i<c; ++i) {
4816ecf6635Schristos 		if (v[i][0] == '-') {
4826ecf6635Schristos 			if (!strcmp(v[i], "--RUNNING-FORKED")) {
4836ecf6635Schristos 				opt_forked = 1;
4846ecf6635Schristos 			} else if (!strcmp(v[i], "--no-fork")) {
4856ecf6635Schristos 				opt_nofork = 1;
4866ecf6635Schristos 			} else if (!strcmp(v[i], "--quiet")) {
4876ecf6635Schristos 				opt_verbosity = -1;
4886ecf6635Schristos 				verbosity_flag = "--quiet";
4896ecf6635Schristos 			} else if (!strcmp(v[i], "--verbose")) {
4906ecf6635Schristos 				opt_verbosity = 2;
4916ecf6635Schristos 				verbosity_flag = "--verbose";
4926ecf6635Schristos 			} else if (!strcmp(v[i], "--terse")) {
4936ecf6635Schristos 				opt_verbosity = 0;
4946ecf6635Schristos 				verbosity_flag = "--terse";
4956ecf6635Schristos 			} else if (!strcmp(v[i], "--help")) {
4966ecf6635Schristos 				usage(groups, 0);
4976ecf6635Schristos 			} else if (!strcmp(v[i], "--list-tests")) {
4986ecf6635Schristos 				usage(groups, 1);
499*657871a7Schristos 			} else if (!strcmp(v[i], "--timeout")) {
500*657871a7Schristos 				++i;
501*657871a7Schristos 				if (i >= c) {
502*657871a7Schristos 					fprintf(stderr, "--timeout requires argument\n");
503*657871a7Schristos 					return -1;
504*657871a7Schristos 				}
505*657871a7Schristos 				opt_timeout = (unsigned)atoi(v[i]);
5066ecf6635Schristos 			} else {
507*657871a7Schristos 				fprintf(stderr, "Unknown option %s. Try --help\n", v[i]);
5086ecf6635Schristos 				return -1;
5096ecf6635Schristos 			}
5106ecf6635Schristos 		} else {
511805a1ce9Schristos 			int r = process_test_option(groups, v[i]);
512805a1ce9Schristos 			if (r<0)
5136ecf6635Schristos 				return -1;
514805a1ce9Schristos 			n += r;
5156ecf6635Schristos 		}
5166ecf6635Schristos 	}
5176ecf6635Schristos 	if (!n)
518805a1ce9Schristos 		tinytest_set_flag_(groups, "..", 1, TT_ENABLED_);
5196ecf6635Schristos 
520805a1ce9Schristos #ifdef _IONBF
5216ecf6635Schristos 	setvbuf(stdout, NULL, _IONBF, 0);
522805a1ce9Schristos #endif
5236ecf6635Schristos 
5246ecf6635Schristos 	++in_tinytest_main;
525*657871a7Schristos 	for (i = 0; groups[i].prefix; ++i) {
526*657871a7Schristos 		struct testgroup_t *group = &groups[i];
527*657871a7Schristos 		for (j = 0; group->cases[j].name; ++j) {
528*657871a7Schristos 			struct testcase_t *testcase = &group->cases[j];
529*657871a7Schristos 			int test_attempts = 3;
530*657871a7Schristos 			int test_ret_err;
531*657871a7Schristos 
532*657871a7Schristos 			if (!(testcase->flags & TT_ENABLED_))
533*657871a7Schristos 				continue;
534*657871a7Schristos 
535*657871a7Schristos 			for (;;) {
536*657871a7Schristos 				test_ret_err = testcase_run_one(group, testcase);
537*657871a7Schristos 
538*657871a7Schristos 				if (test_ret_err == OK)
539*657871a7Schristos 					break;
540*657871a7Schristos 				if (!(testcase->flags & TT_RETRIABLE))
541*657871a7Schristos 					break;
542*657871a7Schristos 				printf("\n  [RETRYING %s (%i)]\n", testcase->name, test_attempts);
543*657871a7Schristos 				if (!test_attempts--)
544*657871a7Schristos 					break;
545*657871a7Schristos 			}
546*657871a7Schristos 
547*657871a7Schristos 			switch (test_ret_err) {
548*657871a7Schristos 				case OK:   ++n_ok;      break;
549*657871a7Schristos 				case SKIP: ++n_skipped; break;
550*657871a7Schristos 				default:   ++n_bad;     break;
551*657871a7Schristos 			}
552*657871a7Schristos 		}
553*657871a7Schristos 	}
5546ecf6635Schristos 
5556ecf6635Schristos 	--in_tinytest_main;
5566ecf6635Schristos 
5576ecf6635Schristos 	if (opt_verbosity==0)
5586ecf6635Schristos 		puts("");
5596ecf6635Schristos 
5606ecf6635Schristos 	if (n_bad)
5616ecf6635Schristos 		printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
5626ecf6635Schristos 		       n_bad+n_ok,n_skipped);
5636ecf6635Schristos 	else if (opt_verbosity >= 1)
5646ecf6635Schristos 		printf("%d tests ok.  (%d skipped)\n", n_ok, n_skipped);
5656ecf6635Schristos 
5666ecf6635Schristos 	return (n_bad == 0) ? 0 : 1;
5676ecf6635Schristos }
5686ecf6635Schristos 
5696ecf6635Schristos int
570805a1ce9Schristos tinytest_get_verbosity_(void)
5716ecf6635Schristos {
5726ecf6635Schristos 	return opt_verbosity;
5736ecf6635Schristos }
5746ecf6635Schristos 
5756ecf6635Schristos void
576805a1ce9Schristos tinytest_set_test_failed_(void)
5776ecf6635Schristos {
5786ecf6635Schristos 	if (opt_verbosity <= 0 && cur_test_name) {
5796ecf6635Schristos 		if (opt_verbosity==0) puts("");
5806ecf6635Schristos 		printf("%s%s: ", cur_test_prefix, cur_test_name);
5816ecf6635Schristos 		cur_test_name = NULL;
5826ecf6635Schristos 	}
583*657871a7Schristos 	cur_test_outcome = FAIL;
5846ecf6635Schristos }
5856ecf6635Schristos 
5866ecf6635Schristos void
587805a1ce9Schristos tinytest_set_test_skipped_(void)
5886ecf6635Schristos {
5896ecf6635Schristos 	if (cur_test_outcome==OK)
5906ecf6635Schristos 		cur_test_outcome = SKIP;
5916ecf6635Schristos }
5926ecf6635Schristos 
593805a1ce9Schristos char *
594805a1ce9Schristos tinytest_format_hex_(const void *val_, unsigned long len)
595805a1ce9Schristos {
596805a1ce9Schristos 	const unsigned char *val = val_;
597805a1ce9Schristos 	char *result, *cp;
598805a1ce9Schristos 	size_t i;
599805a1ce9Schristos 
600805a1ce9Schristos 	if (!val)
601805a1ce9Schristos 		return strdup("null");
602805a1ce9Schristos 	if (!(result = malloc(len*2+1)))
603805a1ce9Schristos 		return strdup("<allocation failure>");
604805a1ce9Schristos 	cp = result;
605805a1ce9Schristos 	for (i=0;i<len;++i) {
606805a1ce9Schristos 		*cp++ = "0123456789ABCDEF"[val[i] >> 4];
607805a1ce9Schristos 		*cp++ = "0123456789ABCDEF"[val[i] & 0x0f];
608805a1ce9Schristos 	}
609805a1ce9Schristos 	*cp = 0;
610805a1ce9Schristos 	return result;
611805a1ce9Schristos }
612