Commit 05b39fae authored by Rusty Russell's avatar Rusty Russell

ccanlint: allow per-file restrictions, so we don't have to remove all tests.

For example, valgrind can't handle some things, so we may need to
switch it off, but we don't want to do that for every test.

So, start a standard format for per-file restrictions, eg:

    Ccanlint: tests_pass_valgrind test/foo.c:FAIL

For the moment, only tests_pass_valgrind takes options, so it has to
change: we now have a helper which returns the array of options
applicable to a given file.
parent 05ab011a
......@@ -186,6 +186,8 @@ static bool run_test(struct ccanlint *i,
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;
}
/**
......@@ -409,11 +411,30 @@ static char **collapse(char **lines, unsigned int *nump)
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;
......@@ -425,9 +446,10 @@ static void add_info_options(struct ccan_file *info, bool mark_fails)
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)
......@@ -454,14 +476,49 @@ static void add_info_options(struct ccan_file *info, bool mark_fails)
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;
......
......@@ -95,7 +95,7 @@ struct ccanlint {
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;
......@@ -221,6 +221,9 @@ char *get_symbol_token(void *ctx, const char **line);
/* 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, ...);
......
......@@ -133,6 +133,19 @@ static char *analyze_output(const char *output, char **errs)
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,
......@@ -145,11 +158,19 @@ static void do_run_tests_vg(struct manifest *m,
/* 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. */
......@@ -164,8 +185,7 @@ static void do_run_tests_vg(struct manifest *m,
" --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... */
......@@ -181,15 +201,13 @@ static void do_run_tests_vg(struct manifest *m,
} 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,
......@@ -226,13 +244,18 @@ static void run_under_debugger_vg(struct manifest *m, struct score *score)
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();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment