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