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