tools: fix up warnings with -Wwrite-strings.
[ccan] / tools / ccanlint / tests / examples_run.c
1 #include <tools/ccanlint/ccanlint.h>
2 #include <tools/tools.h>
3 #include <ccan/talloc/talloc.h>
4 #include <ccan/foreach/foreach.h>
5 #include <ccan/str/str.h>
6 #include <ccan/cast/cast.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <stdint.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <ctype.h>
14 #include <assert.h>
15
16 static const char *can_run(struct manifest *m)
17 {
18         struct list_head *list;
19
20         if (safe_mode)
21                 return "Safe mode enabled";
22         foreach_ptr(list, &m->examples, &m->mangled_examples)
23                 if (!list_empty(list))
24                         return NULL;
25         return "No examples";
26 }
27
28 /* Very dumb scanner, allocates %s-strings. */
29 static bool scan_forv(const void *ctx,
30                       const char *input, const char *fmt, va_list *args)
31 {
32         va_list ap;
33         bool ret;
34
35         if (input[0] == '\0' || fmt[0] == '\0')
36                 return input[0] == fmt[0];
37
38         va_copy(ap, *args);
39
40         if (cisspace(fmt[0])) {
41                 /* One format space can swallow many input spaces */
42                 ret = false;
43                 while (cisspace(input[0])) {
44                         if (scan_forv(ctx, ++input, fmt+1, &ap)) {
45                                 ret = true;
46                                 break;
47                         }
48                 }
49         } else if (fmt[0] != '%') {
50                 if (toupper(input[0]) != toupper(fmt[0]))
51                         ret = false;
52                 else
53                         ret = scan_forv(ctx, input+1, fmt+1, &ap);
54         } else {
55                 char **p = va_arg(ap, char **);
56                 unsigned int len;
57
58                 ret = false;
59                 assert(fmt[1] == 's');
60                 for (len = 1; input[len-1]; len++) {
61                         ret = scan_forv(ctx, input + len, fmt+2, &ap);
62                         if (ret) {
63                                 *p = talloc_strndup(ctx, input, len);
64                                 ret = true;
65                                 break;
66                         }
67                 }
68         }
69         va_end(ap);
70         return ret;
71 }
72
73 static bool scan_for(const void *ctx, const char *input, const char *fmt, ...)
74 {
75         bool ret;
76         va_list ap;
77
78         va_start(ap, fmt);
79         ret = scan_forv(ctx, input, fmt, &ap);
80         va_end(ap);
81         return ret;
82 }
83
84 static char *find_expect(struct ccan_file *file,
85                          char **lines, char **input, bool *exact,
86                          unsigned *line)
87 {
88         char *expect;
89         const char *fmt;
90
91         for (; lines[*line]; (*line)++) {
92                 char *p = lines[*line] + strspn(lines[*line], " \t");
93                 if (!strstarts(p, "//"))
94                         continue;
95                 p += strspn(p, "/ ");
96                 foreach_ptr(fmt,
97                             "given '%s', outputs '%s'",
98                             "given '%s' outputs '%s'",
99                             "given \"%s\", outputs \"%s\"",
100                             "given \"%s\" outputs \"%s\"") {
101                         if (scan_for(file, p, fmt, input, &expect)) {
102                                 *exact = true;
103                                 return expect;
104                         }
105                 }
106
107                 foreach_ptr(fmt,
108                             "given '%s', output contains '%s'",
109                             "given '%s' output contains '%s'",
110                             "given \"%s\", output contains \"%s\"",
111                             "given \"%s\" output contains \"%s\"") {
112                         if (scan_for(file, p, fmt, input, &expect)) {
113                                 *exact = false;
114                                 return expect;
115                         }
116                 }
117
118                 foreach_ptr(fmt, "outputs '%s'", "outputs \"%s\"") {
119                         if (scan_for(file, p, fmt, &expect)) {
120                                 *input = cast_const(char *, "");
121                                 *exact = true;
122                                 return expect;
123                         }
124                 }
125
126                 foreach_ptr(fmt,
127                             "given '%s', output contains '%s'",
128                             "given '%s' output contains '%s'",
129                             "given \"%s\", output contains \"%s\"",
130                             "given \"%s\" output contains \"%s\"") {
131                         if (scan_for(file, p, fmt, input, &expect)) {
132                                 *exact = false;
133                                 return expect;
134                         }
135                 }
136
137                 /* Unquoted versions... we can get this wrong! */
138                 foreach_ptr(fmt,
139                             "given %s, outputs '%s'",
140                             "given '%s', outputs %s",
141                             "given %s, outputs \"%s\"",
142                             "given \"%s\", outputs %s",
143                             "given %s, outputs %s",
144                             "given %s outputs '%s'",
145                             "given '%s' outputs %s",
146                             "given %s outputs \"%s\"",
147                             "given \"%s\" outputs %s",
148                             "given %s outputs %s") {
149                         if (scan_for(file, p, fmt, input, &expect)) {
150                                 *exact = true;
151                                 return expect;
152                         }
153                 }
154
155                 foreach_ptr(fmt,
156                             "given %s, output contains '%s'",
157                             "given '%s', output contains %s",
158                             "given %s, output contains \"%s\"",
159                             "given \"%s\", output contains %s",
160                             "given %s, output contains %s",
161                             "given %s output contains '%s'",
162                             "given '%s' output contains %s",
163                             "given %s output contains \"%s\"",
164                             "given \"%s\" output contains %s",
165                             "given %s output contains %s") {
166                         if (scan_for(file, p, fmt, input, &expect)) {
167                                 *exact = false;
168                                 return expect;
169                         }
170                 }
171
172                 foreach_ptr(fmt,
173                             "outputs '%s'",
174                             "outputs \"%s\"",
175                             "outputs %s") {
176                         if (scan_for(file, p, fmt, &expect)) {
177                                 *input = cast_const(char *, "");
178                                 *exact = true;
179                                 return expect;
180                         }
181                 }
182
183                 foreach_ptr(fmt,
184                             "output contains '%s'",
185                             "output contains \"%s\"",
186                             "output contains %s") {
187                         if (scan_for(file, p, fmt, &expect)) {
188                                 *input = cast_const(char *, "");
189                                 *exact = false;
190                                 return expect;
191                         }
192                 }
193         }               
194         return NULL;
195 }
196
197 static char *trim(char *string)
198 {
199         while (strends(string, "\n"))
200                string[strlen(string)-1] = '\0';
201         return string;
202 }
203
204 static char *unexpected(struct ccan_file *i, const char *input,
205                         const char *expect, bool exact)
206 {
207         char *output, *cmd;
208         bool ok;
209         unsigned int default_time = default_timeout_ms;
210
211         cmd = talloc_asprintf(i, "echo '%s' | %s %s",
212                               input, i->compiled, input);
213
214         output = run_with_timeout(i, cmd, &ok, &default_time);
215         if (!ok)
216                 return talloc_asprintf(i, "Exited with non-zero status\n");
217
218         if (exact) {
219                 if (streq(output, expect) || streq(trim(output), expect))
220                         return NULL;
221         } else {
222                 if (strstr(output, expect))
223                         return NULL;
224         }
225         return output;
226 }
227
228 static void run_examples(struct manifest *m, bool keep,
229                          unsigned int *timeleft, struct score *score)
230 {
231         struct ccan_file *i;
232         struct list_head *list;
233
234         score->total = 0;
235         score->pass = true;
236
237         foreach_ptr(list, &m->examples, &m->mangled_examples) {
238                 list_for_each(list, i, list) {
239                         char **lines, *expect, *input, *output;
240                         unsigned int linenum = 0;
241                         bool exact;
242
243                         lines = get_ccan_file_lines(i);
244
245                         for (expect = find_expect(i, lines, &input, &exact,
246                                                   &linenum);
247                              expect;
248                              linenum++,
249                                      expect = find_expect(i, lines, &input,
250                                                           &exact, &linenum)) {
251                                 if (i->compiled == NULL)
252                                         continue;
253
254                                 score->total++;
255                                 output = unexpected(i, input, expect, exact);
256                                 if (!output) {
257                                         score->score++;
258                                         continue;
259                                 }
260                                 score_file_error(score, i, linenum+1,
261                                                  "output '%s' didn't %s '%s'\n",
262                                                  output,
263                                                  exact ? "match" : "contain",
264                                                  expect);
265                                 score->pass = false;
266                         }
267                 }
268         }
269 }
270
271 struct ccanlint examples_run = {
272         .key = "examples_run",
273         .name = "Module examples with expected output give that output",
274         .check = run_examples,
275         .can_run = can_run,
276         .needs = "examples_compile"
277 };
278
279 REGISTER_TEST(examples_run);