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