Annotation of embedaddon/bird2/test/birdtest.c, revision 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>