xref: /libevent-2.1.12/test/tinytest.c (revision 19a68bd1)
1e49e2891SNick Mathewson /* tinytest.c -- Copyright 2009-2012 Nick Mathewson
2a8203b34SNick Mathewson  *
3a8203b34SNick Mathewson  * Redistribution and use in source and binary forms, with or without
4a8203b34SNick Mathewson  * modification, are permitted provided that the following conditions
5a8203b34SNick Mathewson  * are met:
6a8203b34SNick Mathewson  * 1. Redistributions of source code must retain the above copyright
7a8203b34SNick Mathewson  *    notice, this list of conditions and the following disclaimer.
8a8203b34SNick Mathewson  * 2. Redistributions in binary form must reproduce the above copyright
9a8203b34SNick Mathewson  *    notice, this list of conditions and the following disclaimer in the
10a8203b34SNick Mathewson  *    documentation and/or other materials provided with the distribution.
11a8203b34SNick Mathewson  * 3. The name of the author may not be used to endorse or promote products
12a8203b34SNick Mathewson  *    derived from this software without specific prior written permission.
13a8203b34SNick Mathewson  *
14a8203b34SNick Mathewson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15a8203b34SNick Mathewson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16a8203b34SNick Mathewson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17a8203b34SNick Mathewson  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18a8203b34SNick Mathewson  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19a8203b34SNick Mathewson  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20a8203b34SNick Mathewson  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21a8203b34SNick Mathewson  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22a8203b34SNick Mathewson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23a8203b34SNick Mathewson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24a8203b34SNick Mathewson  */
2595e2455cSNick Mathewson #ifdef TINYTEST_LOCAL
2695e2455cSNick Mathewson #include "tinytest_local.h"
2795e2455cSNick Mathewson #endif
28a8203b34SNick Mathewson 
29a8203b34SNick Mathewson #include <stdio.h>
30a8203b34SNick Mathewson #include <stdlib.h>
31a8203b34SNick Mathewson #include <string.h>
32a8203b34SNick Mathewson #include <assert.h>
33a8203b34SNick Mathewson 
347a804767SNick Mathewson #ifndef NO_FORKING
357a804767SNick Mathewson 
369f560bfaSNick Mathewson #ifdef _WIN32
37a8203b34SNick Mathewson #include <windows.h>
38a8203b34SNick Mathewson #else
39a8203b34SNick Mathewson #include <sys/types.h>
40a8203b34SNick Mathewson #include <sys/wait.h>
41a8203b34SNick Mathewson #include <unistd.h>
42a8203b34SNick Mathewson #endif
43a8203b34SNick Mathewson 
44b62b31f1SNick Mathewson #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
45b62b31f1SNick Mathewson #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \
46b62b31f1SNick Mathewson     __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070)
47b62b31f1SNick Mathewson /* Workaround for a stupid bug in OSX 10.6 */
48b62b31f1SNick Mathewson #define FORK_BREAKS_GCOV
49b62b31f1SNick Mathewson #include <vproc.h>
50b62b31f1SNick Mathewson #endif
51b62b31f1SNick Mathewson #endif
52b62b31f1SNick Mathewson 
537a804767SNick Mathewson #endif /* !NO_FORKING */
547a804767SNick Mathewson 
55f0bd83eaSNick Mathewson #ifndef __GNUC__
56f0bd83eaSNick Mathewson #define __attribute__(x)
57f0bd83eaSNick Mathewson #endif
585b5b880bSNick Mathewson 
59a8203b34SNick Mathewson #include "tinytest.h"
60a8203b34SNick Mathewson #include "tinytest_macros.h"
61a8203b34SNick Mathewson 
62a8203b34SNick Mathewson #define LONGEST_TEST_NAME 16384
63b64dbfb6SAzat Khuzhin #define DEFAULT_TESTCASE_TIMEOUT 30U
64794e8f75Syuangongji #define MAGIC_EXITCODE 42
65b64dbfb6SAzat Khuzhin 
66a8203b34SNick Mathewson static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/
67a8203b34SNick Mathewson static int n_ok = 0; /**< Number of tests that have passed */
68a8203b34SNick Mathewson static int n_bad = 0; /**< Number of tests that have failed. */
6981280062SNick Mathewson static int n_skipped = 0; /**< Number of tests that have been skipped. */
70a8203b34SNick Mathewson 
71a8203b34SNick Mathewson static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/
7281280062SNick Mathewson static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */
7381280062SNick Mathewson static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */
74b64dbfb6SAzat Khuzhin static unsigned int opt_timeout = DEFAULT_TESTCASE_TIMEOUT; /**< Timeout for every test (using alarm()) */
7581280062SNick Mathewson const char *verbosity_flag = "";
76a8203b34SNick Mathewson 
77ef7c4f79SNick Mathewson const struct testlist_alias_t *cfg_aliases=NULL;
78ef7c4f79SNick Mathewson 
79e6ba208bSNick Mathewson enum outcome { SKIP=2, OK=1, FAIL=0 };
80e6ba208bSNick Mathewson static enum outcome cur_test_outcome = 0;
81a8203b34SNick Mathewson const char *cur_test_prefix = NULL; /**< prefix of the current test group */
82a8203b34SNick Mathewson /** Name of the current test, if we haven't logged is yet. Used for --quiet */
83a8203b34SNick Mathewson const char *cur_test_name = NULL;
84a8203b34SNick Mathewson 
85f0bd83eaSNick Mathewson static void usage(struct testgroup_t *groups, int list_groups)
86f0bd83eaSNick Mathewson 	__attribute__((noreturn));
87ef7c4f79SNick Mathewson static int process_test_option(struct testgroup_t *groups, const char *test);
88f0bd83eaSNick Mathewson 
89794e8f75Syuangongji #ifdef _WIN32
90794e8f75Syuangongji /* Copy of argv[0] for win32. */
91794e8f75Syuangongji static char commandname[MAX_PATH+1];
92794e8f75Syuangongji 
93794e8f75Syuangongji struct timeout_thread_args {
94794e8f75Syuangongji 	const testcase_fn *fn;
95794e8f75Syuangongji 	void *env;
96794e8f75Syuangongji };
97794e8f75Syuangongji 
98794e8f75Syuangongji static DWORD WINAPI
timeout_thread_proc_(LPVOID arg)99794e8f75Syuangongji timeout_thread_proc_(LPVOID arg)
100794e8f75Syuangongji {
101794e8f75Syuangongji 	struct timeout_thread_args *args = arg;
102794e8f75Syuangongji 	(*(args->fn))(args->env);
103794e8f75Syuangongji 	ExitThread(cur_test_outcome == FAIL ? 1 : 0);
104794e8f75Syuangongji }
105794e8f75Syuangongji 
106794e8f75Syuangongji static enum outcome
testcase_run_in_thread_(const struct testcase_t * testcase,void * env)107794e8f75Syuangongji testcase_run_in_thread_(const struct testcase_t *testcase, void *env)
108794e8f75Syuangongji {
109794e8f75Syuangongji 	/* We will never run testcase in a new thread when the
110794e8f75Syuangongji 	timeout is set to zero */
111794e8f75Syuangongji 	assert(opt_timeout);
112794e8f75Syuangongji 	DWORD ret, tid;
113794e8f75Syuangongji 	HANDLE handle;
114794e8f75Syuangongji 	struct timeout_thread_args args = {
115794e8f75Syuangongji 		&(testcase->fn),
116794e8f75Syuangongji 		env
117794e8f75Syuangongji 	};
118794e8f75Syuangongji 
119794e8f75Syuangongji 	handle =CreateThread(NULL, 0, timeout_thread_proc_,
120794e8f75Syuangongji 		(LPVOID)&args, 0, &tid);
121794e8f75Syuangongji 	ret = WaitForSingleObject(handle, opt_timeout * 1000U);
122794e8f75Syuangongji 	if (ret == WAIT_OBJECT_0) {
123794e8f75Syuangongji 		ret = 0;
124794e8f75Syuangongji 		if (!GetExitCodeThread(handle, &ret)) {
125794e8f75Syuangongji 			printf("GetExitCodeThread failed\n");
126794e8f75Syuangongji 			ret = 1;
127794e8f75Syuangongji 		}
128794e8f75Syuangongji 	} else if (ret == WAIT_TIMEOUT)	{
129794e8f75Syuangongji 		printf("timeout\n");
130794e8f75Syuangongji 	} else {
131794e8f75Syuangongji 		printf("Wait failed\n");
132794e8f75Syuangongji 	}
133794e8f75Syuangongji 	CloseHandle(handle);
134794e8f75Syuangongji 	if (ret == 0)
135794e8f75Syuangongji 		return OK;
136794e8f75Syuangongji 	else if (ret == MAGIC_EXITCODE)
137794e8f75Syuangongji 		return SKIP;
138794e8f75Syuangongji 	else
139794e8f75Syuangongji 		return FAIL;
140794e8f75Syuangongji }
141794e8f75Syuangongji #else
testcase_set_timeout_(void)142b64dbfb6SAzat Khuzhin static unsigned int testcase_set_timeout_(void)
143b64dbfb6SAzat Khuzhin {
144b64dbfb6SAzat Khuzhin 	return alarm(opt_timeout);
145b64dbfb6SAzat Khuzhin }
146794e8f75Syuangongji 
testcase_reset_timeout_(void)147b64dbfb6SAzat Khuzhin static unsigned int testcase_reset_timeout_(void)
148b64dbfb6SAzat Khuzhin {
149b64dbfb6SAzat Khuzhin 	return alarm(0);
150b64dbfb6SAzat Khuzhin }
151794e8f75Syuangongji #endif
152b64dbfb6SAzat Khuzhin 
153e6ba208bSNick Mathewson static enum outcome
testcase_run_bare_(const struct testcase_t * testcase)1546c81be74SNick Mathewson testcase_run_bare_(const struct testcase_t *testcase)
155a8203b34SNick Mathewson {
156a8203b34SNick Mathewson 	void *env = NULL;
157a8203b34SNick Mathewson 	int outcome;
158a8203b34SNick Mathewson 	if (testcase->setup) {
159a8203b34SNick Mathewson 		env = testcase->setup->setup_fn(testcase);
160e6ba208bSNick Mathewson 		if (!env)
161e6ba208bSNick Mathewson 			return FAIL;
16261f2a45dSNick Mathewson 		else if (env == (void*)TT_SKIP)
16361f2a45dSNick Mathewson 			return SKIP;
164a8203b34SNick Mathewson 	}
165a8203b34SNick Mathewson 
166e6ba208bSNick Mathewson 	cur_test_outcome = OK;
167b64dbfb6SAzat Khuzhin 	{
168794e8f75Syuangongji 		if (opt_timeout) {
169794e8f75Syuangongji #ifdef _WIN32
170794e8f75Syuangongji 			cur_test_outcome = testcase_run_in_thread_(testcase, env);
171794e8f75Syuangongji #else
172b64dbfb6SAzat Khuzhin 			testcase_set_timeout_();
173a8203b34SNick Mathewson 			testcase->fn(env);
174b64dbfb6SAzat Khuzhin 			testcase_reset_timeout_();
175794e8f75Syuangongji #endif
176794e8f75Syuangongji 		} else {
177794e8f75Syuangongji 			testcase->fn(env);
178794e8f75Syuangongji 		}
179b64dbfb6SAzat Khuzhin 	}
180a8203b34SNick Mathewson 	outcome = cur_test_outcome;
181a8203b34SNick Mathewson 
182a8203b34SNick Mathewson 	if (testcase->setup) {
183a8203b34SNick Mathewson 		if (testcase->setup->cleanup_fn(testcase, env) == 0)
184e6ba208bSNick Mathewson 			outcome = FAIL;
185a8203b34SNick Mathewson 	}
186a8203b34SNick Mathewson 
187a8203b34SNick Mathewson 	return outcome;
188a8203b34SNick Mathewson }
189a8203b34SNick Mathewson 
190e6ba208bSNick Mathewson 
1917a804767SNick Mathewson #ifndef NO_FORKING
1927a804767SNick Mathewson 
193e6ba208bSNick Mathewson static enum outcome
testcase_run_forked_(const struct testgroup_t * group,const struct testcase_t * testcase)1946c81be74SNick Mathewson testcase_run_forked_(const struct testgroup_t *group,
195a8203b34SNick Mathewson 		     const struct testcase_t *testcase)
196a8203b34SNick Mathewson {
1979f560bfaSNick Mathewson #ifdef _WIN32
198a8203b34SNick Mathewson 	/* Fork? On Win32?  How primitive!  We'll do what the smart kids do:
199a8203b34SNick Mathewson 	   we'll invoke our own exe (whose name we recall from the command
200a8203b34SNick Mathewson 	   line) with a command line that tells it to run just the test we
201a8203b34SNick Mathewson 	   want, and this time without forking.
202a8203b34SNick Mathewson 
203a8203b34SNick Mathewson 	   (No, threads aren't an option.  The whole point of forking is to
204a8203b34SNick Mathewson 	   share no state between tests.)
205a8203b34SNick Mathewson 	 */
206a8203b34SNick Mathewson 	int ok;
207a8203b34SNick Mathewson 	char buffer[LONGEST_TEST_NAME+256];
208a7a94310SNick Mathewson 	STARTUPINFOA si;
209a8203b34SNick Mathewson 	PROCESS_INFORMATION info;
210794e8f75Syuangongji 	DWORD ret;
211a8203b34SNick Mathewson 
212a8203b34SNick Mathewson 	if (!in_tinytest_main) {
2136c81be74SNick Mathewson 		printf("\nERROR.  On Windows, testcase_run_forked_ must be"
214a8203b34SNick Mathewson 		       " called from within tinytest_main.\n");
215a8203b34SNick Mathewson 		abort();
216a8203b34SNick Mathewson 	}
21781280062SNick Mathewson 	if (opt_verbosity>0)
218a8203b34SNick Mathewson 		printf("[forking] ");
219a8203b34SNick Mathewson 
220794e8f75Syuangongji 	snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s --timeout 0 %s%s",
22181280062SNick Mathewson 		 commandname, verbosity_flag, group->prefix, testcase->name);
222a8203b34SNick Mathewson 
223a8203b34SNick Mathewson 	memset(&si, 0, sizeof(si));
224a8203b34SNick Mathewson 	memset(&info, 0, sizeof(info));
225a8203b34SNick Mathewson 	si.cb = sizeof(si);
226a8203b34SNick Mathewson 
227a7a94310SNick Mathewson 	ok = CreateProcessA(commandname, buffer, NULL, NULL, 0,
228a8203b34SNick Mathewson 			   0, NULL, NULL, &si, &info);
229a8203b34SNick Mathewson 	if (!ok) {
230a8203b34SNick Mathewson 		printf("CreateProcess failed!\n");
231794e8f75Syuangongji 		return FAIL;
232a8203b34SNick Mathewson 	}
233794e8f75Syuangongji 	ret = WaitForSingleObject(info.hProcess,
234794e8f75Syuangongji 		(opt_timeout ? opt_timeout * 1000U : INFINITE));
235794e8f75Syuangongji 
236794e8f75Syuangongji 	if (ret == WAIT_OBJECT_0) {
237794e8f75Syuangongji 		GetExitCodeProcess(info.hProcess, &ret);
238794e8f75Syuangongji 	} else if (ret == WAIT_TIMEOUT) {
239794e8f75Syuangongji 		printf("timeout\n");
240794e8f75Syuangongji 	} else {
241794e8f75Syuangongji 		printf("Wait failed\n");
242794e8f75Syuangongji 	}
243a8203b34SNick Mathewson 	CloseHandle(info.hProcess);
244a8203b34SNick Mathewson 	CloseHandle(info.hThread);
245794e8f75Syuangongji 	if (ret == 0)
246e6ba208bSNick Mathewson 		return OK;
247794e8f75Syuangongji 	else if (ret == MAGIC_EXITCODE)
248e6ba208bSNick Mathewson 		return SKIP;
249e6ba208bSNick Mathewson 	else
250e6ba208bSNick Mathewson 		return FAIL;
251a8203b34SNick Mathewson #else
252a8203b34SNick Mathewson 	int outcome_pipe[2];
253a8203b34SNick Mathewson 	pid_t pid;
254a8203b34SNick Mathewson 	(void)group;
255a8203b34SNick Mathewson 
256a8203b34SNick Mathewson 	if (pipe(outcome_pipe))
257a8203b34SNick Mathewson 		perror("opening pipe");
258a8203b34SNick Mathewson 
25981280062SNick Mathewson 	if (opt_verbosity>0)
260a8203b34SNick Mathewson 		printf("[forking] ");
261a8203b34SNick Mathewson 	pid = fork();
262b62b31f1SNick Mathewson #ifdef FORK_BREAKS_GCOV
263b62b31f1SNick Mathewson 	vproc_transaction_begin(0);
264b62b31f1SNick Mathewson #endif
265a8203b34SNick Mathewson 	if (!pid) {
266a8203b34SNick Mathewson 		/* child. */
267a8203b34SNick Mathewson 		int test_r, write_r;
268e6ba208bSNick Mathewson 		char b[1];
269a8203b34SNick Mathewson 		close(outcome_pipe[0]);
2706c81be74SNick Mathewson 		test_r = testcase_run_bare_(testcase);
271e6ba208bSNick Mathewson 		assert(0<=(int)test_r && (int)test_r<=2);
272e6ba208bSNick Mathewson 		b[0] = "NYS"[test_r];
273f0bd83eaSNick Mathewson 		write_r = (int)write(outcome_pipe[1], b, 1);
274a8203b34SNick Mathewson 		if (write_r != 1) {
275a8203b34SNick Mathewson 			perror("write outcome to pipe");
276a8203b34SNick Mathewson 			exit(1);
277a8203b34SNick Mathewson 		}
278a8203b34SNick Mathewson 		exit(0);
2797bcace2dSNick Mathewson 		return FAIL; /* unreachable */
280a8203b34SNick Mathewson 	} else {
281a8203b34SNick Mathewson 		/* parent */
282*19a68bd1SAzat Khuzhin 		int status, r, exitcode;
283a8203b34SNick Mathewson 		char b[1];
284a8203b34SNick Mathewson 		/* Close this now, so that if the other side closes it,
285a8203b34SNick Mathewson 		 * our read fails. */
286a8203b34SNick Mathewson 		close(outcome_pipe[1]);
287f0bd83eaSNick Mathewson 		r = (int)read(outcome_pipe[0], b, 1);
288a8203b34SNick Mathewson 		if (r == 0) {
289a8203b34SNick Mathewson 			printf("[Lost connection!] ");
290*19a68bd1SAzat Khuzhin 			return FAIL;
291a8203b34SNick Mathewson 		} else if (r != 1) {
292a8203b34SNick Mathewson 			perror("read outcome from pipe");
293a8203b34SNick Mathewson 		}
294a8203b34SNick Mathewson 		waitpid(pid, &status, 0);
295*19a68bd1SAzat Khuzhin 		exitcode = WEXITSTATUS(status);
296a8203b34SNick Mathewson 		close(outcome_pipe[0]);
297*19a68bd1SAzat Khuzhin 		if (opt_verbosity>1)
298*19a68bd1SAzat Khuzhin 			printf("%s%s: exited with %i (%i)\n", group->prefix, testcase->name, exitcode, status);
299*19a68bd1SAzat Khuzhin 		if (exitcode != 0)
300*19a68bd1SAzat Khuzhin 		{
301*19a68bd1SAzat Khuzhin 			printf("[atexit failure!] ");
302*19a68bd1SAzat Khuzhin 			return FAIL;
303*19a68bd1SAzat Khuzhin 		}
304e6ba208bSNick Mathewson 		return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
305a8203b34SNick Mathewson 	}
306a8203b34SNick Mathewson #endif
307a8203b34SNick Mathewson }
308a8203b34SNick Mathewson 
3097a804767SNick Mathewson #endif /* !NO_FORKING */
3107a804767SNick Mathewson 
311a8203b34SNick Mathewson int
testcase_run_one(const struct testgroup_t * group,const struct testcase_t * testcase)312a8203b34SNick Mathewson testcase_run_one(const struct testgroup_t *group,
313a8203b34SNick Mathewson 		 const struct testcase_t *testcase)
314a8203b34SNick Mathewson {
315e6ba208bSNick Mathewson 	enum outcome outcome;
316a8203b34SNick Mathewson 
317ef7c4f79SNick Mathewson 	if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) {
31881280062SNick Mathewson 		if (opt_verbosity>0)
319ef7c4f79SNick Mathewson 			printf("%s%s: %s\n",
320ef7c4f79SNick Mathewson 			   group->prefix, testcase->name,
321ef7c4f79SNick Mathewson 			   (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED");
32281280062SNick Mathewson 		++n_skipped;
323e6ba208bSNick Mathewson 		return SKIP;
324a8203b34SNick Mathewson 	}
325a8203b34SNick Mathewson 
32681280062SNick Mathewson 	if (opt_verbosity>0 && !opt_forked) {
32781280062SNick Mathewson 		printf("%s%s: ", group->prefix, testcase->name);
32881280062SNick Mathewson 	} else {
32981280062SNick Mathewson 		if (opt_verbosity==0) printf(".");
330a8203b34SNick Mathewson 		cur_test_prefix = group->prefix;
331a8203b34SNick Mathewson 		cur_test_name = testcase->name;
332a8203b34SNick Mathewson 	}
333a8203b34SNick Mathewson 
3347a804767SNick Mathewson #ifndef NO_FORKING
33581280062SNick Mathewson 	if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
3366c81be74SNick Mathewson 		outcome = testcase_run_forked_(group, testcase);
337a8203b34SNick Mathewson 	} else {
3387a804767SNick Mathewson #else
3397a804767SNick Mathewson 	{
3407a804767SNick Mathewson #endif
3416c81be74SNick Mathewson 		outcome = testcase_run_bare_(testcase);
342a8203b34SNick Mathewson 	}
343a8203b34SNick Mathewson 
344e6ba208bSNick Mathewson 	if (outcome == OK) {
34581280062SNick Mathewson 		if (opt_verbosity>0 && !opt_forked)
346a8203b34SNick Mathewson 			puts(opt_verbosity==1?"OK":"");
347e6ba208bSNick Mathewson 	} else if (outcome == SKIP) {
34881280062SNick Mathewson 		if (opt_verbosity>0 && !opt_forked)
349e6ba208bSNick Mathewson 			puts("SKIPPED");
350a8203b34SNick Mathewson 	} else {
351a8203b34SNick Mathewson 		if (!opt_forked)
352a8203b34SNick Mathewson 			printf("\n  [%s FAILED]\n", testcase->name);
353a8203b34SNick Mathewson 	}
354a8203b34SNick Mathewson 
355a8203b34SNick Mathewson 	if (opt_forked) {
356e6ba208bSNick Mathewson 		exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
3577bcace2dSNick Mathewson 		return 1; /* unreachable */
358a8203b34SNick Mathewson 	} else {
359e6ba208bSNick Mathewson 		return (int)outcome;
360a8203b34SNick Mathewson 	}
361a8203b34SNick Mathewson }
362a8203b34SNick Mathewson 
363a8203b34SNick Mathewson int
364ef7c4f79SNick Mathewson tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag)
365a8203b34SNick Mathewson {
366a8203b34SNick Mathewson 	int i, j;
367f0bd83eaSNick Mathewson 	size_t length = LONGEST_TEST_NAME;
368a8203b34SNick Mathewson 	char fullname[LONGEST_TEST_NAME];
369a8203b34SNick Mathewson 	int found=0;
370a8203b34SNick Mathewson 	if (strstr(arg, ".."))
371a8203b34SNick Mathewson 		length = strstr(arg,"..")-arg;
372a8203b34SNick Mathewson 	for (i=0; groups[i].prefix; ++i) {
373a8203b34SNick Mathewson 		for (j=0; groups[i].cases[j].name; ++j) {
374ef7c4f79SNick Mathewson 			struct testcase_t *testcase = &groups[i].cases[j];
375f0bd83eaSNick Mathewson 			snprintf(fullname, sizeof(fullname), "%s%s",
376ef7c4f79SNick Mathewson 				 groups[i].prefix, testcase->name);
377ef7c4f79SNick Mathewson 			if (!flag) { /* Hack! */
378ef7c4f79SNick Mathewson 				printf("    %s", fullname);
379ef7c4f79SNick Mathewson 				if (testcase->flags & TT_OFF_BY_DEFAULT)
380ef7c4f79SNick Mathewson 					puts("   (Off by default)");
381ef7c4f79SNick Mathewson 				else if (testcase->flags & TT_SKIP)
382ef7c4f79SNick Mathewson 					puts("  (DISABLED)");
383ef7c4f79SNick Mathewson 				else
384ef7c4f79SNick Mathewson 					puts("");
385ef7c4f79SNick Mathewson 			}
386a8203b34SNick Mathewson 			if (!strncmp(fullname, arg, length)) {
387ef7c4f79SNick Mathewson 				if (set)
388ef7c4f79SNick Mathewson 					testcase->flags |= flag;
389ef7c4f79SNick Mathewson 				else
390ef7c4f79SNick Mathewson 					testcase->flags &= ~flag;
391a8203b34SNick Mathewson 				++found;
392a8203b34SNick Mathewson 			}
393a8203b34SNick Mathewson 		}
394a8203b34SNick Mathewson 	}
395a8203b34SNick Mathewson 	return found;
396a8203b34SNick Mathewson }
397a8203b34SNick Mathewson 
398a8203b34SNick Mathewson static void
399b3460387SNick Mathewson usage(struct testgroup_t *groups, int list_groups)
400a8203b34SNick Mathewson {
40175d7e1ffSAzat Khuzhin 	puts("Options are: [--verbose|--quiet|--terse] [--no-fork] [--timeout <sec>]");
402b3460387SNick Mathewson 	puts("  Specify tests by name, or using a prefix ending with '..'");
403ef7c4f79SNick Mathewson 	puts("  To skip a test, prefix its name with a colon.");
404ef7c4f79SNick Mathewson 	puts("  To enable a disabled test, prefix its name with a plus.");
405b3460387SNick Mathewson 	puts("  Use --list-tests for a list of tests.");
406b3460387SNick Mathewson 	if (list_groups) {
407a8203b34SNick Mathewson 		puts("Known tests are:");
408ef7c4f79SNick Mathewson 		tinytest_set_flag_(groups, "..", 1, 0);
409b3460387SNick Mathewson 	}
410a8203b34SNick Mathewson 	exit(0);
411a8203b34SNick Mathewson }
412a8203b34SNick Mathewson 
413ef7c4f79SNick Mathewson static int
414ef7c4f79SNick Mathewson process_test_alias(struct testgroup_t *groups, const char *test)
415ef7c4f79SNick Mathewson {
416ef7c4f79SNick Mathewson 	int i, j, n, r;
417ef7c4f79SNick Mathewson 	for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) {
418ef7c4f79SNick Mathewson 		if (!strcmp(cfg_aliases[i].name, test)) {
419ef7c4f79SNick Mathewson 			n = 0;
420ef7c4f79SNick Mathewson 			for (j = 0; cfg_aliases[i].tests[j]; ++j) {
421ef7c4f79SNick Mathewson 				r = process_test_option(groups, cfg_aliases[i].tests[j]);
422ef7c4f79SNick Mathewson 				if (r<0)
423ef7c4f79SNick Mathewson 					return -1;
424ef7c4f79SNick Mathewson 				n += r;
425ef7c4f79SNick Mathewson 			}
426ef7c4f79SNick Mathewson 			return n;
427ef7c4f79SNick Mathewson 		}
428ef7c4f79SNick Mathewson 	}
429ef7c4f79SNick Mathewson 	printf("No such test alias as @%s!",test);
430ef7c4f79SNick Mathewson 	return -1;
431ef7c4f79SNick Mathewson }
432ef7c4f79SNick Mathewson 
433ef7c4f79SNick Mathewson static int
434ef7c4f79SNick Mathewson process_test_option(struct testgroup_t *groups, const char *test)
435ef7c4f79SNick Mathewson {
436ef7c4f79SNick Mathewson 	int flag = TT_ENABLED_;
437ef7c4f79SNick Mathewson 	int n = 0;
438ef7c4f79SNick Mathewson 	if (test[0] == '@') {
439ef7c4f79SNick Mathewson 		return process_test_alias(groups, test + 1);
440ef7c4f79SNick Mathewson 	} else if (test[0] == ':') {
441ef7c4f79SNick Mathewson 		++test;
442ef7c4f79SNick Mathewson 		flag = TT_SKIP;
443ef7c4f79SNick Mathewson 	} else if (test[0] == '+') {
444ef7c4f79SNick Mathewson 		++test;
445ef7c4f79SNick Mathewson 		++n;
446ef7c4f79SNick Mathewson 		if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) {
447ef7c4f79SNick Mathewson 			printf("No such test as %s!\n", test);
448ef7c4f79SNick Mathewson 			return -1;
449ef7c4f79SNick Mathewson 		}
450ef7c4f79SNick Mathewson 	} else {
451ef7c4f79SNick Mathewson 		++n;
452ef7c4f79SNick Mathewson 	}
453ef7c4f79SNick Mathewson 	if (!tinytest_set_flag_(groups, test, 1, flag)) {
454ef7c4f79SNick Mathewson 		printf("No such test as %s!\n", test);
455ef7c4f79SNick Mathewson 		return -1;
456ef7c4f79SNick Mathewson 	}
457ef7c4f79SNick Mathewson 	return n;
458ef7c4f79SNick Mathewson }
459ef7c4f79SNick Mathewson 
460ef7c4f79SNick Mathewson void
461ef7c4f79SNick Mathewson tinytest_set_aliases(const struct testlist_alias_t *aliases)
462ef7c4f79SNick Mathewson {
463ef7c4f79SNick Mathewson 	cfg_aliases = aliases;
464ef7c4f79SNick Mathewson }
465ef7c4f79SNick Mathewson 
466a8203b34SNick Mathewson int
467a8203b34SNick Mathewson tinytest_main(int c, const char **v, struct testgroup_t *groups)
468a8203b34SNick Mathewson {
469a8203b34SNick Mathewson 	int i, j, n=0;
470a8203b34SNick Mathewson 
4719f560bfaSNick Mathewson #ifdef _WIN32
47257def346SEd Day 	const char *sp = strrchr(v[0], '.');
473812d42e8SNick Mathewson 	const char *extension = "";
474812d42e8SNick Mathewson 	if (!sp || stricmp(sp, ".exe"))
475812d42e8SNick Mathewson 		extension = ".exe"; /* Add an exe so CreateProcess will work */
476812d42e8SNick Mathewson 	snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension);
477812d42e8SNick Mathewson 	commandname[MAX_PATH]='\0';
478a8203b34SNick Mathewson #endif
479a8203b34SNick Mathewson 	for (i=1; i<c; ++i) {
480a8203b34SNick Mathewson 		if (v[i][0] == '-') {
48181280062SNick Mathewson 			if (!strcmp(v[i], "--RUNNING-FORKED")) {
482a8203b34SNick Mathewson 				opt_forked = 1;
48381280062SNick Mathewson 			} else if (!strcmp(v[i], "--no-fork")) {
48481280062SNick Mathewson 				opt_nofork = 1;
48581280062SNick Mathewson 			} else if (!strcmp(v[i], "--quiet")) {
48681280062SNick Mathewson 				opt_verbosity = -1;
48781280062SNick Mathewson 				verbosity_flag = "--quiet";
48881280062SNick Mathewson 			} else if (!strcmp(v[i], "--verbose")) {
489a8203b34SNick Mathewson 				opt_verbosity = 2;
49081280062SNick Mathewson 				verbosity_flag = "--verbose";
49181280062SNick Mathewson 			} else if (!strcmp(v[i], "--terse")) {
49281280062SNick Mathewson 				opt_verbosity = 0;
49381280062SNick Mathewson 				verbosity_flag = "--terse";
49481280062SNick Mathewson 			} else if (!strcmp(v[i], "--help")) {
495b3460387SNick Mathewson 				usage(groups, 0);
496b3460387SNick Mathewson 			} else if (!strcmp(v[i], "--list-tests")) {
497b3460387SNick Mathewson 				usage(groups, 1);
498b64dbfb6SAzat Khuzhin 			} else if (!strcmp(v[i], "--timeout")) {
49975d7e1ffSAzat Khuzhin 				++i;
50075d7e1ffSAzat Khuzhin 				if (i >= c) {
501b64dbfb6SAzat Khuzhin 					fprintf(stderr, "--timeout requires argument\n");
502b64dbfb6SAzat Khuzhin 					return -1;
503b64dbfb6SAzat Khuzhin 				}
504b64dbfb6SAzat Khuzhin 				opt_timeout = (unsigned)atoi(v[i]);
50581280062SNick Mathewson 			} else {
506b64dbfb6SAzat Khuzhin 				fprintf(stderr, "Unknown option %s. Try --help\n", v[i]);
507a8203b34SNick Mathewson 				return -1;
508a8203b34SNick Mathewson 			}
509a8203b34SNick Mathewson 		} else {
510ef7c4f79SNick Mathewson 			int r = process_test_option(groups, v[i]);
511ef7c4f79SNick Mathewson 			if (r<0)
512a8203b34SNick Mathewson 				return -1;
513ef7c4f79SNick Mathewson 			n += r;
514a8203b34SNick Mathewson 		}
515a8203b34SNick Mathewson 	}
516a8203b34SNick Mathewson 	if (!n)
517ef7c4f79SNick Mathewson 		tinytest_set_flag_(groups, "..", 1, TT_ENABLED_);
518a8203b34SNick Mathewson 
5197a804767SNick Mathewson #ifdef _IONBF
520a8203b34SNick Mathewson 	setvbuf(stdout, NULL, _IONBF, 0);
5217a804767SNick Mathewson #endif
522a8203b34SNick Mathewson 
523a8203b34SNick Mathewson 	++in_tinytest_main;
5246ea1ec68SAzat Khuzhin 	for (i = 0; groups[i].prefix; ++i) {
5256ea1ec68SAzat Khuzhin 		struct testgroup_t *group = &groups[i];
5266ea1ec68SAzat Khuzhin 		for (j = 0; group->cases[j].name; ++j) {
5276ea1ec68SAzat Khuzhin 			struct testcase_t *testcase = &group->cases[j];
5286ea1ec68SAzat Khuzhin 			int test_attempts = 3;
5296ea1ec68SAzat Khuzhin 			int test_ret_err;
5306ea1ec68SAzat Khuzhin 
5316ea1ec68SAzat Khuzhin 			if (!(testcase->flags & TT_ENABLED_))
5326ea1ec68SAzat Khuzhin 				continue;
5336ea1ec68SAzat Khuzhin 
5346ea1ec68SAzat Khuzhin 			for (;;) {
5356ea1ec68SAzat Khuzhin 				test_ret_err = testcase_run_one(group, testcase);
5366ea1ec68SAzat Khuzhin 
5376ea1ec68SAzat Khuzhin 				if (test_ret_err == OK)
5386ea1ec68SAzat Khuzhin 					break;
5396ea1ec68SAzat Khuzhin 				if (!(testcase->flags & TT_RETRIABLE))
5406ea1ec68SAzat Khuzhin 					break;
5416ea1ec68SAzat Khuzhin 				printf("\n  [RETRYING %s (%i)]\n", testcase->name, test_attempts);
5426ea1ec68SAzat Khuzhin 				if (!test_attempts--)
5436ea1ec68SAzat Khuzhin 					break;
5446ea1ec68SAzat Khuzhin 			}
5456ea1ec68SAzat Khuzhin 
5466ea1ec68SAzat Khuzhin 			switch (test_ret_err) {
5476ea1ec68SAzat Khuzhin 				case OK:   ++n_ok;      break;
5486ea1ec68SAzat Khuzhin 				case SKIP: ++n_skipped; break;
5496ea1ec68SAzat Khuzhin 				default:   ++n_bad;     break;
5506ea1ec68SAzat Khuzhin 			}
5516ea1ec68SAzat Khuzhin 		}
5526ea1ec68SAzat Khuzhin 	}
553a8203b34SNick Mathewson 
554a8203b34SNick Mathewson 	--in_tinytest_main;
555a8203b34SNick Mathewson 
55681280062SNick Mathewson 	if (opt_verbosity==0)
55781280062SNick Mathewson 		puts("");
55881280062SNick Mathewson 
559a8203b34SNick Mathewson 	if (n_bad)
56081280062SNick Mathewson 		printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
56181280062SNick Mathewson 		       n_bad+n_ok,n_skipped);
56281280062SNick Mathewson 	else if (opt_verbosity >= 1)
56381280062SNick Mathewson 		printf("%d tests ok.  (%d skipped)\n", n_ok, n_skipped);
56481280062SNick Mathewson 
565a8203b34SNick Mathewson 	return (n_bad == 0) ? 0 : 1;
566a8203b34SNick Mathewson }
567a8203b34SNick Mathewson 
568a8203b34SNick Mathewson int
5696c81be74SNick Mathewson tinytest_get_verbosity_(void)
570a8203b34SNick Mathewson {
571a8203b34SNick Mathewson 	return opt_verbosity;
572a8203b34SNick Mathewson }
573a8203b34SNick Mathewson 
574a8203b34SNick Mathewson void
5756c81be74SNick Mathewson tinytest_set_test_failed_(void)
576a8203b34SNick Mathewson {
57781280062SNick Mathewson 	if (opt_verbosity <= 0 && cur_test_name) {
57881280062SNick Mathewson 		if (opt_verbosity==0) puts("");
57981280062SNick Mathewson 		printf("%s%s: ", cur_test_prefix, cur_test_name);
580a8203b34SNick Mathewson 		cur_test_name = NULL;
581a8203b34SNick Mathewson 	}
582794e8f75Syuangongji 	cur_test_outcome = FAIL;
583a8203b34SNick Mathewson }
584a8203b34SNick Mathewson 
585e6ba208bSNick Mathewson void
5866c81be74SNick Mathewson tinytest_set_test_skipped_(void)
587e6ba208bSNick Mathewson {
588e6ba208bSNick Mathewson 	if (cur_test_outcome==OK)
589e6ba208bSNick Mathewson 		cur_test_outcome = SKIP;
590e6ba208bSNick Mathewson }
591e6ba208bSNick Mathewson 
5927a804767SNick Mathewson char *
5937a804767SNick Mathewson tinytest_format_hex_(const void *val_, unsigned long len)
5947a804767SNick Mathewson {
5957a804767SNick Mathewson 	const unsigned char *val = val_;
5967a804767SNick Mathewson 	char *result, *cp;
5977a804767SNick Mathewson 	size_t i;
5987a804767SNick Mathewson 
5997a804767SNick Mathewson 	if (!val)
6007a804767SNick Mathewson 		return strdup("null");
6017a804767SNick Mathewson 	if (!(result = malloc(len*2+1)))
6027a804767SNick Mathewson 		return strdup("<allocation failure>");
6037a804767SNick Mathewson 	cp = result;
6047a804767SNick Mathewson 	for (i=0;i<len;++i) {
6057a804767SNick Mathewson 		*cp++ = "0123456789ABCDEF"[val[i] >> 4];
6067a804767SNick Mathewson 		*cp++ = "0123456789ABCDEF"[val[i] & 0x0f];
6077a804767SNick Mathewson 	}
6087a804767SNick Mathewson 	*cp = 0;
6097a804767SNick Mathewson 	return result;
6107a804767SNick Mathewson }
611