1 #include <tools/ccanlint/ccanlint.h>
2 #include <tools/tools.h>
3 #include <ccan/foreach/foreach.h>
4 #include <ccan/str/str.h>
5 #include <ccan/tal/str/str.h>
6 #include <ccan/cast/cast.h>
16 static const char *can_run(struct manifest *m)
18 struct list_head *list;
21 return "Safe mode enabled";
22 foreach_ptr(list, &m->examples, &m->mangled_examples)
23 if (!list_empty(list))
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)
35 if (input[0] == '\0' || fmt[0] == '\0')
36 return input[0] == fmt[0];
40 if (cisspace(fmt[0])) {
41 /* One format space can swallow many input spaces */
43 while (cisspace(input[0])) {
44 if (scan_forv(ctx, ++input, fmt+1, &ap)) {
49 } else if (fmt[0] != '%') {
50 if (toupper(input[0]) != toupper(fmt[0]))
53 ret = scan_forv(ctx, input+1, fmt+1, &ap);
55 char **p = va_arg(ap, char **);
59 assert(fmt[1] == 's');
60 for (len = 1; input[len-1]; len++) {
61 ret = scan_forv(ctx, input + len, fmt+2, &ap);
63 *p = tal_strndup(ctx, input, len);
73 static bool scan_for(const void *ctx, const char *input, const char *fmt, ...)
79 ret = scan_forv(ctx, input, fmt, &ap);
84 static char *find_expect(struct ccan_file *file,
85 char **lines, char **input,
86 bool *contains, bool *whitespace, bool *error,
92 for (; lines[*line]; (*line)++) {
93 char *p = lines[*line] + strspn(lines[*line], " \t");
94 if (!strstarts(p, "//"))
98 /* With or without input? */
99 if (strncasecmp(p, "given", strlen("given")) == 0) {
100 /* Must be of form <given "X"> */
101 if (!scan_for(file, p, "given \"%s\" %s", input, &p)) {
109 if (scan_for(file, p, "outputs \"%s\"", &expect)) {
115 if (scan_for(file, p, "output contains \"%s\"", &expect)) {
121 /* Whitespace-ignoring versions. */
122 if (scan_for(file, p, "outputs %s", &expect)) {
128 if (scan_for(file, p, "output contains %s", &expect)) {
134 /* Other malformed line? */
135 if (*input || !strncasecmp(p, "output", strlen("output"))) {
143 static char *unexpected(struct ccan_file *i, const char *input,
144 const char *expect, bool contains, bool whitespace)
149 unsigned int default_time = default_timeout_ms;
152 cmd = tal_fmt(i, "echo '%s' | %s %s",
153 input, i->compiled[COMPILE_NORMAL], input);
155 cmd = tal_fmt(i, "%s", i->compiled[COMPILE_NORMAL]);
157 output = run_with_timeout(i, cmd, &ok, &default_time);
159 return tal_fmt(i, "Exited with non-zero status\n");
162 while ((p = strstr(expect, "\\n")) != NULL) {
163 expect = tal_fmt(cmd, "%.*s\n%s", (int)(p - expect), expect,
168 /* Normalize to spaces. */
169 expect = tal_strjoin(cmd,
170 tal_strsplit(cmd, expect, " \n\t",
173 output = tal_strjoin(cmd,
174 tal_strsplit(cmd, output, " \n\t",
180 if (strstr(output, expect))
183 if (streq(output, expect))
190 static void run_examples(struct manifest *m,
191 unsigned int *timeleft, struct score *score)
194 struct list_head *list;
199 foreach_ptr(list, &m->examples, &m->mangled_examples) {
200 list_for_each(list, i, list) {
201 char **lines, *expect, *input, *output;
202 unsigned int linenum = 0;
203 bool contains, whitespace, error;
205 lines = get_ccan_file_lines(i);
207 for (expect = find_expect(i, lines, &input,
208 &contains, &whitespace, &error,
212 expect = find_expect(i, lines, &input,
213 &contains, &whitespace,
216 score_file_error(score, i, linenum+1,
217 "Unparsable test line '%s'",
223 if (i->compiled[COMPILE_NORMAL] == NULL)
227 output = unexpected(i, input, expect,
228 contains, whitespace);
233 score_file_error(score, i, linenum+1,
234 "output '%s' didn't %s '%s'\n",
236 contains ? "contain" : "match",
244 /* FIXME: Test with reduced features, valgrind! */
245 struct ccanlint examples_run = {
246 .key = "examples_run",
247 .name = "Module examples with expected output give that output",
248 .check = run_examples,
250 .needs = "examples_compile"
253 REGISTER_TEST(examples_run);