Annotation of embedaddon/bird2/test/birdtest.c, revision 1.1.1.1
1.1 misho 1: /*
2: * BIRD -- Unit Test Framework (BIRD Test)
3: *
4: * Can be freely distributed and used under the terms of the GNU GPL.
5: */
6:
7: #include <inttypes.h>
8: #include <stdarg.h>
9: #include <stdint.h>
10: #include <stdio.h>
11: #include <stdlib.h>
12: #include <string.h>
13: #include <signal.h>
14: #include <time.h>
15: #include <unistd.h>
16:
17: #include <sys/ioctl.h>
18: #include <sys/resource.h>
19: #include <sys/wait.h>
20:
21: #include "test/birdtest.h"
22: #include "lib/string.h"
23:
24: #ifdef HAVE_EXECINFO_H
25: #include <execinfo.h>
26: #endif
27:
28: #define BACKTRACE_MAX_LINES 100
29:
30: #define sprintf_concat(s, format, ...) \
31: snprintf(s + strlen(s), sizeof(s) - strlen(s), format, ##__VA_ARGS__)
32:
33: static const char *request;
34: static int list_tests;
35: static int do_core;
36: static int do_die;
37: static int no_fork;
38: static int no_timeout;
39: static int is_terminal; /* Whether stdout is a live terminal or pipe redirect */
40:
41: volatile sig_atomic_t async_config_flag; /* Asynchronous reconfiguration/dump scheduled */
42: volatile sig_atomic_t async_dump_flag;
43: volatile sig_atomic_t async_shutdown_flag;
44:
45:
46: uint bt_verbose;
47: const char *bt_filename;
48: const char *bt_test_id;
49:
50: int bt_result; /* Overall program run result */
51: int bt_suite_result; /* One suit result */
52: char bt_out_fmt_buf[1024]; /* Temporary memory buffer for output of testing function */
53:
54: struct timespec bt_begin, bt_suite_begin, bt_suite_case_begin;
55:
56: u64 bt_random_state[] = {
57: 0x80241f302bd4d95d, 0xd10ba2e910f772b, 0xea188c9046f507c5, 0x4c4c581f04e6da05,
58: 0x53d9772877c1b647, 0xab8ce3eb466de6c5, 0xad02844c8a8e865f, 0xe8cc78080295065d
59: };
60:
61: void
62: bt_init(int argc, char *argv[])
63: {
64: int c;
65:
66: initstate(BT_RANDOM_SEED, (char *) bt_random_state, sizeof(bt_random_state));
67:
68: bt_verbose = 0;
69: bt_filename = argv[0];
70: bt_result = 1;
71: bt_test_id = NULL;
72: is_terminal = isatty(fileno(stdout));
73:
74: while ((c = getopt(argc, argv, "lcdftv")) >= 0)
75: switch (c)
76: {
77: case 'l':
78: list_tests = 1;
79: break;
80:
81: case 'c':
82: do_core = 1;
83: break;
84:
85: case 'd':
86: do_die = 1;
87: break;
88:
89: case 'f':
90: no_fork = 1;
91: break;
92:
93: case 't':
94: no_timeout = 1;
95: break;
96:
97: case 'v':
98: bt_verbose++;
99: break;
100:
101: default:
102: goto usage;
103: }
104:
105: /* Optional requested test_id */
106: if ((optind + 1) == argc)
107: request = argv[optind++];
108:
109: if (optind != argc)
110: goto usage;
111:
112: if (do_core)
113: {
114: struct rlimit rl = {RLIM_INFINITY, RLIM_INFINITY};
115: int rv = setrlimit(RLIMIT_CORE, &rl);
116: bt_syscall(rv < 0, "setrlimit RLIMIT_CORE");
117: }
118:
119: clock_gettime(CLOCK_MONOTONIC, &bt_begin);
120:
121: return;
122:
123: usage:
124: printf("Usage: %s [-l] [-c] [-d] [-f] [-t] [-vvv] [<test_suit_name>]\n", argv[0]);
125: printf("Options: \n");
126: printf(" -l List all test suite names and descriptions \n");
127: printf(" -c Force unlimit core dumps (needs root privileges) \n");
128: printf(" -d Die on first failed test case \n");
129: printf(" -f No forking \n");
130: printf(" -t No timeout limit \n");
131: printf(" -v More verbosity, maximum is 3 -vvv \n");
132: exit(3);
133: }
134:
135: static void
136: bt_dump_backtrace(void)
137: {
138: #ifdef HAVE_EXECINFO_H
139: void *buf[BACKTRACE_MAX_LINES];
140: char **pp_backtrace;
141: int lines, j;
142:
143: if (!bt_verbose)
144: return;
145:
146: lines = backtrace(buf, BACKTRACE_MAX_LINES);
147: bt_log("backtrace() returned %d addresses", lines);
148:
149: pp_backtrace = backtrace_symbols(buf, lines);
150: if (pp_backtrace == NULL)
151: {
152: perror("backtrace_symbols");
153: exit(EXIT_FAILURE);
154: }
155:
156: for (j = 0; j < lines; j++)
157: bt_log("%s", pp_backtrace[j]);
158:
159: free(pp_backtrace);
160: #endif /* HAVE_EXECINFO_H */
161: }
162:
163: static
164: int bt_run_test_fn(int (*fn)(const void *), const void *fn_arg, int timeout)
165: {
166: int result;
167: alarm(timeout);
168:
169: result = fn(fn_arg);
170:
171: if (!bt_suite_result)
172: result = 0;
173:
174: return result;
175: }
176:
177: static uint
178: get_num_terminal_cols(void)
179: {
180: struct winsize w = {};
181: ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
182: uint cols = w.ws_col;
183: return (cols > 0 ? cols : 80);
184: }
185:
186: /**
187: * bt_log_result - pretty print of test result
188: * @result: 1 or 0
189: * @fmt: a description message (could be long, over more lines)
190: * @argptr: variable argument list
191: *
192: * This function is used for pretty printing of test results on all verbose
193: * levels.
194: */
195: static void
196: bt_log_result(int result, u64 time, const char *fmt, va_list argptr)
197: {
198: static char msg_buf[BT_BUFFER_SIZE];
199: char *pos;
200:
201: snprintf(msg_buf, sizeof(msg_buf), "%s%s%s%s %" PRIu64 ".%09" PRIu64 "s",
202: bt_filename,
203: bt_test_id ? ": " : "",
204: bt_test_id ? bt_test_id : "",
205: (fmt && strlen(fmt) > 0) ? ": " : "",
206: time / 1000000000,
207: time % 1000000000
208: );
209: pos = msg_buf + strlen(msg_buf);
210:
211: if (fmt)
212: vsnprintf(pos, sizeof(msg_buf) - (pos - msg_buf), fmt, argptr);
213:
214: int chrs = 0;
215: for (uint i = 0; i < strlen(msg_buf); i += get_num_terminal_cols())
216: {
217: if (i)
218: printf("\n");
219: char *stop = msg_buf + i + get_num_terminal_cols();
220: char backup = *stop;
221: *stop = 0;
222: chrs = printf("%s", msg_buf + i);
223: *stop = backup;
224: }
225:
226: int offset = get_num_terminal_cols() - chrs - BT_PROMPT_OK_FAIL_STRLEN;
227: if (offset < 0)
228: {
229: printf("\n");
230: offset = get_num_terminal_cols() - BT_PROMPT_OK_FAIL_STRLEN;
231: }
232:
233: for (int i = 0; i < offset; i++)
234: putchar(' ');
235:
236: const char *result_str = is_terminal ? BT_PROMPT_OK : BT_PROMPT_OK_NO_COLOR;
237: if (!result)
238: result_str = is_terminal ? BT_PROMPT_FAIL : BT_PROMPT_FAIL_NO_COLOR;
239:
240: printf("%s\n", result_str);
241:
242: if (do_die && !result)
243: abort();
244: }
245:
246: static u64
247: get_time_diff(struct timespec *begin)
248: {
249: struct timespec end;
250: clock_gettime(CLOCK_MONOTONIC, &end);
251: return (end.tv_sec - begin->tv_sec) * 1000000000ULL
252: + end.tv_nsec - begin->tv_nsec;
253: }
254:
255: /**
256: * bt_log_overall_result - pretty print of suite case result
257: * @result: 1 or 0
258: * @fmt: a description message (could be long, over more lines)
259: * ...: variable argument list
260: *
261: * This function is used for pretty printing of test suite case result.
262: */
263: static void
264: bt_log_overall_result(int result, const char *fmt, ...)
265: {
266: va_list argptr;
267: va_start(argptr, fmt);
268: bt_log_result(result, get_time_diff(&bt_begin), fmt, argptr);
269: va_end(argptr);
270: }
271:
272: /**
273: * bt_log_suite_result - pretty print of suite case result
274: * @result: 1 or 0
275: * @fmt: a description message (could be long, over more lines)
276: * ...: variable argument list
277: *
278: * This function is used for pretty printing of test suite case result.
279: */
280: void
281: bt_log_suite_result(int result, const char *fmt, ...)
282: {
283: if (bt_verbose >= BT_VERBOSE_SUITE || !result)
284: {
285: va_list argptr;
286: va_start(argptr, fmt);
287: bt_log_result(result, get_time_diff(&bt_suite_begin), fmt, argptr);
288: va_end(argptr);
289: }
290: }
291:
292: /**
293: * bt_log_suite_case_result - pretty print of suite result
294: * @result: 1 or 0
295: * @fmt: a description message (could be long, over more lines)
296: * ...: variable argument list
297: *
298: * This function is used for pretty printing of test suite result.
299: */
300: void
301: bt_log_suite_case_result(int result, const char *fmt, ...)
302: {
303: if(bt_verbose >= BT_VERBOSE_SUITE_CASE)
304: {
305: va_list argptr;
306: va_start(argptr, fmt);
307: bt_log_result(result, get_time_diff(&bt_suite_case_begin), fmt, argptr);
308: va_end(argptr);
309: }
310: }
311:
312: int
313: bt_test_suite_base(int (*fn)(const void *), const char *id, const void *fn_arg, int forked, int timeout, const char *dsc, ...)
314: {
315: if (list_tests)
316: {
317: printf("%28s - ", id);
318: va_list args;
319: va_start(args, dsc);
320: vprintf(dsc, args);
321: va_end(args);
322: printf("\n");
323: return 1;
324: }
325:
326: if (no_fork)
327: forked = 0;
328:
329: if (no_timeout)
330: timeout = 0;
331:
332: if (request && strcmp(id, request))
333: return 1;
334:
335: bt_suite_result = 1;
336: bt_test_id = id;
337:
338: if (bt_verbose >= BT_VERBOSE_ABSOLUTELY_ALL)
339: bt_log("Starting");
340:
341: clock_gettime(CLOCK_MONOTONIC, &bt_suite_begin);
342:
343: if (!forked)
344: {
345: bt_suite_result = bt_run_test_fn(fn, fn_arg, timeout);
346: }
347: else
348: {
349: pid_t pid = fork();
350: bt_syscall(pid < 0, "fork");
351:
352: if (pid == 0)
353: {
354: /* child of fork */
355: _exit(bt_run_test_fn(fn, fn_arg, timeout));
356: }
357:
358: int s;
359: int rv = waitpid(pid, &s, 0);
360: bt_syscall(rv < 0, "waitpid");
361:
362: if (WIFEXITED(s))
363: {
364: /* Normal exit */
365: bt_suite_result = WEXITSTATUS(s);
366: }
367: else if (WIFSIGNALED(s))
368: {
369: /* Stopped by signal */
370: bt_suite_result = 0;
371:
372: int sn = WTERMSIG(s);
373: if (sn == SIGALRM)
374: {
375: bt_log("Timeout expired");
376: }
377: else if (sn == SIGSEGV)
378: {
379: bt_log("Segmentation fault");
380: bt_dump_backtrace();
381: }
382: else if (sn != SIGABRT)
383: bt_log("Signal %d received", sn);
384: }
385:
386: if (WCOREDUMP(s) && bt_verbose)
387: bt_log("Core dumped");
388: }
389:
390: if (!bt_suite_result)
391: bt_result = 0;
392:
393: bt_log_suite_result(bt_suite_result, NULL);
394: bt_test_id = NULL;
395:
396: return bt_suite_result;
397: }
398:
399: int
400: bt_exit_value(void)
401: {
402: if (!list_tests || (list_tests && !bt_result))
403: bt_log_overall_result(bt_result, "");
404: return bt_result ? EXIT_SUCCESS : EXIT_FAILURE;
405: }
406:
407: /**
408: * bt_assert_batch__ - test a batch of inputs/outputs tests
409: * @opts: includes all necessary data
410: *
411: * Should be called using macro bt_assert_batch().
412: * Returns 1 or 0.
413: */
414: int
415: bt_assert_batch__(struct bt_batch *opts)
416: {
417: int i;
418: for (i = 0; i < opts->ndata; i++)
419: {
420: if (bt_verbose >= BT_VERBOSE_SUITE)
421: clock_gettime(CLOCK_MONOTONIC, &bt_suite_case_begin);
422:
423: int bt_suit_case_result = opts->test_fn(opts->out_buf, opts->data[i].in, opts->data[i].out);
424:
425: if (bt_suit_case_result == 0)
426: bt_suite_result = 0;
427:
428: char b[BT_BUFFER_SIZE];
429: snprintf(b, sizeof(b), "%s(", opts->test_fn_name);
430:
431: opts->in_fmt(b+strlen(b), sizeof(b)-strlen(b), opts->data[i].in);
432: sprintf_concat(b, ") gives ");
433: opts->out_fmt(b+strlen(b), sizeof(b)-strlen(b), opts->out_buf);
434:
435: if (bt_suit_case_result == 0)
436: {
437: sprintf_concat(b, ", but expecting is ");
438: opts->out_fmt(b+strlen(b), sizeof(b)-strlen(b), opts->data[i].out);
439: }
440:
441: bt_log_suite_case_result(bt_suit_case_result, "%s", b);
442: }
443:
444: return bt_suite_result;
445: }
446:
447: /**
448: * bt_fmt_str - formating string into output buffer
449: * @buf: buffer for write
450: * @size: empty size in @buf
451: * @data: null-byte terminated string
452: *
453: * This function can be used with bt_assert_batch() function.
454: * Input @data should be const char * string.
455: */
456: void
457: bt_fmt_str(char *buf, size_t size, const void *data)
458: {
459: const byte *s = data;
460:
461: snprintf(buf, size, "\"");
462: while (*s)
463: {
464: snprintf(buf+strlen(buf), size-strlen(buf), bt_is_char(*s) ? "%c" : "\\%03u", *s);
465: s++;
466: }
467: snprintf(buf+strlen(buf), size-strlen(buf), "\"");
468: }
469:
470: /**
471: * bt_fmt_unsigned - formating unsigned int into output buffer
472: * @buf: buffer for write
473: * @size: empty size in @buf
474: * @data: unsigned number
475: *
476: * This function can be used with bt_assert_batch() function.
477: */
478: void
479: bt_fmt_unsigned(char *buf, size_t size, const void *data)
480: {
481: const uint *n = data;
482: snprintf(buf, size, "0x%x (%u)", *n, *n);
483: }
484:
485: /**
486: * bt_fmt_ipa - formating ip_addr into output buffer
487: * @buf: buffer for write
488: * @size: empty size in @buf
489: * @data: should be struct ip_addr *
490: *
491: * This function can be used with bt_assert_batch() function.
492: */
493: void
494: bt_fmt_ipa(char *buf, size_t size, const void *data)
495: {
496: const ip_addr *ip = data;
497: bsnprintf(buf, size, "%I", *ip);
498: }
499:
500: int
501: bt_is_char(byte c)
502: {
503: return (c >= (byte) 32 && c <= (byte) 126);
504: }
505:
506: /*
507: * Mock-ups of all necessary public functions in main.c
508: */
509:
510: char *bird_name;
511: void async_config(void) {}
512: void async_dump(void) {}
513: void async_shutdown(void) {}
514: void cmd_check_config(char *name UNUSED) {}
515: void cmd_reconfig(char *name UNUSED, int type UNUSED, int timeout UNUSED) {}
516: void cmd_reconfig_confirm(void) {}
517: void cmd_reconfig_undo(void) {}
518: void cmd_reconfig_status(void) {}
519: void cmd_graceful_restart(void) {}
520: void cmd_shutdown(void) {}
521: void cmd_reconfig_undo_notify(void) {}
522:
523: #include "nest/bird.h"
524: #include "lib/net.h"
525: #include "conf/conf.h"
526: void sysdep_preconfig(struct config *c UNUSED) {}
527: int sysdep_commit(struct config *new UNUSED, struct config *old UNUSED) { return 0; }
528: void sysdep_shutdown_done(void) {}
529:
530: #include "nest/cli.h"
531: int cli_get_command(cli *c UNUSED) { return 0; }
532: void cli_write_trigger(cli *c UNUSED) {}
533: cli *cmd_reconfig_stored_cli;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>