static void register_test(struct list_head *h, struct ccanlint *test)
{
list_add(h, &test->list);
+ test->options = talloc_array(NULL, char *, 1);
+ test->options[0] = NULL;
}
/**
if (lines[i][0])
lines[j++] = lines[i];
}
+ lines[j] = NULL;
if (nump)
*nump = j;
return lines;
}
+
+static void add_options(struct ccanlint *test, char **options,
+ unsigned int num_options)
+{
+ unsigned int num;
+
+ if (!test->options)
+ num = 0;
+ else
+ /* -1, because last one is NULL. */
+ num = talloc_array_length(test->options) - 1;
+
+ test->options = talloc_realloc(NULL, test->options,
+ char *,
+ num + num_options + 1);
+ memcpy(&test->options[num], options, (num_options + 1)*sizeof(char *));
+}
+
static void add_info_options(struct ccan_file *info, bool mark_fails)
{
struct doc_section *d;
continue;
for (i = 0; i < d->num_lines; i++) {
+ unsigned int num_words;
char **words = collapse(strsplit(d, d->lines[i], " \t"),
- NULL);
- if (!words[0])
+ &num_words);
+ if (num_words == 0)
continue;
if (strncmp(words[0], "//", 2) == 0)
if (!test->takes_options)
warnx("%s: %s doesn't take options",
info->fullname, words[0]);
- /* Copy line exactly into options. */
- test->options = strstr(d->lines[i], words[0])
- + strlen(words[0]);
+ add_options(test, words+1, num_words-1);
}
}
}
}
+/* If options are of form "filename:<option>" they only apply to that file */
+char **per_file_options(const struct ccanlint *test, struct ccan_file *f)
+{
+ char **ret;
+ unsigned int i, j = 0;
+
+ /* Fast path. */
+ if (!test->options[0])
+ return test->options;
+
+ ret = talloc_array(f, char *, talloc_array_length(test->options));
+ for (i = 0; test->options[i]; i++) {
+ char *optname;
+
+ if (!test->options[i] || !strchr(test->options[i], ':')) {
+ optname = test->options[i];
+ } else if (strstarts(test->options[i], f->name)
+ && test->options[i][strlen(f->name)] == ':') {
+ optname = test->options[i] + strlen(f->name) + 1;
+ } else
+ continue;
+
+ /* FAIL overrides anything else. */
+ if (streq(optname, "FAIL")) {
+ ret = talloc_array(f, char *, 2);
+ ret[0] = (char *)"FAIL";
+ ret[1] = NULL;
+ return ret;
+ }
+ ret[j++] = optname;
+ }
+ ret[j] = NULL;
+
+ /* Shrink it to size so talloc_array_length() works as expected. */
+ return talloc_realloc(NULL, ret, char *, j + 1);
+}
+
static bool depends_on(struct ccanlint *i, struct ccanlint *target)
{
const struct dependent *d;
void (*handle)(struct manifest *m, struct score *score);
/* Options from _info. */
- char *options;
+ char **options;
/* If not set, we'll give an error if they try to set options. */
bool takes_options;
/* Similarly for ->doc_sections */
struct list_head *get_ccan_file_docs(struct ccan_file *f);
+/* Get NULL-terminated array options for this file for this test */
+char **per_file_options(const struct ccanlint *test, struct ccan_file *f);
+
/* Append message about this file (and line, if non-zero) to the score->error */
void score_file_error(struct score *, struct ccan_file *f, unsigned line,
const char *errorfmt, ...);
return leaks;
}
+static const char *concat(struct score *score, char *bits[])
+{
+ unsigned int i;
+ char *ret = talloc_strdup(score, "");
+
+ for (i = 0; bits[i]; i++) {
+ if (i)
+ ret = talloc_append_string(ret, " ");
+ ret = talloc_append_string(ret, bits[i]);
+ }
+ return ret;
+}
+
/* FIXME: Run examples, too! */
static void do_run_tests_vg(struct manifest *m,
bool keep,
/* This is slow, so we run once but grab leak info. */
score->total = 0;
+ score->pass = true;
foreach_ptr(list, &m->run_tests, &m->api_tests) {
list_for_each(list, i, list) {
char *output, *err, *log;
bool pass;
+ const char *options;
+
score->total++;
+ options = concat(score,
+ per_file_options(&tests_pass_valgrind,
+ i));
+ if (streq(options, "FAIL"))
+ continue;
/* FIXME: Valgrind's output sucks. XML is unreadable by
* humans *and* doesn't support children reporting. */
" --leak-check=full"
" --log-fd=3 %s %s"
" 3> %s",
- tests_pass_valgrind.options ?
- tests_pass_valgrind.options : "",
+ options,
i->compiled, log);
output = grab_file(i, log, NULL);
/* No valgrind errors? Expect it to pass... */
} else {
i->leak_info = analyze_output(output, &err);
}
- if (err)
+ if (err) {
score_file_error(score, i, 0, "%s", err);
- else
+ score->pass = false;
+ } else
score->score++;
}
}
-
- if (score->score == score->total)
- score->pass = true;
}
static void do_leakcheck_vg(struct manifest *m,
struct file_error *first;
char *command;
+ /* Don't ask anything if they suppressed tests. */
+ if (score->pass)
+ return;
+
if (!ask("Should I run the first failing test under the debugger?"))
return;
first = list_top(&score->per_file_errors, struct file_error, list);
command = talloc_asprintf(m, "valgrind --leak-check=full --db-attach=yes%s %s",
- tests_pass_valgrind.options ?
- tests_pass_valgrind.options : "",
+ concat(score,
+ per_file_options(&tests_pass_valgrind,
+ first->file)),
first->file->compiled);
if (system(command))
doesnt_matter();