]> git.ipfire.org Git - thirdparty/bird.git/blob - test/birdtest.c
Tests: Add missing mockup function to tests
[thirdparty/bird.git] / test / birdtest.c
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 bt_suite_case_begin = bt_suite_begin = bt_begin;
121
122 return;
123
124 usage:
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");
133 exit(3);
134 }
135
136 static void
137 bt_dump_backtrace(void)
138 {
139 #ifdef HAVE_EXECINFO_H
140 void *buf[BACKTRACE_MAX_LINES];
141 char **pp_backtrace;
142 int lines, j;
143
144 if (!bt_verbose)
145 return;
146
147 lines = backtrace(buf, BACKTRACE_MAX_LINES);
148 bt_log("backtrace() returned %d addresses", lines);
149
150 pp_backtrace = backtrace_symbols(buf, lines);
151 if (pp_backtrace == NULL)
152 {
153 perror("backtrace_symbols");
154 exit(EXIT_FAILURE);
155 }
156
157 for (j = 0; j < lines; j++)
158 bt_log("%s", pp_backtrace[j]);
159
160 free(pp_backtrace);
161 #endif /* HAVE_EXECINFO_H */
162 }
163
164 static
165 int bt_run_test_fn(int (*fn)(const void *), const void *fn_arg, int timeout)
166 {
167 int result;
168 alarm(timeout);
169
170 result = fn(fn_arg);
171
172 if (!bt_suite_result)
173 result = 0;
174
175 return result;
176 }
177
178 static uint
179 get_num_terminal_cols(void)
180 {
181 struct winsize w = {};
182 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
183 uint cols = w.ws_col;
184 return (cols > 0 ? cols : 80);
185 }
186
187 /**
188 * bt_log_result - pretty print of test result
189 * @result: 1 or 0
190 * @fmt: a description message (could be long, over more lines)
191 * @argptr: variable argument list
192 *
193 * This function is used for pretty printing of test results on all verbose
194 * levels.
195 */
196 static void
197 bt_log_result(int result, u64 time, const char *fmt, va_list argptr)
198 {
199 static char msg_buf[BT_BUFFER_SIZE];
200 char *pos;
201
202 snprintf(msg_buf, sizeof(msg_buf), "%s%s%s %" PRIu64 ".%09" PRIu64 "s%s",
203 bt_filename,
204 bt_test_id ? ": " : "",
205 bt_test_id ? bt_test_id : "",
206 time / 1000000000,
207 time % 1000000000,
208 (fmt && strlen(fmt) > 0) ? ": " : "");
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 bt_suite_case_begin = bt_suite_begin;
343
344 if (!forked)
345 {
346 bt_suite_result = bt_run_test_fn(fn, fn_arg, timeout);
347 }
348 else
349 {
350 pid_t pid = fork();
351 bt_syscall(pid < 0, "fork");
352
353 if (pid == 0)
354 {
355 /* child of fork */
356 _exit(bt_run_test_fn(fn, fn_arg, timeout));
357 }
358
359 int s;
360 int rv = waitpid(pid, &s, 0);
361 bt_syscall(rv < 0, "waitpid");
362
363 if (WIFEXITED(s))
364 {
365 /* Normal exit */
366 bt_suite_result = WEXITSTATUS(s);
367 }
368 else if (WIFSIGNALED(s))
369 {
370 /* Stopped by signal */
371 bt_suite_result = 0;
372
373 int sn = WTERMSIG(s);
374 if (sn == SIGALRM)
375 {
376 bt_log("Timeout expired");
377 }
378 else if (sn == SIGSEGV)
379 {
380 bt_log("Segmentation fault");
381 bt_dump_backtrace();
382 }
383 else if (sn != SIGABRT)
384 bt_log("Signal %d received", sn);
385 }
386
387 if (WCOREDUMP(s) && bt_verbose)
388 bt_log("Core dumped");
389 }
390
391 if (!bt_suite_result)
392 bt_result = 0;
393
394 bt_log_suite_result(bt_suite_result, NULL);
395 bt_test_id = NULL;
396
397 return bt_suite_result;
398 }
399
400 int
401 bt_exit_value(void)
402 {
403 if (!list_tests || (list_tests && !bt_result))
404 bt_log_overall_result(bt_result, "");
405 return bt_result ? EXIT_SUCCESS : EXIT_FAILURE;
406 }
407
408 /**
409 * bt_assert_batch__ - test a batch of inputs/outputs tests
410 * @opts: includes all necessary data
411 *
412 * Should be called using macro bt_assert_batch().
413 * Returns 1 or 0.
414 */
415 int
416 bt_assert_batch__(struct bt_batch *opts)
417 {
418 int i;
419 for (i = 0; i < opts->ndata; i++)
420 {
421 if (bt_verbose >= BT_VERBOSE_SUITE)
422 clock_gettime(CLOCK_MONOTONIC, &bt_suite_case_begin);
423
424 int bt_suit_case_result = opts->test_fn(opts->out_buf, opts->data[i].in, opts->data[i].out);
425
426 if (bt_suit_case_result == 0)
427 bt_suite_result = 0;
428
429 char b[BT_BUFFER_SIZE];
430 snprintf(b, sizeof(b), "%s(", opts->test_fn_name);
431
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);
435
436 if (bt_suit_case_result == 0)
437 {
438 sprintf_concat(b, ", but expecting is ");
439 opts->out_fmt(b+strlen(b), sizeof(b)-strlen(b), opts->data[i].out);
440 }
441
442 bt_log_suite_case_result(bt_suit_case_result, "%s", b);
443 }
444
445 return bt_suite_result;
446 }
447
448 /**
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
453 *
454 * This function can be used with bt_assert_batch() function.
455 * Input @data should be const char * string.
456 */
457 void
458 bt_fmt_str(char *buf, size_t size, const void *data)
459 {
460 const byte *s = data;
461
462 snprintf(buf, size, "\"");
463 while (*s)
464 {
465 snprintf(buf+strlen(buf), size-strlen(buf), bt_is_char(*s) ? "%c" : "\\%03u", *s);
466 s++;
467 }
468 snprintf(buf+strlen(buf), size-strlen(buf), "\"");
469 }
470
471 /**
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
476 *
477 * This function can be used with bt_assert_batch() function.
478 */
479 void
480 bt_fmt_unsigned(char *buf, size_t size, const void *data)
481 {
482 const uint *n = data;
483 snprintf(buf, size, "0x%x (%u)", *n, *n);
484 }
485
486 /**
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 *
491 *
492 * This function can be used with bt_assert_batch() function.
493 */
494 void
495 bt_fmt_ipa(char *buf, size_t size, const void *data)
496 {
497 const ip_addr *ip = data;
498 if (data)
499 bsnprintf(buf, size, "%I", *ip);
500 else
501 bsnprintf(buf, size, "(null)");
502 }
503
504 int
505 bt_is_char(byte c)
506 {
507 return (c >= (byte) 32 && c <= (byte) 126);
508 }
509
510 /*
511 * Mock-ups of all necessary public functions in main.c
512 */
513
514 int parse_and_exit;
515 char *bird_name;
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) {}
528
529 #include "nest/bird.h"
530 #include "lib/net.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) {}
535
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;