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