xref: /netbsd-src/external/bsd/ntp/dist/sntp/libevent/test/tinytest.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: tinytest.c,v 1.7 2024/08/18 20:47:24 christos Exp $	*/
28585484eSchristos 
38585484eSchristos /* tinytest.c -- Copyright 2009-2012 Nick Mathewson
48585484eSchristos  *
58585484eSchristos  * Redistribution and use in source and binary forms, with or without
68585484eSchristos  * modification, are permitted provided that the following conditions
78585484eSchristos  * are met:
88585484eSchristos  * 1. Redistributions of source code must retain the above copyright
98585484eSchristos  *    notice, this list of conditions and the following disclaimer.
108585484eSchristos  * 2. Redistributions in binary form must reproduce the above copyright
118585484eSchristos  *    notice, this list of conditions and the following disclaimer in the
128585484eSchristos  *    documentation and/or other materials provided with the distribution.
138585484eSchristos  * 3. The name of the author may not be used to endorse or promote products
148585484eSchristos  *    derived from this software without specific prior written permission.
158585484eSchristos  *
168585484eSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
178585484eSchristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
188585484eSchristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
198585484eSchristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
208585484eSchristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
218585484eSchristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
228585484eSchristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
238585484eSchristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
248585484eSchristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
258585484eSchristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
268585484eSchristos  */
278585484eSchristos #ifdef TINYTEST_LOCAL
288585484eSchristos #include "tinytest_local.h"
298585484eSchristos #endif
308585484eSchristos 
318585484eSchristos #include <stdio.h>
328585484eSchristos #include <stdlib.h>
338585484eSchristos #include <string.h>
348585484eSchristos #include <assert.h>
358585484eSchristos 
367476e6e4Schristos #ifndef NO_FORKING
377476e6e4Schristos 
388585484eSchristos #ifdef _WIN32
398585484eSchristos #include <windows.h>
408585484eSchristos #else
418585484eSchristos #include <sys/types.h>
428585484eSchristos #include <sys/wait.h>
438585484eSchristos #include <unistd.h>
448585484eSchristos #endif
458585484eSchristos 
468585484eSchristos #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
478585484eSchristos #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \
488585484eSchristos     __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070)
498585484eSchristos /* Workaround for a stupid bug in OSX 10.6 */
508585484eSchristos #define FORK_BREAKS_GCOV
518585484eSchristos #include <vproc.h>
528585484eSchristos #endif
538585484eSchristos #endif
548585484eSchristos 
557476e6e4Schristos #endif /* !NO_FORKING */
567476e6e4Schristos 
578585484eSchristos #ifndef __GNUC__
588585484eSchristos #define __attribute__(x)
598585484eSchristos #endif
608585484eSchristos 
618585484eSchristos #include "tinytest.h"
628585484eSchristos #include "tinytest_macros.h"
638585484eSchristos 
648585484eSchristos #define LONGEST_TEST_NAME 16384
65*eabc0478Schristos #define DEFAULT_TESTCASE_TIMEOUT 30U
66*eabc0478Schristos #define MAGIC_EXITCODE 42
678585484eSchristos 
688585484eSchristos static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/
698585484eSchristos static int n_ok = 0; /**< Number of tests that have passed */
708585484eSchristos static int n_bad = 0; /**< Number of tests that have failed. */
718585484eSchristos static int n_skipped = 0; /**< Number of tests that have been skipped. */
728585484eSchristos 
738585484eSchristos static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/
748585484eSchristos static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */
758585484eSchristos static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */
76*eabc0478Schristos static unsigned int opt_timeout = DEFAULT_TESTCASE_TIMEOUT; /**< Timeout for every test (using alarm()) */
778585484eSchristos const char *verbosity_flag = "";
788585484eSchristos 
798585484eSchristos const struct testlist_alias_t *cfg_aliases=NULL;
808585484eSchristos 
818585484eSchristos enum outcome { SKIP=2, OK=1, FAIL=0 };
828585484eSchristos static enum outcome cur_test_outcome = 0;
838585484eSchristos const char *cur_test_prefix = NULL; /**< prefix of the current test group */
848585484eSchristos /** Name of the current test, if we haven't logged is yet. Used for --quiet */
858585484eSchristos const char *cur_test_name = NULL;
868585484eSchristos 
878585484eSchristos static void usage(struct testgroup_t *groups, int list_groups)
888585484eSchristos 	__attribute__((noreturn));
898585484eSchristos static int process_test_option(struct testgroup_t *groups, const char *test);
908585484eSchristos 
91*eabc0478Schristos #ifdef _WIN32
92*eabc0478Schristos /* Copy of argv[0] for win32. */
93*eabc0478Schristos static char commandname[MAX_PATH+1];
94*eabc0478Schristos 
95*eabc0478Schristos struct timeout_thread_args {
96*eabc0478Schristos 	const testcase_fn *fn;
97*eabc0478Schristos 	void *env;
98*eabc0478Schristos };
99*eabc0478Schristos 
100*eabc0478Schristos static DWORD WINAPI
101*eabc0478Schristos timeout_thread_proc_(LPVOID arg)
102*eabc0478Schristos {
103*eabc0478Schristos 	struct timeout_thread_args *args = arg;
104*eabc0478Schristos 	(*(args->fn))(args->env);
105*eabc0478Schristos 	ExitThread(cur_test_outcome == FAIL ? 1 : 0);
106*eabc0478Schristos }
107*eabc0478Schristos 
108*eabc0478Schristos static enum outcome
109*eabc0478Schristos testcase_run_in_thread_(const struct testcase_t *testcase, void *env)
110*eabc0478Schristos {
111*eabc0478Schristos 	/* We will never run testcase in a new thread when the
112*eabc0478Schristos 	timeout is set to zero */
113*eabc0478Schristos 	assert(opt_timeout);
114*eabc0478Schristos 	DWORD ret, tid;
115*eabc0478Schristos 	HANDLE handle;
116*eabc0478Schristos 	struct timeout_thread_args args = {
117*eabc0478Schristos 		&(testcase->fn),
118*eabc0478Schristos 		env
119*eabc0478Schristos 	};
120*eabc0478Schristos 
121*eabc0478Schristos 	handle =CreateThread(NULL, 0, timeout_thread_proc_,
122*eabc0478Schristos 		(LPVOID)&args, 0, &tid);
123*eabc0478Schristos 	ret = WaitForSingleObject(handle, opt_timeout * 1000U);
124*eabc0478Schristos 	if (ret == WAIT_OBJECT_0) {
125*eabc0478Schristos 		ret = 0;
126*eabc0478Schristos 		if (!GetExitCodeThread(handle, &ret)) {
127*eabc0478Schristos 			printf("GetExitCodeThread failed\n");
128*eabc0478Schristos 			ret = 1;
129*eabc0478Schristos 		}
130*eabc0478Schristos 	} else if (ret == WAIT_TIMEOUT)	{
131*eabc0478Schristos 		printf("timeout\n");
132*eabc0478Schristos 	} else {
133*eabc0478Schristos 		printf("Wait failed\n");
134*eabc0478Schristos 	}
135*eabc0478Schristos 	CloseHandle(handle);
136*eabc0478Schristos 	if (ret == 0)
137*eabc0478Schristos 		return OK;
138*eabc0478Schristos 	else if (ret == MAGIC_EXITCODE)
139*eabc0478Schristos 		return SKIP;
140*eabc0478Schristos 	else
141*eabc0478Schristos 		return FAIL;
142*eabc0478Schristos }
143*eabc0478Schristos #else
144*eabc0478Schristos static unsigned int testcase_set_timeout_(void)
145*eabc0478Schristos {
146*eabc0478Schristos 	return alarm(opt_timeout);
147*eabc0478Schristos }
148*eabc0478Schristos 
149*eabc0478Schristos static unsigned int testcase_reset_timeout_(void)
150*eabc0478Schristos {
151*eabc0478Schristos 	return alarm(0);
152*eabc0478Schristos }
153*eabc0478Schristos #endif
154*eabc0478Schristos 
1558585484eSchristos static enum outcome
1568585484eSchristos testcase_run_bare_(const struct testcase_t *testcase)
1578585484eSchristos {
1588585484eSchristos 	void *env = NULL;
1598585484eSchristos 	int outcome;
1608585484eSchristos 	if (testcase->setup) {
1618585484eSchristos 		env = testcase->setup->setup_fn(testcase);
1628585484eSchristos 		if (!env)
1638585484eSchristos 			return FAIL;
1648585484eSchristos 		else if (env == (void*)TT_SKIP)
1658585484eSchristos 			return SKIP;
1668585484eSchristos 	}
1678585484eSchristos 
1688585484eSchristos 	cur_test_outcome = OK;
169*eabc0478Schristos 	{
170*eabc0478Schristos 		if (opt_timeout) {
171*eabc0478Schristos #ifdef _WIN32
172*eabc0478Schristos 			cur_test_outcome = testcase_run_in_thread_(testcase, env);
173*eabc0478Schristos #else
174*eabc0478Schristos 			testcase_set_timeout_();
1758585484eSchristos 			testcase->fn(env);
176*eabc0478Schristos 			testcase_reset_timeout_();
177*eabc0478Schristos #endif
178*eabc0478Schristos 		} else {
179*eabc0478Schristos 			testcase->fn(env);
180*eabc0478Schristos 		}
181*eabc0478Schristos 	}
1828585484eSchristos 	outcome = cur_test_outcome;
1838585484eSchristos 
1848585484eSchristos 	if (testcase->setup) {
1858585484eSchristos 		if (testcase->setup->cleanup_fn(testcase, env) == 0)
1868585484eSchristos 			outcome = FAIL;
1878585484eSchristos 	}
1888585484eSchristos 
1898585484eSchristos 	return outcome;
1908585484eSchristos }
1918585484eSchristos 
1928585484eSchristos 
1937476e6e4Schristos #ifndef NO_FORKING
1947476e6e4Schristos 
1958585484eSchristos static enum outcome
1968585484eSchristos testcase_run_forked_(const struct testgroup_t *group,
1978585484eSchristos 		     const struct testcase_t *testcase)
1988585484eSchristos {
1998585484eSchristos #ifdef _WIN32
2008585484eSchristos 	/* Fork? On Win32?  How primitive!  We'll do what the smart kids do:
2018585484eSchristos 	   we'll invoke our own exe (whose name we recall from the command
2028585484eSchristos 	   line) with a command line that tells it to run just the test we
2038585484eSchristos 	   want, and this time without forking.
2048585484eSchristos 
2058585484eSchristos 	   (No, threads aren't an option.  The whole point of forking is to
2068585484eSchristos 	   share no state between tests.)
2078585484eSchristos 	 */
2088585484eSchristos 	int ok;
2098585484eSchristos 	char buffer[LONGEST_TEST_NAME+256];
2108585484eSchristos 	STARTUPINFOA si;
2118585484eSchristos 	PROCESS_INFORMATION info;
212*eabc0478Schristos 	DWORD ret;
2138585484eSchristos 
2148585484eSchristos 	if (!in_tinytest_main) {
2158585484eSchristos 		printf("\nERROR.  On Windows, testcase_run_forked_ must be"
2168585484eSchristos 		       " called from within tinytest_main.\n");
2178585484eSchristos 		abort();
2188585484eSchristos 	}
2198585484eSchristos 	if (opt_verbosity>0)
2208585484eSchristos 		printf("[forking] ");
2218585484eSchristos 
222*eabc0478Schristos 	snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s --timeout 0 %s%s",
2238585484eSchristos 		 commandname, verbosity_flag, group->prefix, testcase->name);
2248585484eSchristos 
2258585484eSchristos 	memset(&si, 0, sizeof(si));
2268585484eSchristos 	memset(&info, 0, sizeof(info));
2278585484eSchristos 	si.cb = sizeof(si);
2288585484eSchristos 
2298585484eSchristos 	ok = CreateProcessA(commandname, buffer, NULL, NULL, 0,
2308585484eSchristos 			   0, NULL, NULL, &si, &info);
2318585484eSchristos 	if (!ok) {
2328585484eSchristos 		printf("CreateProcess failed!\n");
233*eabc0478Schristos 		return FAIL;
2348585484eSchristos 	}
235*eabc0478Schristos 	ret = WaitForSingleObject(info.hProcess,
236*eabc0478Schristos 		(opt_timeout ? opt_timeout * 1000U : INFINITE));
237*eabc0478Schristos 
238*eabc0478Schristos 	if (ret == WAIT_OBJECT_0) {
239*eabc0478Schristos 		GetExitCodeProcess(info.hProcess, &ret);
240*eabc0478Schristos 	} else if (ret == WAIT_TIMEOUT) {
241*eabc0478Schristos 		printf("timeout\n");
242*eabc0478Schristos 	} else {
243*eabc0478Schristos 		printf("Wait failed\n");
244*eabc0478Schristos 	}
2458585484eSchristos 	CloseHandle(info.hProcess);
2468585484eSchristos 	CloseHandle(info.hThread);
247*eabc0478Schristos 	if (ret == 0)
2488585484eSchristos 		return OK;
249*eabc0478Schristos 	else if (ret == MAGIC_EXITCODE)
2508585484eSchristos 		return SKIP;
2518585484eSchristos 	else
2528585484eSchristos 		return FAIL;
2538585484eSchristos #else
2548585484eSchristos 	int outcome_pipe[2];
2558585484eSchristos 	pid_t pid;
2568585484eSchristos 	(void)group;
2578585484eSchristos 
2588585484eSchristos 	if (pipe(outcome_pipe))
2598585484eSchristos 		perror("opening pipe");
2608585484eSchristos 
2618585484eSchristos 	if (opt_verbosity>0)
2628585484eSchristos 		printf("[forking] ");
2638585484eSchristos 	pid = fork();
2648585484eSchristos #ifdef FORK_BREAKS_GCOV
2658585484eSchristos 	vproc_transaction_begin(0);
2668585484eSchristos #endif
2678585484eSchristos 	if (!pid) {
2688585484eSchristos 		/* child. */
2698585484eSchristos 		int test_r, write_r;
2708585484eSchristos 		char b[1];
2718585484eSchristos 		close(outcome_pipe[0]);
2728585484eSchristos 		test_r = testcase_run_bare_(testcase);
2738585484eSchristos 		assert(0<=(int)test_r && (int)test_r<=2);
2748585484eSchristos 		b[0] = "NYS"[test_r];
2758585484eSchristos 		write_r = (int)write(outcome_pipe[1], b, 1);
2768585484eSchristos 		if (write_r != 1) {
2778585484eSchristos 			perror("write outcome to pipe");
2788585484eSchristos 			exit(1);
2798585484eSchristos 		}
2808585484eSchristos 		exit(0);
2818585484eSchristos 		return FAIL; /* unreachable */
2828585484eSchristos 	} else {
2838585484eSchristos 		/* parent */
284*eabc0478Schristos 		int status, r, exitcode;
2858585484eSchristos 		char b[1];
2868585484eSchristos 		/* Close this now, so that if the other side closes it,
2878585484eSchristos 		 * our read fails. */
2888585484eSchristos 		close(outcome_pipe[1]);
2898585484eSchristos 		r = (int)read(outcome_pipe[0], b, 1);
2908585484eSchristos 		if (r == 0) {
2918585484eSchristos 			printf("[Lost connection!] ");
292*eabc0478Schristos 			return FAIL;
2938585484eSchristos 		} else if (r != 1) {
2948585484eSchristos 			perror("read outcome from pipe");
2958585484eSchristos 		}
2968585484eSchristos 		waitpid(pid, &status, 0);
297*eabc0478Schristos 		exitcode = WEXITSTATUS(status);
2988585484eSchristos 		close(outcome_pipe[0]);
299*eabc0478Schristos 		if (opt_verbosity>1)
300*eabc0478Schristos 			printf("%s%s: exited with %i (%i)\n", group->prefix, testcase->name, exitcode, status);
301*eabc0478Schristos 		if (exitcode != 0)
302*eabc0478Schristos 		{
303*eabc0478Schristos 			printf("[atexit failure!] ");
304*eabc0478Schristos 			return FAIL;
305*eabc0478Schristos 		}
3068585484eSchristos 		return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
3078585484eSchristos 	}
3088585484eSchristos #endif
3098585484eSchristos }
3108585484eSchristos 
3117476e6e4Schristos #endif /* !NO_FORKING */
3127476e6e4Schristos 
3138585484eSchristos int
3148585484eSchristos testcase_run_one(const struct testgroup_t *group,
3158585484eSchristos 		 const struct testcase_t *testcase)
3168585484eSchristos {
3178585484eSchristos 	enum outcome outcome;
3188585484eSchristos 
3198585484eSchristos 	if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) {
3208585484eSchristos 		if (opt_verbosity>0)
3218585484eSchristos 			printf("%s%s: %s\n",
3228585484eSchristos 			   group->prefix, testcase->name,
3238585484eSchristos 			   (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED");
3248585484eSchristos 		++n_skipped;
3258585484eSchristos 		return SKIP;
3268585484eSchristos 	}
3278585484eSchristos 
3288585484eSchristos 	if (opt_verbosity>0 && !opt_forked) {
3298585484eSchristos 		printf("%s%s: ", group->prefix, testcase->name);
3308585484eSchristos 	} else {
3318585484eSchristos 		if (opt_verbosity==0) printf(".");
3328585484eSchristos 		cur_test_prefix = group->prefix;
3338585484eSchristos 		cur_test_name = testcase->name;
3348585484eSchristos 	}
3358585484eSchristos 
3367476e6e4Schristos #ifndef NO_FORKING
3378585484eSchristos 	if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
3388585484eSchristos 		outcome = testcase_run_forked_(group, testcase);
3398585484eSchristos 	} else {
3407476e6e4Schristos #else
3417476e6e4Schristos 	{
3427476e6e4Schristos #endif
3438585484eSchristos 		outcome = testcase_run_bare_(testcase);
3448585484eSchristos 	}
3458585484eSchristos 
3468585484eSchristos 	if (outcome == OK) {
3478585484eSchristos 		if (opt_verbosity>0 && !opt_forked)
3488585484eSchristos 			puts(opt_verbosity==1?"OK":"");
3498585484eSchristos 	} else if (outcome == SKIP) {
3508585484eSchristos 		if (opt_verbosity>0 && !opt_forked)
3518585484eSchristos 			puts("SKIPPED");
3528585484eSchristos 	} else {
3538585484eSchristos 		if (!opt_forked)
3548585484eSchristos 			printf("\n  [%s FAILED]\n", testcase->name);
3558585484eSchristos 	}
3568585484eSchristos 
3578585484eSchristos 	if (opt_forked) {
3588585484eSchristos 		exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
3598585484eSchristos 		return 1; /* unreachable */
3608585484eSchristos 	} else {
3618585484eSchristos 		return (int)outcome;
3628585484eSchristos 	}
3638585484eSchristos }
3648585484eSchristos 
3658585484eSchristos int
3668585484eSchristos tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag)
3678585484eSchristos {
3688585484eSchristos 	int i, j;
3698585484eSchristos 	size_t length = LONGEST_TEST_NAME;
3708585484eSchristos 	char fullname[LONGEST_TEST_NAME];
3718585484eSchristos 	int found=0;
3728585484eSchristos 	if (strstr(arg, ".."))
3738585484eSchristos 		length = strstr(arg,"..")-arg;
3748585484eSchristos 	for (i=0; groups[i].prefix; ++i) {
3758585484eSchristos 		for (j=0; groups[i].cases[j].name; ++j) {
3768585484eSchristos 			struct testcase_t *testcase = &groups[i].cases[j];
3778585484eSchristos 			snprintf(fullname, sizeof(fullname), "%s%s",
3788585484eSchristos 				 groups[i].prefix, testcase->name);
3798585484eSchristos 			if (!flag) { /* Hack! */
3808585484eSchristos 				printf("    %s", fullname);
3818585484eSchristos 				if (testcase->flags & TT_OFF_BY_DEFAULT)
3828585484eSchristos 					puts("   (Off by default)");
3838585484eSchristos 				else if (testcase->flags & TT_SKIP)
3848585484eSchristos 					puts("  (DISABLED)");
3858585484eSchristos 				else
3868585484eSchristos 					puts("");
3878585484eSchristos 			}
3888585484eSchristos 			if (!strncmp(fullname, arg, length)) {
3898585484eSchristos 				if (set)
3908585484eSchristos 					testcase->flags |= flag;
3918585484eSchristos 				else
3928585484eSchristos 					testcase->flags &= ~flag;
3938585484eSchristos 				++found;
3948585484eSchristos 			}
3958585484eSchristos 		}
3968585484eSchristos 	}
3978585484eSchristos 	return found;
3988585484eSchristos }
3998585484eSchristos 
4008585484eSchristos static void
4018585484eSchristos usage(struct testgroup_t *groups, int list_groups)
4028585484eSchristos {
403*eabc0478Schristos 	puts("Options are: [--verbose|--quiet|--terse] [--no-fork] [--timeout <sec>]");
4048585484eSchristos 	puts("  Specify tests by name, or using a prefix ending with '..'");
4058585484eSchristos 	puts("  To skip a test, prefix its name with a colon.");
4068585484eSchristos 	puts("  To enable a disabled test, prefix its name with a plus.");
4078585484eSchristos 	puts("  Use --list-tests for a list of tests.");
4088585484eSchristos 	if (list_groups) {
4098585484eSchristos 		puts("Known tests are:");
4108585484eSchristos 		tinytest_set_flag_(groups, "..", 1, 0);
4118585484eSchristos 	}
4128585484eSchristos 	exit(0);
4138585484eSchristos }
4148585484eSchristos 
4158585484eSchristos static int
4168585484eSchristos process_test_alias(struct testgroup_t *groups, const char *test)
4178585484eSchristos {
4188585484eSchristos 	int i, j, n, r;
4198585484eSchristos 	for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) {
4208585484eSchristos 		if (!strcmp(cfg_aliases[i].name, test)) {
4218585484eSchristos 			n = 0;
4228585484eSchristos 			for (j = 0; cfg_aliases[i].tests[j]; ++j) {
4238585484eSchristos 				r = process_test_option(groups, cfg_aliases[i].tests[j]);
4248585484eSchristos 				if (r<0)
4258585484eSchristos 					return -1;
4268585484eSchristos 				n += r;
4278585484eSchristos 			}
4288585484eSchristos 			return n;
4298585484eSchristos 		}
4308585484eSchristos 	}
4318585484eSchristos 	printf("No such test alias as @%s!",test);
4328585484eSchristos 	return -1;
4338585484eSchristos }
4348585484eSchristos 
4358585484eSchristos static int
4368585484eSchristos process_test_option(struct testgroup_t *groups, const char *test)
4378585484eSchristos {
4388585484eSchristos 	int flag = TT_ENABLED_;
4398585484eSchristos 	int n = 0;
4408585484eSchristos 	if (test[0] == '@') {
4418585484eSchristos 		return process_test_alias(groups, test + 1);
4428585484eSchristos 	} else if (test[0] == ':') {
4438585484eSchristos 		++test;
4448585484eSchristos 		flag = TT_SKIP;
4458585484eSchristos 	} else if (test[0] == '+') {
4468585484eSchristos 		++test;
4478585484eSchristos 		++n;
4488585484eSchristos 		if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) {
4498585484eSchristos 			printf("No such test as %s!\n", test);
4508585484eSchristos 			return -1;
4518585484eSchristos 		}
4528585484eSchristos 	} else {
4538585484eSchristos 		++n;
4548585484eSchristos 	}
4558585484eSchristos 	if (!tinytest_set_flag_(groups, test, 1, flag)) {
4568585484eSchristos 		printf("No such test as %s!\n", test);
4578585484eSchristos 		return -1;
4588585484eSchristos 	}
4598585484eSchristos 	return n;
4608585484eSchristos }
4618585484eSchristos 
4628585484eSchristos void
4638585484eSchristos tinytest_set_aliases(const struct testlist_alias_t *aliases)
4648585484eSchristos {
4658585484eSchristos 	cfg_aliases = aliases;
4668585484eSchristos }
4678585484eSchristos 
4688585484eSchristos int
4698585484eSchristos tinytest_main(int c, const char **v, struct testgroup_t *groups)
4708585484eSchristos {
4718585484eSchristos 	int i, j, n=0;
4728585484eSchristos 
4738585484eSchristos #ifdef _WIN32
4748585484eSchristos 	const char *sp = strrchr(v[0], '.');
4758585484eSchristos 	const char *extension = "";
4768585484eSchristos 	if (!sp || stricmp(sp, ".exe"))
4778585484eSchristos 		extension = ".exe"; /* Add an exe so CreateProcess will work */
4788585484eSchristos 	snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension);
4798585484eSchristos 	commandname[MAX_PATH]='\0';
4808585484eSchristos #endif
4818585484eSchristos 	for (i=1; i<c; ++i) {
4828585484eSchristos 		if (v[i][0] == '-') {
4838585484eSchristos 			if (!strcmp(v[i], "--RUNNING-FORKED")) {
4848585484eSchristos 				opt_forked = 1;
4858585484eSchristos 			} else if (!strcmp(v[i], "--no-fork")) {
4868585484eSchristos 				opt_nofork = 1;
4878585484eSchristos 			} else if (!strcmp(v[i], "--quiet")) {
4888585484eSchristos 				opt_verbosity = -1;
4898585484eSchristos 				verbosity_flag = "--quiet";
4908585484eSchristos 			} else if (!strcmp(v[i], "--verbose")) {
4918585484eSchristos 				opt_verbosity = 2;
4928585484eSchristos 				verbosity_flag = "--verbose";
4938585484eSchristos 			} else if (!strcmp(v[i], "--terse")) {
4948585484eSchristos 				opt_verbosity = 0;
4958585484eSchristos 				verbosity_flag = "--terse";
4968585484eSchristos 			} else if (!strcmp(v[i], "--help")) {
4978585484eSchristos 				usage(groups, 0);
4988585484eSchristos 			} else if (!strcmp(v[i], "--list-tests")) {
4998585484eSchristos 				usage(groups, 1);
500*eabc0478Schristos 			} else if (!strcmp(v[i], "--timeout")) {
501*eabc0478Schristos 				++i;
502*eabc0478Schristos 				if (i >= c) {
503*eabc0478Schristos 					fprintf(stderr, "--timeout requires argument\n");
504*eabc0478Schristos 					return -1;
505*eabc0478Schristos 				}
506*eabc0478Schristos 				opt_timeout = (unsigned)atoi(v[i]);
5078585484eSchristos 			} else {
508*eabc0478Schristos 				fprintf(stderr, "Unknown option %s. Try --help\n", v[i]);
5098585484eSchristos 				return -1;
5108585484eSchristos 			}
5118585484eSchristos 		} else {
5128585484eSchristos 			int r = process_test_option(groups, v[i]);
5138585484eSchristos 			if (r<0)
5148585484eSchristos 				return -1;
5158585484eSchristos 			n += r;
5168585484eSchristos 		}
5178585484eSchristos 	}
5188585484eSchristos 	if (!n)
5198585484eSchristos 		tinytest_set_flag_(groups, "..", 1, TT_ENABLED_);
5208585484eSchristos 
5217476e6e4Schristos #ifdef _IONBF
5228585484eSchristos 	setvbuf(stdout, NULL, _IONBF, 0);
5237476e6e4Schristos #endif
5248585484eSchristos 
5258585484eSchristos 	++in_tinytest_main;
526*eabc0478Schristos 	for (i = 0; groups[i].prefix; ++i) {
527*eabc0478Schristos 		struct testgroup_t *group = &groups[i];
528*eabc0478Schristos 		for (j = 0; group->cases[j].name; ++j) {
529*eabc0478Schristos 			struct testcase_t *testcase = &group->cases[j];
530*eabc0478Schristos 			int test_attempts = 3;
531*eabc0478Schristos 			int test_ret_err;
532*eabc0478Schristos 
533*eabc0478Schristos 			if (!(testcase->flags & TT_ENABLED_))
534*eabc0478Schristos 				continue;
535*eabc0478Schristos 
536*eabc0478Schristos 			for (;;) {
537*eabc0478Schristos 				test_ret_err = testcase_run_one(group, testcase);
538*eabc0478Schristos 
539*eabc0478Schristos 				if (test_ret_err == OK)
540*eabc0478Schristos 					break;
541*eabc0478Schristos 				if (!(testcase->flags & TT_RETRIABLE))
542*eabc0478Schristos 					break;
543*eabc0478Schristos 				printf("\n  [RETRYING %s (%i)]\n", testcase->name, test_attempts);
544*eabc0478Schristos 				if (!test_attempts--)
545*eabc0478Schristos 					break;
546*eabc0478Schristos 			}
547*eabc0478Schristos 
548*eabc0478Schristos 			switch (test_ret_err) {
549*eabc0478Schristos 				case OK:   ++n_ok;      break;
550*eabc0478Schristos 				case SKIP: ++n_skipped; break;
551*eabc0478Schristos 				default:   ++n_bad;     break;
552*eabc0478Schristos 			}
553*eabc0478Schristos 		}
554*eabc0478Schristos 	}
5558585484eSchristos 
5568585484eSchristos 	--in_tinytest_main;
5578585484eSchristos 
5588585484eSchristos 	if (opt_verbosity==0)
5598585484eSchristos 		puts("");
5608585484eSchristos 
5618585484eSchristos 	if (n_bad)
5628585484eSchristos 		printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
5638585484eSchristos 		       n_bad+n_ok,n_skipped);
5648585484eSchristos 	else if (opt_verbosity >= 1)
5658585484eSchristos 		printf("%d tests ok.  (%d skipped)\n", n_ok, n_skipped);
5668585484eSchristos 
5678585484eSchristos 	return (n_bad == 0) ? 0 : 1;
5688585484eSchristos }
5698585484eSchristos 
5708585484eSchristos int
5718585484eSchristos tinytest_get_verbosity_(void)
5728585484eSchristos {
5738585484eSchristos 	return opt_verbosity;
5748585484eSchristos }
5758585484eSchristos 
5768585484eSchristos void
5778585484eSchristos tinytest_set_test_failed_(void)
5788585484eSchristos {
5798585484eSchristos 	if (opt_verbosity <= 0 && cur_test_name) {
5808585484eSchristos 		if (opt_verbosity==0) puts("");
5818585484eSchristos 		printf("%s%s: ", cur_test_prefix, cur_test_name);
5828585484eSchristos 		cur_test_name = NULL;
5838585484eSchristos 	}
584*eabc0478Schristos 	cur_test_outcome = FAIL;
5858585484eSchristos }
5868585484eSchristos 
5878585484eSchristos void
5888585484eSchristos tinytest_set_test_skipped_(void)
5898585484eSchristos {
5908585484eSchristos 	if (cur_test_outcome==OK)
5918585484eSchristos 		cur_test_outcome = SKIP;
5928585484eSchristos }
5938585484eSchristos 
5947476e6e4Schristos char *
5957476e6e4Schristos tinytest_format_hex_(const void *val_, unsigned long len)
5967476e6e4Schristos {
5977476e6e4Schristos 	const unsigned char *val = val_;
5987476e6e4Schristos 	char *result, *cp;
5997476e6e4Schristos 	size_t i;
6007476e6e4Schristos 
6017476e6e4Schristos 	if (!val)
6027476e6e4Schristos 		return strdup("null");
6037476e6e4Schristos 	if (!(result = malloc(len*2+1)))
6047476e6e4Schristos 		return strdup("<allocation failure>");
6057476e6e4Schristos 	cp = result;
6067476e6e4Schristos 	for (i=0;i<len;++i) {
6077476e6e4Schristos 		*cp++ = "0123456789ABCDEF"[val[i] >> 4];
6087476e6e4Schristos 		*cp++ = "0123456789ABCDEF"[val[i] & 0x0f];
6097476e6e4Schristos 	}
6107476e6e4Schristos 	*cp = 0;
6117476e6e4Schristos 	return result;
6127476e6e4Schristos }
613