2 * BIRD -- Unit Test Framework (BIRD Test)
4 * Can be freely distributed and used under the terms of the GNU GPL.
17 #include <sys/ioctl.h>
18 #include <sys/resource.h>
21 #include "test/birdtest.h"
22 #include "lib/string.h"
24 #ifdef HAVE_EXECINFO_H
28 #define BACKTRACE_MAX_LINES 100
30 #define sprintf_concat(s, format, ...) \
31 snprintf(s + strlen(s), sizeof(s) - strlen(s), format, ##__VA_ARGS__)
33 static const char *request
;
34 static int list_tests
;
38 static int no_timeout
;
39 static int is_terminal
; /* Whether stdout is a live terminal or pipe redirect */
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
;
47 const char *bt_filename
;
48 const char *bt_test_id
;
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 */
54 struct timespec bt_begin
, bt_suite_begin
, bt_suite_case_begin
;
56 u64 bt_random_state
[] = {
57 0x80241f302bd4d95d, 0xd10ba2e910f772b, 0xea188c9046f507c5, 0x4c4c581f04e6da05,
58 0x53d9772877c1b647, 0xab8ce3eb466de6c5, 0xad02844c8a8e865f, 0xe8cc78080295065d
62 bt_init(int argc
, char *argv
[])
66 initstate(BT_RANDOM_SEED
, (char *) bt_random_state
, sizeof(bt_random_state
));
69 bt_filename
= argv
[0];
72 is_terminal
= isatty(fileno(stdout
));
74 while ((c
= getopt(argc
, argv
, "lcdftv")) >= 0)
105 /* Optional requested test_id */
106 if ((optind
+ 1) == argc
)
107 request
= argv
[optind
++];
114 struct rlimit rl
= {RLIM_INFINITY
, RLIM_INFINITY
};
115 int rv
= setrlimit(RLIMIT_CORE
, &rl
);
116 bt_syscall(rv
< 0, "setrlimit RLIMIT_CORE");
119 clock_gettime(CLOCK_MONOTONIC
, &bt_begin
);
120 bt_suite_case_begin
= bt_suite_begin
= bt_begin
;
125 printf("Usage: %s [-l] [-c] [-d] [-f] [-t] [-vvv] [<test_suit_name>]\n", argv
[0]);
126 printf("Options: \n");
127 printf(" -l List all test suite names and descriptions \n");
128 printf(" -c Force unlimit core dumps (needs root privileges) \n");
129 printf(" -d Die on first failed test case \n");
130 printf(" -f No forking \n");
131 printf(" -t No timeout limit \n");
132 printf(" -v More verbosity, maximum is 3 -vvv \n");
137 bt_dump_backtrace(void)
139 #ifdef HAVE_EXECINFO_H
140 void *buf
[BACKTRACE_MAX_LINES
];
147 lines
= backtrace(buf
, BACKTRACE_MAX_LINES
);
148 bt_log("backtrace() returned %d addresses", lines
);
150 pp_backtrace
= backtrace_symbols(buf
, lines
);
151 if (pp_backtrace
== NULL
)
153 perror("backtrace_symbols");
157 for (j
= 0; j
< lines
; j
++)
158 bt_log("%s", pp_backtrace
[j
]);
161 #endif /* HAVE_EXECINFO_H */
165 int bt_run_test_fn(int (*fn
)(const void *), const void *fn_arg
, int timeout
)
172 if (!bt_suite_result
)
179 get_num_terminal_cols(void)
181 struct winsize w
= {};
182 ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &w
);
183 uint cols
= w
.ws_col
;
184 return (cols
> 0 ? cols
: 80);
188 * bt_log_result - pretty print of test result
190 * @fmt: a description message (could be long, over more lines)
191 * @argptr: variable argument list
193 * This function is used for pretty printing of test results on all verbose
197 bt_log_result(int result
, u64 time
, const char *fmt
, va_list argptr
)
199 static char msg_buf
[BT_BUFFER_SIZE
];
202 snprintf(msg_buf
, sizeof(msg_buf
), "%s%s%s %" PRIu64
".%09" PRIu64
"s%s",
204 bt_test_id
? ": " : "",
205 bt_test_id
? bt_test_id
: "",
208 (fmt
&& strlen(fmt
) > 0) ? ": " : "");
209 pos
= msg_buf
+ strlen(msg_buf
);
212 vsnprintf(pos
, sizeof(msg_buf
) - (pos
- msg_buf
), fmt
, argptr
);
215 for (uint i
= 0; i
< strlen(msg_buf
); i
+= get_num_terminal_cols())
219 char *stop
= msg_buf
+ i
+ get_num_terminal_cols();
222 chrs
= printf("%s", msg_buf
+ i
);
226 int offset
= get_num_terminal_cols() - chrs
- BT_PROMPT_OK_FAIL_STRLEN
;
230 offset
= get_num_terminal_cols() - BT_PROMPT_OK_FAIL_STRLEN
;
233 for (int i
= 0; i
< offset
; i
++)
236 const char *result_str
= is_terminal
? BT_PROMPT_OK
: BT_PROMPT_OK_NO_COLOR
;
238 result_str
= is_terminal
? BT_PROMPT_FAIL
: BT_PROMPT_FAIL_NO_COLOR
;
240 printf("%s\n", result_str
);
242 if (do_die
&& !result
)
247 get_time_diff(struct timespec
*begin
)
250 clock_gettime(CLOCK_MONOTONIC
, &end
);
251 return (end
.tv_sec
- begin
->tv_sec
) * 1000000000ULL
252 + end
.tv_nsec
- begin
->tv_nsec
;
256 * bt_log_overall_result - pretty print of suite case result
258 * @fmt: a description message (could be long, over more lines)
259 * ...: variable argument list
261 * This function is used for pretty printing of test suite case result.
264 bt_log_overall_result(int result
, const char *fmt
, ...)
267 va_start(argptr
, fmt
);
268 bt_log_result(result
, get_time_diff(&bt_begin
), fmt
, argptr
);
273 * bt_log_suite_result - pretty print of suite case result
275 * @fmt: a description message (could be long, over more lines)
276 * ...: variable argument list
278 * This function is used for pretty printing of test suite case result.
281 bt_log_suite_result(int result
, const char *fmt
, ...)
283 if (bt_verbose
>= BT_VERBOSE_SUITE
|| !result
)
286 va_start(argptr
, fmt
);
287 bt_log_result(result
, get_time_diff(&bt_suite_begin
), fmt
, argptr
);
293 * bt_log_suite_case_result - pretty print of suite result
295 * @fmt: a description message (could be long, over more lines)
296 * ...: variable argument list
298 * This function is used for pretty printing of test suite result.
301 bt_log_suite_case_result(int result
, const char *fmt
, ...)
303 if(bt_verbose
>= BT_VERBOSE_SUITE_CASE
)
306 va_start(argptr
, fmt
);
307 bt_log_result(result
, get_time_diff(&bt_suite_case_begin
), fmt
, argptr
);
313 bt_test_suite_base(int (*fn
)(const void *), const char *id
, const void *fn_arg
, int forked
, int timeout
, const char *dsc
, ...)
317 printf("%28s - ", id
);
332 if (request
&& strcmp(id
, request
))
338 if (bt_verbose
>= BT_VERBOSE_ABSOLUTELY_ALL
)
341 clock_gettime(CLOCK_MONOTONIC
, &bt_suite_begin
);
342 bt_suite_case_begin
= bt_suite_begin
;
346 bt_suite_result
= bt_run_test_fn(fn
, fn_arg
, timeout
);
351 bt_syscall(pid
< 0, "fork");
356 _exit(bt_run_test_fn(fn
, fn_arg
, timeout
));
360 int rv
= waitpid(pid
, &s
, 0);
361 bt_syscall(rv
< 0, "waitpid");
366 bt_suite_result
= WEXITSTATUS(s
);
368 else if (WIFSIGNALED(s
))
370 /* Stopped by signal */
373 int sn
= WTERMSIG(s
);
376 bt_log("Timeout expired");
378 else if (sn
== SIGSEGV
)
380 bt_log("Segmentation fault");
383 else if (sn
!= SIGABRT
)
384 bt_log("Signal %d received", sn
);
387 if (WCOREDUMP(s
) && bt_verbose
)
388 bt_log("Core dumped");
391 if (!bt_suite_result
)
394 bt_log_suite_result(bt_suite_result
, NULL
);
397 return bt_suite_result
;
403 if (!list_tests
|| (list_tests
&& !bt_result
))
404 bt_log_overall_result(bt_result
, "");
405 return bt_result
? EXIT_SUCCESS
: EXIT_FAILURE
;
409 * bt_assert_batch__ - test a batch of inputs/outputs tests
410 * @opts: includes all necessary data
412 * Should be called using macro bt_assert_batch().
416 bt_assert_batch__(struct bt_batch
*opts
)
419 for (i
= 0; i
< opts
->ndata
; i
++)
421 if (bt_verbose
>= BT_VERBOSE_SUITE
)
422 clock_gettime(CLOCK_MONOTONIC
, &bt_suite_case_begin
);
424 int bt_suit_case_result
= opts
->test_fn(opts
->out_buf
, opts
->data
[i
].in
, opts
->data
[i
].out
);
426 if (bt_suit_case_result
== 0)
429 char b
[BT_BUFFER_SIZE
];
430 snprintf(b
, sizeof(b
), "%s(", opts
->test_fn_name
);
432 opts
->in_fmt(b
+strlen(b
), sizeof(b
)-strlen(b
), opts
->data
[i
].in
);
433 sprintf_concat(b
, ") gives ");
434 opts
->out_fmt(b
+strlen(b
), sizeof(b
)-strlen(b
), opts
->out_buf
);
436 if (bt_suit_case_result
== 0)
438 sprintf_concat(b
, ", but expecting is ");
439 opts
->out_fmt(b
+strlen(b
), sizeof(b
)-strlen(b
), opts
->data
[i
].out
);
442 bt_log_suite_case_result(bt_suit_case_result
, "%s", b
);
445 return bt_suite_result
;
449 * bt_fmt_str - formating string into output buffer
450 * @buf: buffer for write
451 * @size: empty size in @buf
452 * @data: null-byte terminated string
454 * This function can be used with bt_assert_batch() function.
455 * Input @data should be const char * string.
458 bt_fmt_str(char *buf
, size_t size
, const void *data
)
460 const byte
*s
= data
;
462 snprintf(buf
, size
, "\"");
465 snprintf(buf
+strlen(buf
), size
-strlen(buf
), bt_is_char(*s
) ? "%c" : "\\%03u", *s
);
468 snprintf(buf
+strlen(buf
), size
-strlen(buf
), "\"");
472 * bt_fmt_unsigned - formating unsigned int into output buffer
473 * @buf: buffer for write
474 * @size: empty size in @buf
475 * @data: unsigned number
477 * This function can be used with bt_assert_batch() function.
480 bt_fmt_unsigned(char *buf
, size_t size
, const void *data
)
482 const uint
*n
= data
;
483 snprintf(buf
, size
, "0x%x (%u)", *n
, *n
);
487 * bt_fmt_ipa - formating ip_addr into output buffer
488 * @buf: buffer for write
489 * @size: empty size in @buf
490 * @data: should be struct ip_addr *
492 * This function can be used with bt_assert_batch() function.
495 bt_fmt_ipa(char *buf
, size_t size
, const void *data
)
497 const ip_addr
*ip
= data
;
499 bsnprintf(buf
, size
, "%I", *ip
);
501 bsnprintf(buf
, size
, "(null)");
507 return (c
>= (byte
) 32 && c
<= (byte
) 126);
511 * Mock-ups of all necessary public functions in main.c
516 void async_config(void) {}
517 void async_dump(void) {}
518 void async_shutdown(void) {}
519 char *get_hostname(linpool
*lp UNUSED
) { return NULL
; }
520 void cmd_check_config(char *name UNUSED
) {}
521 void cmd_reconfig(char *name UNUSED
, int type UNUSED
, int timeout UNUSED
) {}
522 void cmd_reconfig_confirm(void) {}
523 void cmd_reconfig_undo(void) {}
524 void cmd_reconfig_status(void) {}
525 void cmd_graceful_restart(void) {}
526 void cmd_shutdown(void) {}
527 void cmd_reconfig_undo_notify(void) {}
529 #include "nest/bird.h"
531 #include "conf/conf.h"
532 void sysdep_preconfig(struct config
*c UNUSED
) {}
533 int sysdep_commit(struct config
*new UNUSED
, struct config
*old UNUSED
) { return 0; }
534 void sysdep_shutdown_done(void) {}
536 #include "nest/cli.h"
537 int cli_get_command(cli
*c UNUSED
) { return 0; }
538 void cli_write_trigger(cli
*c UNUSED
) {}
539 cli
*cmd_reconfig_stored_cli
;