tools/ccanlint: add global .valgrind_suppressions file.
[ccan] / tools / ccanlint / tests / tests_pass.c
1 #include <tools/ccanlint/ccanlint.h>
2 #include <tools/tools.h>
3 #include <ccan/take/take.h>
4 #include <ccan/str/str.h>
5 #include <ccan/foreach/foreach.h>
6 #include <ccan/tal/path/path.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <limits.h>
12 #include <errno.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <err.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include "tests_pass.h"
19
20 bool do_valgrind = false;
21 const char *valgrind_suppress = "";
22
23 static const char *can_run(struct manifest *m)
24 {
25         unsigned int timeleft = default_timeout_ms;
26         char *output;
27         if (safe_mode)
28                 return "Safe mode enabled";
29
30         if (!is_excluded("tests_pass_valgrind")
31             && run_command(m, &timeleft, &output,
32                            "valgrind -q true")) {
33                 const char *sfile;
34
35                 do_valgrind = true;
36
37                 /* Check for suppressions file for all of CCAN. */
38                 sfile = path_join(m, ccan_dir, ".valgrind_suppressions");
39                 if (path_is_file(sfile))
40                         valgrind_suppress = tal_fmt(m, "--suppressions=%s",
41                                                     sfile);
42         }
43
44         return NULL;
45 }
46
47 static const char *concat(struct score *score, char *bits[])
48 {
49         unsigned int i;
50         char *ret = tal_strdup(score, "");
51
52         for (i = 0; bits[i]; i++) {
53                 if (i)
54                         ret = tal_strcat(score, take(ret), " ");
55                 ret = tal_strcat(score, take(ret), bits[i]);
56         }
57         return ret;
58 }
59
60 static void run_test(void *ctx,
61                      struct manifest *m,
62                      unsigned int *timeleft,
63                      struct ccan_file *i)
64 {
65         if (do_valgrind) {
66                 const char *options;
67                 options = concat(ctx,
68                                  per_file_options(&tests_pass_valgrind, i));
69
70                 if (!streq(options, "FAIL")) {
71                         /* FIXME: Valgrind's output sucks.  XML is
72                          * unreadable by humans *and* doesn't support
73                          * children reporting. */
74                         i->valgrind_log = tal_fmt(m,
75                                           "%s.valgrind-log",
76                                           i->compiled[COMPILE_NORMAL]);
77
78                         run_command_async(i, *timeleft,
79                                           "valgrind -q"
80                                           " --leak-check=full"
81                                           " --log-fd=3 %s %s %s"
82                                           " 3> %s",
83                                           valgrind_suppress, options,
84                                           i->compiled[COMPILE_NORMAL],
85                                           i->valgrind_log);
86                         return;
87                 }
88         }
89
90         run_command_async(i, *timeleft, "%s",
91                           i->compiled[COMPILE_NORMAL]);
92 }
93
94 static void do_run_tests(struct manifest *m,
95                          unsigned int *timeleft,
96                          struct score *score)
97 {
98         struct list_head *list;
99         struct ccan_file *i;
100         char *cmdout;
101         bool ok;
102
103         score->total = 0;
104         foreach_ptr(list, &m->run_tests, &m->api_tests) {
105                 list_for_each(list, i, list) {
106                         score->total++;
107                         if (verbose >= 2)
108                                 printf("   %s...\n", i->name);
109                         run_test(score, m, timeleft, i);
110                 }
111         }
112
113         while ((i = collect_command(&ok, &cmdout)) != NULL) {
114                 if (!ok)
115                         score_file_error(score, i, 0, "%s", cmdout);
116                 else
117                         score->score++;
118                 if (verbose >= 2)
119                         printf("   ...%s\n", i->name);
120         }
121
122         if (score->score == score->total)
123                 score->pass = true;
124 }
125
126
127 /* Gcc's warn_unused_result is fascist bullshit. */
128 #define doesnt_matter()
129
130 static void run_under_debugger(struct manifest *m, struct score *score)
131 {
132         char *command;
133         struct file_error *first;
134
135         first = list_top(&score->per_file_errors, struct file_error, list);
136
137         if (!ask("Should I run the first failing test under the debugger?"))
138                 return;
139
140         command = tal_fmt(m, "gdb -ex 'break tap.c:139' -ex 'run' %s",
141                           first->file->compiled[COMPILE_NORMAL]);
142         if (system(command))
143                 doesnt_matter();
144 }
145
146 struct ccanlint tests_pass = {
147         .key = "tests_pass",
148         .name = "Module's run and api tests pass",
149         .check = do_run_tests,
150         .handle = run_under_debugger,
151         .can_run = can_run,
152         .needs = "tests_compile"
153 };
154
155 REGISTER_TEST(tests_pass);