xref: /freebsd/contrib/ntp/sntp/libevent/test/tinytest.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
1 /* tinytest.c -- Copyright 2009-2012 Nick Mathewson
2  *
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions
5  * are met:
6  * 1. Redistributions of source code must retain the above copyright
7  *    notice, this list of conditions and the following disclaimer.
8  * 2. Redistributions in binary form must reproduce the above copyright
9  *    notice, this list of conditions and the following disclaimer in the
10  *    documentation and/or other materials provided with the distribution.
11  * 3. The name of the author may not be used to endorse or promote products
12  *    derived from this software without specific prior written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 #ifdef TINYTEST_LOCAL
26 #include "tinytest_local.h"
27 #endif
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <assert.h>
33 
34 #ifndef NO_FORKING
35 
36 #ifdef _WIN32
37 #include <windows.h>
38 #else
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <unistd.h>
42 #endif
43 
44 #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
45 #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \
46     __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070)
47 /* Workaround for a stupid bug in OSX 10.6 */
48 #define FORK_BREAKS_GCOV
49 #include <vproc.h>
50 #endif
51 #endif
52 
53 #endif /* !NO_FORKING */
54 
55 #ifndef __GNUC__
56 #define __attribute__(x)
57 #endif
58 
59 #include "tinytest.h"
60 #include "tinytest_macros.h"
61 
62 #define LONGEST_TEST_NAME 16384
63 #define DEFAULT_TESTCASE_TIMEOUT 30U
64 #define MAGIC_EXITCODE 42
65 
66 static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/
67 static int n_ok = 0; /**< Number of tests that have passed */
68 static int n_bad = 0; /**< Number of tests that have failed. */
69 static int n_skipped = 0; /**< Number of tests that have been skipped. */
70 
71 static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/
72 static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */
73 static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */
74 static unsigned int opt_timeout = DEFAULT_TESTCASE_TIMEOUT; /**< Timeout for every test (using alarm()) */
75 const char *verbosity_flag = "";
76 
77 const struct testlist_alias_t *cfg_aliases=NULL;
78 
79 enum outcome { SKIP=2, OK=1, FAIL=0 };
80 static enum outcome cur_test_outcome = 0;
81 const char *cur_test_prefix = NULL; /**< prefix of the current test group */
82 /** Name of the current test, if we haven't logged is yet. Used for --quiet */
83 const char *cur_test_name = NULL;
84 
85 static void usage(struct testgroup_t *groups, int list_groups)
86 	__attribute__((noreturn));
87 static int process_test_option(struct testgroup_t *groups, const char *test);
88 
89 #ifdef _WIN32
90 /* Copy of argv[0] for win32. */
91 static char commandname[MAX_PATH+1];
92 
93 struct timeout_thread_args {
94 	const testcase_fn *fn;
95 	void *env;
96 };
97 
98 static DWORD WINAPI
99 timeout_thread_proc_(LPVOID arg)
100 {
101 	struct timeout_thread_args *args = arg;
102 	(*(args->fn))(args->env);
103 	ExitThread(cur_test_outcome == FAIL ? 1 : 0);
104 }
105 
106 static enum outcome
107 testcase_run_in_thread_(const struct testcase_t *testcase, void *env)
108 {
109 	/* We will never run testcase in a new thread when the
110 	timeout is set to zero */
111 	assert(opt_timeout);
112 	DWORD ret, tid;
113 	HANDLE handle;
114 	struct timeout_thread_args args = {
115 		&(testcase->fn),
116 		env
117 	};
118 
119 	handle =CreateThread(NULL, 0, timeout_thread_proc_,
120 		(LPVOID)&args, 0, &tid);
121 	ret = WaitForSingleObject(handle, opt_timeout * 1000U);
122 	if (ret == WAIT_OBJECT_0) {
123 		ret = 0;
124 		if (!GetExitCodeThread(handle, &ret)) {
125 			printf("GetExitCodeThread failed\n");
126 			ret = 1;
127 		}
128 	} else if (ret == WAIT_TIMEOUT)	{
129 		printf("timeout\n");
130 	} else {
131 		printf("Wait failed\n");
132 	}
133 	CloseHandle(handle);
134 	if (ret == 0)
135 		return OK;
136 	else if (ret == MAGIC_EXITCODE)
137 		return SKIP;
138 	else
139 		return FAIL;
140 }
141 #else
142 static unsigned int testcase_set_timeout_(void)
143 {
144 	return alarm(opt_timeout);
145 }
146 
147 static unsigned int testcase_reset_timeout_(void)
148 {
149 	return alarm(0);
150 }
151 #endif
152 
153 static enum outcome
154 testcase_run_bare_(const struct testcase_t *testcase)
155 {
156 	void *env = NULL;
157 	int outcome;
158 	if (testcase->setup) {
159 		env = testcase->setup->setup_fn(testcase);
160 		if (!env)
161 			return FAIL;
162 		else if (env == (void*)TT_SKIP)
163 			return SKIP;
164 	}
165 
166 	cur_test_outcome = OK;
167 	{
168 		if (opt_timeout) {
169 #ifdef _WIN32
170 			cur_test_outcome = testcase_run_in_thread_(testcase, env);
171 #else
172 			testcase_set_timeout_();
173 			testcase->fn(env);
174 			testcase_reset_timeout_();
175 #endif
176 		} else {
177 			testcase->fn(env);
178 		}
179 	}
180 	outcome = cur_test_outcome;
181 
182 	if (testcase->setup) {
183 		if (testcase->setup->cleanup_fn(testcase, env) == 0)
184 			outcome = FAIL;
185 	}
186 
187 	return outcome;
188 }
189 
190 
191 #ifndef NO_FORKING
192 
193 static enum outcome
194 testcase_run_forked_(const struct testgroup_t *group,
195 		     const struct testcase_t *testcase)
196 {
197 #ifdef _WIN32
198 	/* Fork? On Win32?  How primitive!  We'll do what the smart kids do:
199 	   we'll invoke our own exe (whose name we recall from the command
200 	   line) with a command line that tells it to run just the test we
201 	   want, and this time without forking.
202 
203 	   (No, threads aren't an option.  The whole point of forking is to
204 	   share no state between tests.)
205 	 */
206 	int ok;
207 	char buffer[LONGEST_TEST_NAME+256];
208 	STARTUPINFOA si;
209 	PROCESS_INFORMATION info;
210 	DWORD ret;
211 
212 	if (!in_tinytest_main) {
213 		printf("\nERROR.  On Windows, testcase_run_forked_ must be"
214 		       " called from within tinytest_main.\n");
215 		abort();
216 	}
217 	if (opt_verbosity>0)
218 		printf("[forking] ");
219 
220 	snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s --timeout 0 %s%s",
221 		 commandname, verbosity_flag, group->prefix, testcase->name);
222 
223 	memset(&si, 0, sizeof(si));
224 	memset(&info, 0, sizeof(info));
225 	si.cb = sizeof(si);
226 
227 	ok = CreateProcessA(commandname, buffer, NULL, NULL, 0,
228 			   0, NULL, NULL, &si, &info);
229 	if (!ok) {
230 		printf("CreateProcess failed!\n");
231 		return FAIL;
232 	}
233 	ret = WaitForSingleObject(info.hProcess,
234 		(opt_timeout ? opt_timeout * 1000U : INFINITE));
235 
236 	if (ret == WAIT_OBJECT_0) {
237 		GetExitCodeProcess(info.hProcess, &ret);
238 	} else if (ret == WAIT_TIMEOUT) {
239 		printf("timeout\n");
240 	} else {
241 		printf("Wait failed\n");
242 	}
243 	CloseHandle(info.hProcess);
244 	CloseHandle(info.hThread);
245 	if (ret == 0)
246 		return OK;
247 	else if (ret == MAGIC_EXITCODE)
248 		return SKIP;
249 	else
250 		return FAIL;
251 #else
252 	int outcome_pipe[2];
253 	pid_t pid;
254 	(void)group;
255 
256 	if (pipe(outcome_pipe))
257 		perror("opening pipe");
258 
259 	if (opt_verbosity>0)
260 		printf("[forking] ");
261 	pid = fork();
262 #ifdef FORK_BREAKS_GCOV
263 	vproc_transaction_begin(0);
264 #endif
265 	if (!pid) {
266 		/* child. */
267 		int test_r, write_r;
268 		char b[1];
269 		close(outcome_pipe[0]);
270 		test_r = testcase_run_bare_(testcase);
271 		assert(0<=(int)test_r && (int)test_r<=2);
272 		b[0] = "NYS"[test_r];
273 		write_r = (int)write(outcome_pipe[1], b, 1);
274 		if (write_r != 1) {
275 			perror("write outcome to pipe");
276 			exit(1);
277 		}
278 		exit(0);
279 		return FAIL; /* unreachable */
280 	} else {
281 		/* parent */
282 		int status, r, exitcode;
283 		char b[1];
284 		/* Close this now, so that if the other side closes it,
285 		 * our read fails. */
286 		close(outcome_pipe[1]);
287 		r = (int)read(outcome_pipe[0], b, 1);
288 		if (r == 0) {
289 			printf("[Lost connection!] ");
290 			return FAIL;
291 		} else if (r != 1) {
292 			perror("read outcome from pipe");
293 		}
294 		waitpid(pid, &status, 0);
295 		exitcode = WEXITSTATUS(status);
296 		close(outcome_pipe[0]);
297 		if (opt_verbosity>1)
298 			printf("%s%s: exited with %i (%i)\n", group->prefix, testcase->name, exitcode, status);
299 		if (exitcode != 0)
300 		{
301 			printf("[atexit failure!] ");
302 			return FAIL;
303 		}
304 		return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
305 	}
306 #endif
307 }
308 
309 #endif /* !NO_FORKING */
310 
311 int
312 testcase_run_one(const struct testgroup_t *group,
313 		 const struct testcase_t *testcase)
314 {
315 	enum outcome outcome;
316 
317 	if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) {
318 		if (opt_verbosity>0)
319 			printf("%s%s: %s\n",
320 			   group->prefix, testcase->name,
321 			   (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED");
322 		++n_skipped;
323 		return SKIP;
324 	}
325 
326 	if (opt_verbosity>0 && !opt_forked) {
327 		printf("%s%s: ", group->prefix, testcase->name);
328 	} else {
329 		if (opt_verbosity==0) printf(".");
330 		cur_test_prefix = group->prefix;
331 		cur_test_name = testcase->name;
332 	}
333 
334 #ifndef NO_FORKING
335 	if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
336 		outcome = testcase_run_forked_(group, testcase);
337 	} else {
338 #else
339 	{
340 #endif
341 		outcome = testcase_run_bare_(testcase);
342 	}
343 
344 	if (outcome == OK) {
345 		if (opt_verbosity>0 && !opt_forked)
346 			puts(opt_verbosity==1?"OK":"");
347 	} else if (outcome == SKIP) {
348 		if (opt_verbosity>0 && !opt_forked)
349 			puts("SKIPPED");
350 	} else {
351 		if (!opt_forked)
352 			printf("\n  [%s FAILED]\n", testcase->name);
353 	}
354 
355 	if (opt_forked) {
356 		exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
357 		return 1; /* unreachable */
358 	} else {
359 		return (int)outcome;
360 	}
361 }
362 
363 int
364 tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag)
365 {
366 	int i, j;
367 	size_t length = LONGEST_TEST_NAME;
368 	char fullname[LONGEST_TEST_NAME];
369 	int found=0;
370 	if (strstr(arg, ".."))
371 		length = strstr(arg,"..")-arg;
372 	for (i=0; groups[i].prefix; ++i) {
373 		for (j=0; groups[i].cases[j].name; ++j) {
374 			struct testcase_t *testcase = &groups[i].cases[j];
375 			snprintf(fullname, sizeof(fullname), "%s%s",
376 				 groups[i].prefix, testcase->name);
377 			if (!flag) { /* Hack! */
378 				printf("    %s", fullname);
379 				if (testcase->flags & TT_OFF_BY_DEFAULT)
380 					puts("   (Off by default)");
381 				else if (testcase->flags & TT_SKIP)
382 					puts("  (DISABLED)");
383 				else
384 					puts("");
385 			}
386 			if (!strncmp(fullname, arg, length)) {
387 				if (set)
388 					testcase->flags |= flag;
389 				else
390 					testcase->flags &= ~flag;
391 				++found;
392 			}
393 		}
394 	}
395 	return found;
396 }
397 
398 static void
399 usage(struct testgroup_t *groups, int list_groups)
400 {
401 	puts("Options are: [--verbose|--quiet|--terse] [--no-fork] [--timeout <sec>]");
402 	puts("  Specify tests by name, or using a prefix ending with '..'");
403 	puts("  To skip a test, prefix its name with a colon.");
404 	puts("  To enable a disabled test, prefix its name with a plus.");
405 	puts("  Use --list-tests for a list of tests.");
406 	if (list_groups) {
407 		puts("Known tests are:");
408 		tinytest_set_flag_(groups, "..", 1, 0);
409 	}
410 	exit(0);
411 }
412 
413 static int
414 process_test_alias(struct testgroup_t *groups, const char *test)
415 {
416 	int i, j, n, r;
417 	for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) {
418 		if (!strcmp(cfg_aliases[i].name, test)) {
419 			n = 0;
420 			for (j = 0; cfg_aliases[i].tests[j]; ++j) {
421 				r = process_test_option(groups, cfg_aliases[i].tests[j]);
422 				if (r<0)
423 					return -1;
424 				n += r;
425 			}
426 			return n;
427 		}
428 	}
429 	printf("No such test alias as @%s!",test);
430 	return -1;
431 }
432 
433 static int
434 process_test_option(struct testgroup_t *groups, const char *test)
435 {
436 	int flag = TT_ENABLED_;
437 	int n = 0;
438 	if (test[0] == '@') {
439 		return process_test_alias(groups, test + 1);
440 	} else if (test[0] == ':') {
441 		++test;
442 		flag = TT_SKIP;
443 	} else if (test[0] == '+') {
444 		++test;
445 		++n;
446 		if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) {
447 			printf("No such test as %s!\n", test);
448 			return -1;
449 		}
450 	} else {
451 		++n;
452 	}
453 	if (!tinytest_set_flag_(groups, test, 1, flag)) {
454 		printf("No such test as %s!\n", test);
455 		return -1;
456 	}
457 	return n;
458 }
459 
460 void
461 tinytest_set_aliases(const struct testlist_alias_t *aliases)
462 {
463 	cfg_aliases = aliases;
464 }
465 
466 int
467 tinytest_main(int c, const char **v, struct testgroup_t *groups)
468 {
469 	int i, j, n=0;
470 
471 #ifdef _WIN32
472 	const char *sp = strrchr(v[0], '.');
473 	const char *extension = "";
474 	if (!sp || stricmp(sp, ".exe"))
475 		extension = ".exe"; /* Add an exe so CreateProcess will work */
476 	snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension);
477 	commandname[MAX_PATH]='\0';
478 #endif
479 	for (i=1; i<c; ++i) {
480 		if (v[i][0] == '-') {
481 			if (!strcmp(v[i], "--RUNNING-FORKED")) {
482 				opt_forked = 1;
483 			} else if (!strcmp(v[i], "--no-fork")) {
484 				opt_nofork = 1;
485 			} else if (!strcmp(v[i], "--quiet")) {
486 				opt_verbosity = -1;
487 				verbosity_flag = "--quiet";
488 			} else if (!strcmp(v[i], "--verbose")) {
489 				opt_verbosity = 2;
490 				verbosity_flag = "--verbose";
491 			} else if (!strcmp(v[i], "--terse")) {
492 				opt_verbosity = 0;
493 				verbosity_flag = "--terse";
494 			} else if (!strcmp(v[i], "--help")) {
495 				usage(groups, 0);
496 			} else if (!strcmp(v[i], "--list-tests")) {
497 				usage(groups, 1);
498 			} else if (!strcmp(v[i], "--timeout")) {
499 				++i;
500 				if (i >= c) {
501 					fprintf(stderr, "--timeout requires argument\n");
502 					return -1;
503 				}
504 				opt_timeout = (unsigned)atoi(v[i]);
505 			} else {
506 				fprintf(stderr, "Unknown option %s. Try --help\n", v[i]);
507 				return -1;
508 			}
509 		} else {
510 			int r = process_test_option(groups, v[i]);
511 			if (r<0)
512 				return -1;
513 			n += r;
514 		}
515 	}
516 	if (!n)
517 		tinytest_set_flag_(groups, "..", 1, TT_ENABLED_);
518 
519 #ifdef _IONBF
520 	setvbuf(stdout, NULL, _IONBF, 0);
521 #endif
522 
523 	++in_tinytest_main;
524 	for (i = 0; groups[i].prefix; ++i) {
525 		struct testgroup_t *group = &groups[i];
526 		for (j = 0; group->cases[j].name; ++j) {
527 			struct testcase_t *testcase = &group->cases[j];
528 			int test_attempts = 3;
529 			int test_ret_err;
530 
531 			if (!(testcase->flags & TT_ENABLED_))
532 				continue;
533 
534 			for (;;) {
535 				test_ret_err = testcase_run_one(group, testcase);
536 
537 				if (test_ret_err == OK)
538 					break;
539 				if (!(testcase->flags & TT_RETRIABLE))
540 					break;
541 				printf("\n  [RETRYING %s (%i)]\n", testcase->name, test_attempts);
542 				if (!test_attempts--)
543 					break;
544 			}
545 
546 			switch (test_ret_err) {
547 				case OK:   ++n_ok;      break;
548 				case SKIP: ++n_skipped; break;
549 				default:   ++n_bad;     break;
550 			}
551 		}
552 	}
553 
554 	--in_tinytest_main;
555 
556 	if (opt_verbosity==0)
557 		puts("");
558 
559 	if (n_bad)
560 		printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
561 		       n_bad+n_ok,n_skipped);
562 	else if (opt_verbosity >= 1)
563 		printf("%d tests ok.  (%d skipped)\n", n_ok, n_skipped);
564 
565 	return (n_bad == 0) ? 0 : 1;
566 }
567 
568 int
569 tinytest_get_verbosity_(void)
570 {
571 	return opt_verbosity;
572 }
573 
574 void
575 tinytest_set_test_failed_(void)
576 {
577 	if (opt_verbosity <= 0 && cur_test_name) {
578 		if (opt_verbosity==0) puts("");
579 		printf("%s%s: ", cur_test_prefix, cur_test_name);
580 		cur_test_name = NULL;
581 	}
582 	cur_test_outcome = FAIL;
583 }
584 
585 void
586 tinytest_set_test_skipped_(void)
587 {
588 	if (cur_test_outcome==OK)
589 		cur_test_outcome = SKIP;
590 }
591 
592 char *
593 tinytest_format_hex_(const void *val_, unsigned long len)
594 {
595 	const unsigned char *val = val_;
596 	char *result, *cp;
597 	size_t i;
598 
599 	if (!val)
600 		return strdup("null");
601 	if (!(result = malloc(len*2+1)))
602 		return strdup("<allocation failure>");
603 	cp = result;
604 	for (i=0;i<len;++i) {
605 		*cp++ = "0123456789ABCDEF"[val[i] >> 4];
606 		*cp++ = "0123456789ABCDEF"[val[i] & 0x0f];
607 	}
608 	*cp = 0;
609 	return result;
610 }
611