File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird2 / test / birdtest.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 21 16:03:56 2019 UTC (4 years, 8 months ago) by misho
Branches: bird2, MAIN
CVS tags: v2_0_7p0, HEAD
bird2 ver 2.0.7

    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>