summaryrefslogtreecommitdiff
path: root/tests/runner.c
blob: 71a5e2a3e93f51f400b550edbdeba30ffdf7317c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
   It is considered beerware. Prost. Skol. Cheers or whatever.

   $id$ */

/* System */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

/* Opentracker */
#include "test.h"

struct module {
  const char *name;
  void (*run)(void);
};

static const struct module modules[] = {{"ot_urlencoded", test_urlencoded},
                                        {"ot_iovec", test_iovec},
                                        {"ot_vector", test_vector},
                                        {"ot_random", test_random},
                                        {"ot_hex", test_hex},
                                        {"ot_scan", test_scan},
                                        {"ot_buf", test_buf},
                                        {"ot_ip", test_ip},
                                        {"ot_socket", test_socket},
                                        {"ot_mutex", test_mutex},
                                        {"ot_batch", test_batch},
                                        {"ot_fdstate", test_fdstate},
                                        {"ot_event", test_event},
                                        {"ot_fmt", test_fmt},
                                        {"ot_accesslist", test_accesslist},
                                        {NULL, NULL}};

struct tally {
  unsigned long checks;
  unsigned long failures;
};

/* Each module runs in a child of its own, so that a crash or a sanitizer
   abort costs that module's results rather than every module after it. The
   child reports its tally through a pipe; one that dies without reporting is
   counted as a failure under its own name. */
static void run_forked(const struct module *m) {
  struct tally tally = {0, 0};
  int          pipefd[2], status = 0;
  pid_t        child;
  ssize_t      got;

  if (pipe(pipefd)) {
    test_failf(__FILE__, __LINE__, "pipe failed, cannot isolate %s", m->name);
    return;
  }

  fflush(stdout); /* or the child inherits a copy and prints it again */
  child = fork();
  if (child < 0) {
    close(pipefd[0]);
    close(pipefd[1]);
    test_failf(__FILE__, __LINE__, "fork failed, cannot isolate %s", m->name);
    return;
  }

  if (!child) {
    close(pipefd[0]);
    test_checks = test_failures = 0;
    m->run();
    tally.checks   = test_checks;
    tally.failures = test_failures;
    if (write(pipefd[1], &tally, sizeof(tally)) != (ssize_t)sizeof(tally))
      _exit(1);
    fflush(stdout);
    _exit(0);
  }

  close(pipefd[1]);
  got = read(pipefd[0], &tally, sizeof(tally));
  close(pipefd[0]);
  while (waitpid(child, &status, 0) < 0)
    ;

  if (got == (ssize_t)sizeof(tally)) {
    test_checks   += tally.checks;
    test_failures += tally.failures;
  }
  if (got != (ssize_t)sizeof(tally) || !WIFEXITED(status) || WEXITSTATUS(status))
    test_failf(__FILE__, __LINE__, "%s did not run to the end", m->name);
}

/* Run every module, or only those whose name contains argv[1].
   OT_TEST_FORK=0 keeps everything in one process, which is what a debugger
   wants. */
int main(int argc, char **argv) {
  const char          *fork_setting = getenv("OT_TEST_FORK");
  int                  isolate      = !fork_setting || strcmp(fork_setting, "0");
  const struct module *m;

  for (m = modules; m->name; ++m) {
    if (argc > 1 && !strstr(m->name, argv[1]))
      continue;
    test_module(m->name);
    if (!isolate) {
      m->run();
      continue;
    }
    test_case("the module runs to the end");
    run_forked(m);
  }

  return test_summary();
}