Statistics
| Branch: | Revision:

iof-bird-daemon / test / birdtest.c @ 9b0a0ba9

History | View | Annotate | Download (10.9 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 = 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;