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>