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