xref: /freebsd/contrib/libevent/test/tinytest.c (revision d0b2dbfa0ecf2bbc9709efc5e20baf8e4b44bbbf)
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 
64 static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/
65 static int n_ok = 0; /**< Number of tests that have passed */
66 static int n_bad = 0; /**< Number of tests that have failed. */
67 static int n_skipped = 0; /**< Number of tests that have been skipped. */
68 
69 static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/
70 static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */
71 static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */
72 const char *verbosity_flag = "";
73 
74 const struct testlist_alias_t *cfg_aliases=NULL;
75 
76 enum outcome { SKIP=2, OK=1, FAIL=0 };
77 static enum outcome cur_test_outcome = 0;
78 const char *cur_test_prefix = NULL; /**< prefix of the current test group */
79 /** Name of the current test, if we haven't logged is yet. Used for --quiet */
80 const char *cur_test_name = NULL;
81 
82 #ifdef _WIN32
83 /* Copy of argv[0] for win32. */
84 static char commandname[MAX_PATH+1];
85 #endif
86 
87 static void usage(struct testgroup_t *groups, int list_groups)
88   __attribute__((noreturn));
89 static int process_test_option(struct testgroup_t *groups, const char *test);
90 
91 static enum outcome
92 testcase_run_bare_(const struct testcase_t *testcase)
93 {
94 	void *env = NULL;
95 	int outcome;
96 	if (testcase->setup) {
97 		env = testcase->setup->setup_fn(testcase);
98 		if (!env)
99 			return FAIL;
100 		else if (env == (void*)TT_SKIP)
101 			return SKIP;
102 	}
103 
104 	cur_test_outcome = OK;
105 	testcase->fn(env);
106 	outcome = cur_test_outcome;
107 
108 	if (testcase->setup) {
109 		if (testcase->setup->cleanup_fn(testcase, env) == 0)
110 			outcome = FAIL;
111 	}
112 
113 	return outcome;
114 }
115 
116 #define MAGIC_EXITCODE 42
117 
118 #ifndef NO_FORKING
119 
120 static enum outcome
121 testcase_run_forked_(const struct testgroup_t *group,
122 		     const struct testcase_t *testcase)
123 {
124 #ifdef _WIN32
125 	/* Fork? On Win32?  How primitive!  We'll do what the smart kids do:
126 	   we'll invoke our own exe (whose name we recall from the command
127 	   line) with a command line that tells it to run just the test we
128 	   want, and this time without forking.
129 
130 	   (No, threads aren't an option.  The whole point of forking is to
131 	   share no state between tests.)
132 	 */
133 	int ok;
134 	char buffer[LONGEST_TEST_NAME+256];
135 	STARTUPINFOA si;
136 	PROCESS_INFORMATION info;
137 	DWORD exitcode;
138 
139 	if (!in_tinytest_main) {
140 		printf("\nERROR.  On Windows, testcase_run_forked_ must be"
141 		       " called from within tinytest_main.\n");
142 		abort();
143 	}
144 	if (opt_verbosity>0)
145 		printf("[forking] ");
146 
147 	snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s",
148 		 commandname, verbosity_flag, group->prefix, testcase->name);
149 
150 	memset(&si, 0, sizeof(si));
151 	memset(&info, 0, sizeof(info));
152 	si.cb = sizeof(si);
153 
154 	ok = CreateProcessA(commandname, buffer, NULL, NULL, 0,
155 			   0, NULL, NULL, &si, &info);
156 	if (!ok) {
157 		printf("CreateProcess failed!\n");
158 		return 0;
159 	}
160 	WaitForSingleObject(info.hProcess, INFINITE);
161 	GetExitCodeProcess(info.hProcess, &exitcode);
162 	CloseHandle(info.hProcess);
163 	CloseHandle(info.hThread);
164 	if (exitcode == 0)
165 		return OK;
166 	else if (exitcode == MAGIC_EXITCODE)
167 		return SKIP;
168 	else
169 		return FAIL;
170 #else
171 	int outcome_pipe[2];
172 	pid_t pid;
173 	(void)group;
174 
175 	if (pipe(outcome_pipe))
176 		perror("opening pipe");
177 
178 	if (opt_verbosity>0)
179 		printf("[forking] ");
180 	pid = fork();
181 #ifdef FORK_BREAKS_GCOV
182 	vproc_transaction_begin(0);
183 #endif
184 	if (!pid) {
185 		/* child. */
186 		int test_r, write_r;
187 		char b[1];
188 		close(outcome_pipe[0]);
189 		test_r = testcase_run_bare_(testcase);
190 		assert(0<=(int)test_r && (int)test_r<=2);
191 		b[0] = "NYS"[test_r];
192 		write_r = (int)write(outcome_pipe[1], b, 1);
193 		if (write_r != 1) {
194 			perror("write outcome to pipe");
195 			exit(1);
196 		}
197 		exit(0);
198 		return FAIL; /* unreachable */
199 	} else {
200 		/* parent */
201 		int status, r;
202 		char b[1];
203 		/* Close this now, so that if the other side closes it,
204 		 * our read fails. */
205 		close(outcome_pipe[1]);
206 		r = (int)read(outcome_pipe[0], b, 1);
207 		if (r == 0) {
208 			printf("[Lost connection!] ");
209 			return 0;
210 		} else if (r != 1) {
211 			perror("read outcome from pipe");
212 		}
213 		waitpid(pid, &status, 0);
214 		close(outcome_pipe[0]);
215 		return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
216 	}
217 #endif
218 }
219 
220 #endif /* !NO_FORKING */
221 
222 int
223 testcase_run_one(const struct testgroup_t *group,
224 		 const struct testcase_t *testcase)
225 {
226 	enum outcome outcome;
227 
228 	if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) {
229 		if (opt_verbosity>0)
230 			printf("%s%s: %s\n",
231 			   group->prefix, testcase->name,
232 			   (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED");
233 		++n_skipped;
234 		return SKIP;
235 	}
236 
237 	if (opt_verbosity>0 && !opt_forked) {
238 		printf("%s%s: ", group->prefix, testcase->name);
239 	} else {
240 		if (opt_verbosity==0) printf(".");
241 		cur_test_prefix = group->prefix;
242 		cur_test_name = testcase->name;
243 	}
244 
245 #ifndef NO_FORKING
246 	if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
247 		outcome = testcase_run_forked_(group, testcase);
248 	} else {
249 #else
250 	{
251 #endif
252 		outcome = testcase_run_bare_(testcase);
253 	}
254 
255 	if (outcome == OK) {
256 		++n_ok;
257 		if (opt_verbosity>0 && !opt_forked)
258 			puts(opt_verbosity==1?"OK":"");
259 	} else if (outcome == SKIP) {
260 		++n_skipped;
261 		if (opt_verbosity>0 && !opt_forked)
262 			puts("SKIPPED");
263 	} else {
264 		++n_bad;
265 		if (!opt_forked)
266 			printf("\n  [%s FAILED]\n", testcase->name);
267 	}
268 
269 	if (opt_forked) {
270 		exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
271 		return 1; /* unreachable */
272 	} else {
273 		return (int)outcome;
274 	}
275 }
276 
277 int
278 tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag)
279 {
280 	int i, j;
281 	size_t length = LONGEST_TEST_NAME;
282 	char fullname[LONGEST_TEST_NAME];
283 	int found=0;
284 	if (strstr(arg, ".."))
285 		length = strstr(arg,"..")-arg;
286 	for (i=0; groups[i].prefix; ++i) {
287 		for (j=0; groups[i].cases[j].name; ++j) {
288 			struct testcase_t *testcase = &groups[i].cases[j];
289 			snprintf(fullname, sizeof(fullname), "%s%s",
290 				 groups[i].prefix, testcase->name);
291 			if (!flag) { /* Hack! */
292 				printf("    %s", fullname);
293 				if (testcase->flags & TT_OFF_BY_DEFAULT)
294 					puts("   (Off by default)");
295 				else if (testcase->flags & TT_SKIP)
296 					puts("  (DISABLED)");
297 				else
298 					puts("");
299 			}
300 			if (!strncmp(fullname, arg, length)) {
301 				if (set)
302 					testcase->flags |= flag;
303 				else
304 					testcase->flags &= ~flag;
305 				++found;
306 			}
307 		}
308 	}
309 	return found;
310 }
311 
312 static void
313 usage(struct testgroup_t *groups, int list_groups)
314 {
315 	puts("Options are: [--verbose|--quiet|--terse] [--no-fork]");
316 	puts("  Specify tests by name, or using a prefix ending with '..'");
317 	puts("  To skip a test, prefix its name with a colon.");
318 	puts("  To enable a disabled test, prefix its name with a plus.");
319 	puts("  Use --list-tests for a list of tests.");
320 	if (list_groups) {
321 		puts("Known tests are:");
322 		tinytest_set_flag_(groups, "..", 1, 0);
323 	}
324 	exit(0);
325 }
326 
327 static int
328 process_test_alias(struct testgroup_t *groups, const char *test)
329 {
330 	int i, j, n, r;
331 	for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) {
332 		if (!strcmp(cfg_aliases[i].name, test)) {
333 			n = 0;
334 			for (j = 0; cfg_aliases[i].tests[j]; ++j) {
335 				r = process_test_option(groups, cfg_aliases[i].tests[j]);
336 				if (r<0)
337 					return -1;
338 				n += r;
339 			}
340 			return n;
341 		}
342 	}
343 	printf("No such test alias as @%s!",test);
344 	return -1;
345 }
346 
347 static int
348 process_test_option(struct testgroup_t *groups, const char *test)
349 {
350 	int flag = TT_ENABLED_;
351 	int n = 0;
352 	if (test[0] == '@') {
353 		return process_test_alias(groups, test + 1);
354 	} else if (test[0] == ':') {
355 		++test;
356 		flag = TT_SKIP;
357 	} else if (test[0] == '+') {
358 		++test;
359 		++n;
360 		if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) {
361 			printf("No such test as %s!\n", test);
362 			return -1;
363 		}
364 	} else {
365 		++n;
366 	}
367 	if (!tinytest_set_flag_(groups, test, 1, flag)) {
368 		printf("No such test as %s!\n", test);
369 		return -1;
370 	}
371 	return n;
372 }
373 
374 void
375 tinytest_set_aliases(const struct testlist_alias_t *aliases)
376 {
377 	cfg_aliases = aliases;
378 }
379 
380 int
381 tinytest_main(int c, const char **v, struct testgroup_t *groups)
382 {
383 	int i, j, n=0;
384 
385 #ifdef _WIN32
386 	const char *sp = strrchr(v[0], '.');
387 	const char *extension = "";
388 	if (!sp || stricmp(sp, ".exe"))
389 		extension = ".exe"; /* Add an exe so CreateProcess will work */
390 	snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension);
391 	commandname[MAX_PATH]='\0';
392 #endif
393 	for (i=1; i<c; ++i) {
394 		if (v[i][0] == '-') {
395 			if (!strcmp(v[i], "--RUNNING-FORKED")) {
396 				opt_forked = 1;
397 			} else if (!strcmp(v[i], "--no-fork")) {
398 				opt_nofork = 1;
399 			} else if (!strcmp(v[i], "--quiet")) {
400 				opt_verbosity = -1;
401 				verbosity_flag = "--quiet";
402 			} else if (!strcmp(v[i], "--verbose")) {
403 				opt_verbosity = 2;
404 				verbosity_flag = "--verbose";
405 			} else if (!strcmp(v[i], "--terse")) {
406 				opt_verbosity = 0;
407 				verbosity_flag = "--terse";
408 			} else if (!strcmp(v[i], "--help")) {
409 				usage(groups, 0);
410 			} else if (!strcmp(v[i], "--list-tests")) {
411 				usage(groups, 1);
412 			} else {
413 				printf("Unknown option %s.  Try --help\n",v[i]);
414 				return -1;
415 			}
416 		} else {
417 			int r = process_test_option(groups, v[i]);
418 			if (r<0)
419 				return -1;
420 			n += r;
421 		}
422 	}
423 	if (!n)
424 		tinytest_set_flag_(groups, "..", 1, TT_ENABLED_);
425 
426 #ifdef _IONBF
427 	setvbuf(stdout, NULL, _IONBF, 0);
428 #endif
429 
430 	++in_tinytest_main;
431 	for (i=0; groups[i].prefix; ++i)
432 		for (j=0; groups[i].cases[j].name; ++j)
433 			if (groups[i].cases[j].flags & TT_ENABLED_)
434 				testcase_run_one(&groups[i],
435 						 &groups[i].cases[j]);
436 
437 	--in_tinytest_main;
438 
439 	if (opt_verbosity==0)
440 		puts("");
441 
442 	if (n_bad)
443 		printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
444 		       n_bad+n_ok,n_skipped);
445 	else if (opt_verbosity >= 1)
446 		printf("%d tests ok.  (%d skipped)\n", n_ok, n_skipped);
447 
448 	return (n_bad == 0) ? 0 : 1;
449 }
450 
451 int
452 tinytest_get_verbosity_(void)
453 {
454 	return opt_verbosity;
455 }
456 
457 void
458 tinytest_set_test_failed_(void)
459 {
460 	if (opt_verbosity <= 0 && cur_test_name) {
461 		if (opt_verbosity==0) puts("");
462 		printf("%s%s: ", cur_test_prefix, cur_test_name);
463 		cur_test_name = NULL;
464 	}
465 	cur_test_outcome = 0;
466 }
467 
468 void
469 tinytest_set_test_skipped_(void)
470 {
471 	if (cur_test_outcome==OK)
472 		cur_test_outcome = SKIP;
473 }
474 
475 char *
476 tinytest_format_hex_(const void *val_, unsigned long len)
477 {
478 	const unsigned char *val = val_;
479 	char *result, *cp;
480 	size_t i;
481 
482 	if (!val)
483 		return strdup("null");
484 	if (!(result = malloc(len*2+1)))
485 		return strdup("<allocation failure>");
486 	cp = result;
487 	for (i=0;i<len;++i) {
488 		*cp++ = "0123456789ABCDEF"[val[i] >> 4];
489 		*cp++ = "0123456789ABCDEF"[val[i] & 0x0f];
490 	}
491 	*cp = 0;
492 	return result;
493 }
494