Statistics
| Branch: | Revision:

iof-bird / bird-2.0.1 / test / birdtest.c @ 6b3f1a54

History | View | Annotate | Download (10.7 KB)

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 = 1;
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)
164
    result = 0;
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: 1 or 0
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
  static char msg_buf[BT_BUFFER_SIZE];
191
  char *pos;
192

    
193
  snprintf(msg_buf, sizeof(msg_buf), "%s%s%s%s",
194
           bt_filename,
195
           bt_test_id ? ": " : "",
196
           bt_test_id ? bt_test_id : "",
197
           (fmt && strlen(fmt) > 0) ? ": " : "");
198
  pos = msg_buf + strlen(msg_buf);
199

    
200
  vsnprintf(pos, sizeof(msg_buf) - (pos - msg_buf), fmt, argptr);
201

    
202
  int chrs = 0;
203
  for (uint i = 0; i < strlen(msg_buf); i += get_num_terminal_cols())
204
  {
205
    if (i)
206
      printf("\n");
207
    char *stop = msg_buf + i + get_num_terminal_cols();
208
    char backup = *stop;
209
    *stop = 0;
210
    chrs = printf("%s", msg_buf + i);
211
    *stop = backup;
212
  }
213

    
214
  int offset = get_num_terminal_cols() - chrs - BT_PROMPT_OK_FAIL_STRLEN;
215
  if (offset < 0)
216
  {
217
    printf("\n");
218
    offset = get_num_terminal_cols() - BT_PROMPT_OK_FAIL_STRLEN;
219
  }
220

    
221
  for (int i = 0; i < offset; i++)
222
    putchar(' ');
223

    
224
  const char *result_str = is_terminal ? BT_PROMPT_OK : BT_PROMPT_OK_NO_COLOR;
225
  if (!result)
226
    result_str = is_terminal ? BT_PROMPT_FAIL : BT_PROMPT_FAIL_NO_COLOR;
227

    
228
  printf("%s\n", result_str);
229
}
230

    
231
/**
232
 * bt_log_overall_result - pretty print of suite case result
233
 * @result: 1 or 0
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
250
 * @result: 1 or 0
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
{
259
  if(bt_verbose >= BT_VERBOSE_SUITE || !result)
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
270
 * @result: 1 or 0
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");
299
    return 1;
300
  }
301

    
302
  if (no_fork)
303
    forked = 0;
304

    
305
  if (no_timeout)
306
    timeout = 0;
307

    
308
  if (request && strcmp(id, request))
309
    return 1;
310

    
311
  bt_suite_result = 1;
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 */
344
      bt_suite_result = 0;
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

    
364
  if (!bt_suite_result)
365
    bt_result = 0;
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
{
376
  if (!list_tests || (list_tests && !bt_result))
377
    bt_log_overall_result(bt_result, "");
378
  return bt_result ? EXIT_SUCCESS : EXIT_FAILURE;
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().
386
 * Returns 1 or 0.
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

    
396
    if (bt_suit_case_result == 0)
397
      bt_suite_result = 0;
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

    
406
    if (bt_suit_case_result == 0)
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) {}
489
void cmd_shutdown(void) {}
490
void cmd_reconfig_undo_notify(void) {}
491

    
492
#include "nest/bird.h"
493
#include "lib/net.h"
494
#include "conf/conf.h"
495
void sysdep_preconfig(struct config *c UNUSED) {}
496
int sysdep_commit(struct config *new UNUSED, struct config *old UNUSED) { return 0; }
497
void sysdep_shutdown_done(void) {}
498

    
499
#include "nest/cli.h"
500
int cli_get_command(cli *c UNUSED) { return 0; }
501
void cli_write_trigger(cli *c UNUSED) {}
502
cli *cmd_reconfig_stored_cli;