]> git.ozlabs.org Git - ccan/commitdiff
ccanlint: rework so checks have more structure.
authorRusty Russell <rusty@rustcorp.com.au>
Tue, 9 Nov 2010 02:44:39 +0000 (13:14 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Tue, 9 Nov 2010 02:44:39 +0000 (13:14 +1030)
Previously each check returned a void *, but in fact most of them fell into
similar patterns.  So define 'struct score' and a helper to add files to it,
and use that.

Under these rules, you get 0/1 if you skip a test because a dependency failed
which in theory means your score (as a percentage) could drop if you fix
a test.

25 files changed:
tools/ccanlint/ccanlint.c
tools/ccanlint/ccanlint.h
tools/ccanlint/compulsory_tests/build.c
tools/ccanlint/compulsory_tests/build_objs.c
tools/ccanlint/compulsory_tests/check_build.c
tools/ccanlint/compulsory_tests/check_depends_built.c
tools/ccanlint/compulsory_tests/check_depends_exist.c
tools/ccanlint/compulsory_tests/check_includes_build.c
tools/ccanlint/compulsory_tests/has_info.c
tools/ccanlint/compulsory_tests/has_main_header.c
tools/ccanlint/file_analysis.c
tools/ccanlint/tests/build-coverage.c
tools/ccanlint/tests/compile_test_helpers.c
tools/ccanlint/tests/compile_tests.c
tools/ccanlint/tests/depends_accurate.c
tools/ccanlint/tests/examples_compile.c
tools/ccanlint/tests/examples_run.c
tools/ccanlint/tests/has_examples.c
tools/ccanlint/tests/has_info_documentation.c
tools/ccanlint/tests/has_tests.c
tools/ccanlint/tests/idempotent.c
tools/ccanlint/tests/run-coverage.c
tools/ccanlint/tests/run_tests.c
tools/ccanlint/tests/run_tests_valgrind.c
tools/ccanlint/tests/trailing_whitespace.c

index f2c79795a5c50a215b99a1a71fff61abcad64549..31406cccbfa0386a8cda1b3f451a094b11984150 100644 (file)
@@ -88,30 +88,37 @@ static const char *should_skip(struct manifest *m, struct ccanlint *i)
 
 static bool run_test(struct ccanlint *i,
                     bool quiet,
 
 static bool run_test(struct ccanlint *i,
                     bool quiet,
-                    unsigned int *score,
-                    unsigned int *total_score,
+                    unsigned int *running_score,
+                    unsigned int *running_total,
                     struct manifest *m)
 {
                     struct manifest *m)
 {
-       void *result;
-       unsigned int this_score, max_score, timeleft;
+       unsigned int timeleft;
        const struct dependent *d;
        const char *skip;
        const struct dependent *d;
        const char *skip;
-       bool bad, good;
+       struct score *score;
 
        //one less test to run through
        list_for_each(&i->dependencies, d, node)
                d->dependent->num_depends--;
 
 
        //one less test to run through
        list_for_each(&i->dependencies, d, node)
                d->dependent->num_depends--;
 
+       score = talloc(m, struct score);
+       list_head_init(&score->per_file_errors);
+       score->error = NULL;
+       score->pass = false;
+       score->score = 0;
+       score->total = 1;
+
        skip = should_skip(m, i);
 
        if (skip) {
        skip:
                if (verbose && !streq(skip, "not relevant to target"))
        skip = should_skip(m, i);
 
        if (skip) {
        skip:
                if (verbose && !streq(skip, "not relevant to target"))
-                       printf("  %s: skipped (%s)\n", i->name, skip);
+                       printf("%s: skipped (%s)\n", i->name, skip);
 
 
-               /* If we're skipping this because a prereq failed, we fail. */
+               /* If we're skipping this because a prereq failed, we fail:
+                * count it as a score of 1. */
                if (i->skip_fail)
                if (i->skip_fail)
-                       *total_score += i->total_score;
+                       (*running_total)++;
                        
                list_del(&i->list);
                list_add_tail(&finished_tests, &i->list);
                        
                list_del(&i->list);
                list_add_tail(&finished_tests, &i->list);
@@ -121,55 +128,52 @@ static bool run_test(struct ccanlint *i,
                        d->dependent->skip = "dependency was skipped";
                        d->dependent->skip_fail = i->skip_fail;
                }
                        d->dependent->skip = "dependency was skipped";
                        d->dependent->skip_fail = i->skip_fail;
                }
-               return true;
+               return i->skip_fail ? false : true;
        }
 
        timeleft = timeout ? timeout : default_timeout_ms;
        }
 
        timeleft = timeout ? timeout : default_timeout_ms;
-       result = i->check(m, i->keep_results, &timeleft);
+       i->check(m, i->keep_results, &timeleft, score);
        if (timeout && timeleft == 0) {
                skip = "timeout";
                goto skip;
        }
 
        if (timeout && timeleft == 0) {
                skip = "timeout";
                goto skip;
        }
 
-       max_score = i->total_score;
-       if (!max_score)
-               max_score = 1;
-
-       if (!result)
-               this_score = max_score;
-       else if (i->score)
-               this_score = i->score(m, result);
-       else
-               this_score = 0;
-
-       bad = (this_score == 0);
-       good = (this_score >= max_score);
-
-       if (verbose || (!good && !quiet)) {
-               printf("  %s: %s", i->name,
-                      bad ? "FAIL" : good ? "PASS" : "PARTIAL");
-               if (max_score > 1)
-                       printf(" (+%u/%u)", this_score, max_score);
+       assert(score->score <= score->total);
+       if ((!score->pass && !quiet)
+           || (score->score < score->total && verbose)
+           || verbose > 1) {
+               printf("%s: %s", i->name, score->pass ? "PASS" : "FAIL");
+               if (score->total > 1)
+                       printf(" (+%u/%u)", score->score, score->total);
                printf("\n");
        }
 
                printf("\n");
        }
 
-       if (!quiet && result) {
-               const char *desc;
-               if (i->describe && (desc = i->describe(m, result)) != NULL) 
-                       printf("    %s\n", desc);
+       if (!quiet && !score->pass) {
+               struct file_error *f;
+
+               if (score->error)
+                       printf("%s:\n", score->error);
+
+               list_for_each(&score->per_file_errors, f, list) {
+                       if (f->line)
+                               printf("%s:%u:%s\n",
+                                      f->file->fullname, f->line, f->error);
+                       else if (f->file)
+                               printf("%s:%s\n", f->file->fullname, f->error);
+                       else
+                               printf("%s\n", f->error);
+               }
                if (i->handle)
                if (i->handle)
-                       i->handle(m, result);
+                       i->handle(m, score);
        }
 
        }
 
-       if (i->total_score) {
-               *score += this_score;
-               *total_score += i->total_score;
-       }
+       *running_score += score->score;
+       *running_total += score->total;
 
        list_del(&i->list);
        list_add_tail(&finished_tests, &i->list);
 
 
        list_del(&i->list);
        list_add_tail(&finished_tests, &i->list);
 
-       if (bad) {
+       if (!score->pass) {
                /* Skip any tests which depend on this one. */
                list_for_each(&i->dependencies, d, node) {
                        if (d->dependent->skip)
                /* Skip any tests which depend on this one. */
                list_for_each(&i->dependencies, d, node) {
                        if (d->dependent->skip)
@@ -178,7 +182,7 @@ static bool run_test(struct ccanlint *i,
                        d->dependent->skip_fail = true;
                }
        }
                        d->dependent->skip_fail = true;
                }
        }
-       return good;
+       return score->pass;
 }
 
 static void register_test(struct list_head *h, struct ccanlint *test, ...)
 }
 
 static void register_test(struct list_head *h, struct ccanlint *test, ...)
@@ -451,9 +455,6 @@ int main(int argc, char *argv[])
        }
 
        /* If you don't pass the compulsory tests, you get a score of 0. */
        }
 
        /* If you don't pass the compulsory tests, you get a score of 0. */
-       if (verbose)
-               printf("Compulsory tests:\n");
-
        while ((i = get_next_test(&compulsory_tests)) != NULL) {
                if (!run_test(i, summary, &score, &total_score, m)) {
                        printf("%sTotal score: 0/%u\n", prefix, total_score);
        while ((i = get_next_test(&compulsory_tests)) != NULL) {
                if (!run_test(i, summary, &score, &total_score, m)) {
                        printf("%sTotal score: 0/%u\n", prefix, total_score);
@@ -462,9 +463,6 @@ int main(int argc, char *argv[])
        }
 
        add_info_fails(m->info_file);
        }
 
        add_info_fails(m->info_file);
-
-       if (verbose)
-               printf("\nNormal tests:\n");
        while ((i = get_next_test(&normal_tests)) != NULL)
                run_test(i, summary, &score, &total_score, m);
 
        while ((i = get_next_test(&normal_tests)) != NULL)
                run_test(i, summary, &score, &total_score, m);
 
index 8c2ee0e8826b5ff0ba974f888f879a6953721e8e..0e57140018ffee1693bbf3f1010dd622316e1335 100644 (file)
@@ -42,6 +42,20 @@ struct manifest {
 
 struct manifest *get_manifest(const void *ctx, const char *dir);
 
 
 struct manifest *get_manifest(const void *ctx, const char *dir);
 
+struct file_error {
+       struct list_node list;
+       struct ccan_file *file;
+       unsigned int line; /* 0 not to print */
+       const char *error;
+};
+
+struct score {
+       bool pass;
+       unsigned int score, total;
+       const char *error;
+       struct list_head per_file_errors;
+};
+
 struct ccanlint {
        struct list_node list;
 
 struct ccanlint {
        struct list_node list;
 
@@ -51,27 +65,18 @@ struct ccanlint {
        /* Unique name of test */
        const char *name;
 
        /* Unique name of test */
        const char *name;
 
-       /* Total score that this test is worth. */
-       unsigned int total_score;
-
        /* Can we run this test?  Return string explaining why, if not. */
        const char *(*can_run)(struct manifest *m);
 
        /* Can we run this test?  Return string explaining why, if not. */
        const char *(*can_run)(struct manifest *m);
 
-       /* If this returns non-NULL, it means the check failed.
-        * keep is set if you should keep the results.
-        * If timeleft is set to 0, means it timed out. */
-       void *(*check)(struct manifest *m, bool keep, unsigned int *timeleft);
-
-       /* The non-NULL return from check is passed to one of these: */
-
-       /* So, what did this get out of the total_score?  (NULL means 0). */
-       unsigned int (*score)(struct manifest *m, void *check_result);
-
-       /* Verbose description of what was wrong. */
-       const char *(*describe)(struct manifest *m, void *check_result);
+       /* keep is set if you should keep the results.
+        * If timeleft is set to 0, means it timed out.
+        * score is the result, and a talloc context freed after all our
+        * depends are done. */
+       void (*check)(struct manifest *m,
+                     bool keep, unsigned int *timeleft, struct score *score);
 
        /* Can we do something about it? (NULL if not) */
 
        /* Can we do something about it? (NULL if not) */
-       void (*handle)(struct manifest *m, void *check_result);
+       void (*handle)(struct manifest *m, struct score *score);
 
        /* Internal use fields: */
        /* Who depends on us? */
 
        /* Internal use fields: */
        /* Who depends on us? */
@@ -184,11 +189,9 @@ char *get_symbol_token(void *ctx, const char **line);
 struct list_head *get_ccan_file_docs(struct ccan_file *f);
 
 
 struct list_head *get_ccan_file_docs(struct ccan_file *f);
 
 
-/* Call the reporting on every line in the file.  sofar contains
- * previous results. */
-char *report_on_lines(struct list_head *files,
-                     char *(*report)(const char *),
-                     char *sofar);
+/* Add an error about this file (and line, if non-zero) to the score struct */
+void score_file_error(struct score *, struct ccan_file *f, unsigned line,
+                     const char *error);
 
 /* Normal tests. */
 extern struct ccanlint trailing_whitespace;
 
 /* Normal tests. */
 extern struct ccanlint trailing_whitespace;
index c78e500b238f4e0953e7084f46a36deba03c0f44..7c73f0d4f834c98519dc68ff18420cb935d4d6cc 100644 (file)
@@ -33,42 +33,41 @@ static char *obj_list(const struct manifest *m)
        return list;
 }
 
        return list;
 }
 
-static void *do_build(struct manifest *m,
-                     bool keep,
-                     unsigned int *timeleft)
+static void do_build(struct manifest *m,
+                    bool keep,
+                    unsigned int *timeleft,
+                    struct score *score)
 {
 {
-       char *filename, *err;
+       char *filename, *errstr;
 
        if (list_empty(&m->c_files)) {
                /* No files?  No score, but we "pass". */
 
        if (list_empty(&m->c_files)) {
                /* No files?  No score, but we "pass". */
-               build.total_score = 0;
-               return NULL;
+               score->total = 0;
+               score->pass = true;
+               return;
        }
        }
-       filename = link_objects(m, m->basename, false, obj_list(m), &err);
-       if (filename && keep) {
+
+       filename = link_objects(m, m->basename, false, obj_list(m), &errstr);
+       if (!filename) {
+               score->error = "The object file didn't build";
+               score_file_error(score, NULL, 0, errstr);
+               return;
+       }
+
+       if (keep) {
                char *realname = talloc_asprintf(m, "%s.o", m->dir);
                /* We leave this object file around, all built. */
                if (!move_file(filename, realname))
                char *realname = talloc_asprintf(m, "%s.o", m->dir);
                /* We leave this object file around, all built. */
                if (!move_file(filename, realname))
-                       return talloc_asprintf(m, "Failed to rename %s to %s",
-                                              filename, realname);
-               return NULL;
+                       err(1, "Renaming %s to %s", filename, realname);
        }
        }
-       return err;
-}
-
-static const char *describe_build(struct manifest *m, void *check_result)
-{
-       return talloc_asprintf(check_result, 
-                              "The object file for the module didn't build:\n"
-                              "%s", (char *)check_result);
+       score->pass = true;
+       score->score = score->total;
 }
 
 struct ccanlint build = {
        .key = "build",
        .name = "Module can be built from object files",
 }
 
 struct ccanlint build = {
        .key = "build",
        .name = "Module can be built from object files",
-       .total_score = 1,
        .check = do_build,
        .check = do_build,
-       .describe = describe_build,
        .can_run = can_build,
 };
 
        .can_run = can_build,
 };
 
index af7a57b73e8482c18f627abfe5d2e0e814222764..aa8e7b36a5ebce098d9967d359eb86147395cf8e 100644 (file)
@@ -21,42 +21,37 @@ static const char *can_build(struct manifest *m)
        return NULL;
 }
 
        return NULL;
 }
 
-static void *check_objs_build(struct manifest *m,
-                             bool keep, unsigned int *timeleft)
+static void check_objs_build(struct manifest *m,
+                            bool keep,
+                            unsigned int *timeleft, struct score *score)
 {
 {
-       char *report = NULL;
        struct ccan_file *i;
 
        struct ccan_file *i;
 
+       if (list_empty(&m->c_files))
+               score->total = 0;
+
        list_for_each(&m->c_files, i, list) {
                char *err;
                char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
 
        list_for_each(&m->c_files, i, list) {
                char *err;
                char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
 
-               /* One point for each obj file. */
-               build_objs.total_score++;
-
                i->compiled = maybe_temp_file(m, "", keep, fullfile);
                err = compile_object(m, fullfile, ccan_dir, "", i->compiled);
                if (err) {
                        talloc_free(i->compiled);
                i->compiled = maybe_temp_file(m, "", keep, fullfile);
                err = compile_object(m, fullfile, ccan_dir, "", i->compiled);
                if (err) {
                        talloc_free(i->compiled);
-                       if (report)
-                               report = talloc_append_string(report, err);
-                       else
-                               report = err;
+                       score->error = "Compiling object files";
+                       score_file_error(score, i, 0, err);
                }
        }
                }
        }
-       return report;
-}
-
-static const char *describe_objs_build(struct manifest *m, void *check_result)
-{
-       return check_result;
+       if (!score->error) {
+               score->pass = true;
+               score->score = score->total;
+       }
 }
 
 struct ccanlint build_objs = {
        .key = "build-objects",
        .name = "Module object files can be built",
        .check = check_objs_build,
 }
 
 struct ccanlint build_objs = {
        .key = "build-objects",
        .name = "Module object files can be built",
        .check = check_objs_build,
-       .describe = describe_objs_build,
        .can_run = can_build,
 };
 
        .can_run = can_build,
 };
 
index 42eda13ae56aede90e2fd9df85927e9a1260129c..9175b91835f5bb2271f6e645d9040038030c8663 100644 (file)
@@ -45,9 +45,9 @@ static char *lib_list(const struct manifest *m)
        return ret;
 }
 
        return ret;
 }
 
-static void *check_use_build(struct manifest *m,
-                            bool keep,
-                            unsigned int *timeleft)
+static void check_use_build(struct manifest *m,
+                           bool keep,
+                           unsigned int *timeleft, struct score *score)
 {
        char *contents;
        char *tmpfile;
 {
        char *contents;
        char *tmpfile;
@@ -58,8 +58,7 @@ static void *check_use_build(struct manifest *m,
 
        fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
        if (fd < 0)
 
        fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
        if (fd < 0)
-               return talloc_asprintf(m, "Creating temporary file: %s",
-                                      strerror(errno));
+               err(1, "Creating temporary file %s", tmpfile);
 
        contents = talloc_asprintf(tmpfile,
                                   "#include <ccan/%s/%s.h>\n"
 
        contents = talloc_asprintf(tmpfile,
                                   "#include <ccan/%s/%s.h>\n"
@@ -68,30 +67,23 @@ static void *check_use_build(struct manifest *m,
                                   "    return 0;\n"
                                   "}\n",
                                   m->basename, m->basename);
                                   "    return 0;\n"
                                   "}\n",
                                   m->basename, m->basename);
-       if (write(fd, contents, strlen(contents)) != strlen(contents)) {
-               close(fd);
-               return "Failure writing to temporary file";
-       }
+       if (write(fd, contents, strlen(contents)) != strlen(contents))
+               err(1, "Failure writing to temporary file %s", tmpfile);
        close(fd);
 
        close(fd);
 
-       return compile_and_link(m, tmpfile, ccan_dir, obj_list(m), "",
-                               lib_list(m),
-                               maybe_temp_file(m, "", keep, tmpfile));
-}
-
-static const char *describe_use_build(struct manifest *m, void *check_result)
-{
-       return talloc_asprintf(check_result, 
-                              "Linking against module:\n"
-                              "%s", (char *)check_result);
+       score->error = compile_and_link(m, tmpfile, ccan_dir, obj_list(m), "",
+                                       lib_list(m),
+                                       maybe_temp_file(m, "", keep, tmpfile));
+       if (!score->error) {
+               score->pass = true;
+               score->score = score->total;
+       }
 }
 
 struct ccanlint check_build = {
        .key = "check-link",
        .name = "Module can be linked against trivial program",
 }
 
 struct ccanlint check_build = {
        .key = "check-link",
        .name = "Module can be linked against trivial program",
-       .total_score = 1,
        .check = check_use_build,
        .check = check_use_build,
-       .describe = describe_use_build,
        .can_run = can_build,
 };
 
        .can_run = can_build,
 };
 
index 04ef54eb2c79a0060d5b0a43982653b4c671004a..41e5d8885e52aea5026d73d426c10eee9c592e99 100644 (file)
@@ -35,13 +35,12 @@ static bool expect_obj_file(const char *dir)
        return has_c_files;
 }
 
        return has_c_files;
 }
 
-static void *check_depends_built(struct manifest *m,
-                                bool keep,
-                                unsigned int *timeleft)
+static void check_depends_built(struct manifest *m,
+                               bool keep,
+                               unsigned int *timeleft, struct score *score)
 {
        struct ccan_file *i;
        struct stat st;
 {
        struct ccan_file *i;
        struct stat st;
-       char *report = NULL;
 
        list_for_each(&m->dep_dirs, i, list) {
                if (!expect_obj_file(i->fullname))
 
        list_for_each(&m->dep_dirs, i, list) {
                if (!expect_obj_file(i->fullname))
@@ -49,9 +48,11 @@ static void *check_depends_built(struct manifest *m,
 
                i->compiled = talloc_asprintf(i, "%s.o", i->fullname);
                if (stat(i->compiled, &st) != 0) {
 
                i->compiled = talloc_asprintf(i, "%s.o", i->fullname);
                if (stat(i->compiled, &st) != 0) {
-                       report = talloc_asprintf_append(report,
-                                                       "object file %s\n",
-                                                       i->compiled);
+                       score->error = "Dependencies are not built";
+                       score_file_error(score, i, 0,
+                                        talloc_asprintf(score,
+                                                       "object file %s",
+                                                        i->compiled));
                        i->compiled = NULL;
                }                       
        }
                        i->compiled = NULL;
                }                       
        }
@@ -61,30 +62,21 @@ static void *check_depends_built(struct manifest *m,
            && (!list_empty(&m->run_tests) || !list_empty(&m->api_tests))) {
                char *tapobj = talloc_asprintf(m, "%s/ccan/tap.o", ccan_dir);
                if (stat(tapobj, &st) != 0) {
            && (!list_empty(&m->run_tests) || !list_empty(&m->api_tests))) {
                char *tapobj = talloc_asprintf(m, "%s/ccan/tap.o", ccan_dir);
                if (stat(tapobj, &st) != 0) {
-                       report = talloc_asprintf_append(report,
-                                                       "object file %s"
-                                                       " (for tests)\n",
-                                                       tapobj);
+                       score->error = talloc_asprintf(score,
+                                              "tap object file not built");
                }
        }
 
                }
        }
 
-       return talloc_steal(m, report);
-}
-
-static const char *describe_depends_built(struct manifest *m,
-                                         void *check_result)
-{
-       return talloc_asprintf(check_result, 
-                              "The following dependencies are not built:\n"
-                              "%s", (char *)check_result);
+       if (!score->error) {
+               score->pass = true;
+               score->score = score->total;
+       }
 }
 
 struct ccanlint depends_built = {
        .key = "depends-built",
        .name = "Module's CCAN dependencies are already built",
 }
 
 struct ccanlint depends_built = {
        .key = "depends-built",
        .name = "Module's CCAN dependencies are already built",
-       .total_score = 1,
        .check = check_depends_built,
        .check = check_depends_built,
-       .describe = describe_depends_built,
        .can_run = can_build,
 };
 
        .can_run = can_build,
 };
 
index 7cdb49f5336a3426cd40adbb8441c191347232e0..e2437083e018d2dc52d70e64e09971b406b06e1b 100644 (file)
 #include <string.h>
 #include <ctype.h>
 
 #include <string.h>
 #include <ctype.h>
 
-static char *add_dep(char *sofar, struct manifest *m, const char *dep)
+static void add_dep(struct manifest *m, const char *dep, struct score *score)
 {
        struct stat st;
        struct ccan_file *f;
 
        f = new_ccan_file(m, ccan_dir, talloc_strdup(m, dep));
        if (stat(f->fullname, &st) != 0) {
 {
        struct stat st;
        struct ccan_file *f;
 
        f = new_ccan_file(m, ccan_dir, talloc_strdup(m, dep));
        if (stat(f->fullname, &st) != 0) {
-               return talloc_asprintf_append(sofar,
-                                             "ccan/%s: expected it in"
-                                             " directory %s\n",
-                                             dep, f->fullname);
-       }
-
-       list_add_tail(&m->dep_dirs, &f->list);
-       return sofar;
+               score->error = "Depends don't exist";
+               score_file_error(score, f, 0, "could not stat");
+       } else
+               list_add_tail(&m->dep_dirs, &f->list);
 }
 
 }
 
-static void *check_depends_exist(struct manifest *m,
-                                bool keep,
-                                unsigned int *timeleft)
+static void check_depends_exist(struct manifest *m,
+                               bool keep,
+                               unsigned int *timeleft, struct score *score)
 {
        unsigned int i;
 {
        unsigned int i;
-       char *report = NULL;
        char **deps;
        char *updir = talloc_strdup(m, m->dir);
 
        char **deps;
        char *updir = talloc_strdup(m, m->dir);
 
@@ -52,25 +47,18 @@ static void *check_depends_exist(struct manifest *m,
                if (!strstarts(deps[i], "ccan/"))
                        continue;
 
                if (!strstarts(deps[i], "ccan/"))
                        continue;
 
-               report = add_dep(report, m, deps[i]);
+               add_dep(m, deps[i], score);
+       }
+       if (!score->error) {
+               score->pass = true;
+               score->score = score->total;
        }
        }
-       return report;
-}
-
-static const char *describe_depends_exist(struct manifest *m,
-                                         void *check_result)
-{
-       return talloc_asprintf(check_result,
-                              "The following dependencies are are expected:\n"
-                              "%s", (char *)check_result);
 }
 
 struct ccanlint depends_exist = {
        .key = "depends-exist",
        .name = "Module's CCAN dependencies are present",
 }
 
 struct ccanlint depends_exist = {
        .key = "depends-exist",
        .name = "Module's CCAN dependencies are present",
-       .total_score = 1,
        .check = check_depends_exist,
        .check = check_depends_exist,
-       .describe = describe_depends_exist,
 };
 
 REGISTER_TEST(depends_exist, NULL);
 };
 
 REGISTER_TEST(depends_exist, NULL);
index 79d10d29775d65c7fa3a7e2695c113429ff17d35..cc5edc4ccaabbf2525b9072a4e64da3ceef6d02f 100644 (file)
@@ -32,12 +32,12 @@ static struct ccan_file *main_header(struct manifest *m)
                        return f;
        }
        /* Should not happen: we depend on has_main_header */
                        return f;
        }
        /* Should not happen: we depend on has_main_header */
-       return NULL;
+       abort();
 }
 
 }
 
-static void *check_includes_build(struct manifest *m,
-                                 bool keep,
-                                 unsigned int *timeleft)
+static void check_includes_build(struct manifest *m,
+                                bool keep,
+                                unsigned int *timeleft, struct score *score)
 {
        char *contents;
        char *tmpsrc, *tmpobj;
 {
        char *contents;
        char *tmpsrc, *tmpobj;
@@ -49,34 +49,29 @@ static void *check_includes_build(struct manifest *m,
 
        fd = open(tmpsrc, O_WRONLY | O_CREAT | O_EXCL, 0600);
        if (fd < 0)
 
        fd = open(tmpsrc, O_WRONLY | O_CREAT | O_EXCL, 0600);
        if (fd < 0)
-               return talloc_asprintf(m, "Creating temporary file %s: %s",
-                                      tmpsrc, strerror(errno));
+               err(1, "Creating temporary file %s", tmpsrc);
 
        contents = talloc_asprintf(tmpsrc, "#include <ccan/%s/%s.h>\n",
                                   m->basename, m->basename);
 
        contents = talloc_asprintf(tmpsrc, "#include <ccan/%s/%s.h>\n",
                                   m->basename, m->basename);
-       if (write(fd, contents, strlen(contents)) != strlen(contents)) {
-               close(fd);
-               return "Failure writing to temporary file";
-       }
+       if (write(fd, contents, strlen(contents)) != strlen(contents))
+               err(1, "writing to temporary file %s", tmpsrc);
        close(fd);
 
        close(fd);
 
-       return compile_object(m, tmpsrc, ccan_dir, "", tmpobj);
-}
-
-static const char *describe_includes_build(struct manifest *m,
-                                          void *check_result)
-{
-       return talloc_asprintf(check_result, 
-                              "#include of the main header file:\n"
-                              "%s", (char *)check_result);
+       score->error = compile_object(m, tmpsrc, ccan_dir, "", tmpobj);
+       if (score->error) {
+               score->error = talloc_asprintf(score,
+                                      "#include of the main header file:\n%s",
+                                      score->error);
+       } else {
+               score->pass = true;
+               score->score = score->total;
+       }
 }
 
 struct ccanlint includes_build = {
        .key = "include-main",
        .name = "Modules main header compiles",
 }
 
 struct ccanlint includes_build = {
        .key = "include-main",
        .name = "Modules main header compiles",
-       .total_score = 1,
        .check = check_includes_build,
        .check = check_includes_build,
-       .describe = describe_includes_build,
        .can_run = can_build,
 };
 
        .can_run = can_build,
 };
 
index 77867dedd9c875ff22bcc4f42c7886065bd99265..ce131f85686ec88111c1b0ea709a04c77fbbcc50 100644 (file)
 #include <ccan/noerr/noerr.h>
 #include <ccan/talloc/talloc.h>
 
 #include <ccan/noerr/noerr.h>
 #include <ccan/talloc/talloc.h>
 
-static void *check_has_info(struct manifest *m,
-                           bool keep,
-                           unsigned int *timeleft)
+static void check_has_info(struct manifest *m,
+                          bool keep,
+                          unsigned int *timeleft,
+                          struct score *score)
 {
 {
-       if (m->info_file)
-               return NULL;
-       return m;
-}
-
-static const char *describe_has_info(struct manifest *m, void *check_result)
-{
-       return "You have no _info file.\n\n"
+       if (m->info_file) {
+               score->pass = true;
+               score->score = score->total;
+       } else {
+               score->error = "You have no _info file.\n\n"
        "The file _info contains the metadata for a ccan package: things\n"
        "like the dependencies, the documentation for the package as a whole\n"
        "and license information.\n";
        "The file _info contains the metadata for a ccan package: things\n"
        "like the dependencies, the documentation for the package as a whole\n"
        "and license information.\n";
+       }
 }
 
 static const char template[] = 
 }
 
 static const char template[] = 
@@ -55,7 +54,7 @@ static const char template[] =
        "       return 1;\n"
        "}\n";
 
        "       return 1;\n"
        "}\n";
 
-static void create_info_template(struct manifest *m, void *check_result)
+static void create_info_template(struct manifest *m, struct score *score)
 {
        FILE *info;
        const char *filename;
 {
        FILE *info;
        const char *filename;
@@ -79,7 +78,6 @@ struct ccanlint has_info = {
        .key = "info",
        .name = "Module has _info file",
        .check = check_has_info,
        .key = "info",
        .name = "Module has _info file",
        .check = check_has_info,
-       .describe = describe_has_info,
        .handle = create_info_template,
 };
 
        .handle = create_info_template,
 };
 
index eb74e9f0101b016a577e98218428cd4e7e97cff6..bce6ef485a2b448778e0aefd69742e426ce243b7 100644 (file)
 #include <ccan/talloc/talloc.h>
 #include <ccan/noerr/noerr.h>
 
 #include <ccan/talloc/talloc.h>
 #include <ccan/noerr/noerr.h>
 
-static void *check_has_main_header(struct manifest *m,
-                                  bool keep,
-                                  unsigned int *timeleft)
+static void check_has_main_header(struct manifest *m,
+                                 bool keep,
+                                 unsigned int *timeleft, struct score *score)
 {
        struct ccan_file *f;
 
        list_for_each(&m->h_files, f, list) {
                if (strstarts(f->name, m->basename)
 {
        struct ccan_file *f;
 
        list_for_each(&m->h_files, f, list) {
                if (strstarts(f->name, m->basename)
-                   && strlen(f->name) == strlen(m->basename) + 2)
-                       return NULL;
+                   && strlen(f->name) == strlen(m->basename) + 2) {
+                       score->pass = true;
+                       score->score = score->total;
+                       return;
+               } 
        }
        }
-       return m;
-}
-
-static const char *describe_has_main_header(struct manifest *m,
-                                           void *check_result)
-{
-       return talloc_asprintf(m,
+       score->error = talloc_asprintf(score,
        "You have no %s/%s.h header file.\n\n"
        "CCAN modules have a name, the same as the directory name.  They're\n"
        "expected to have an interface in the header of the same name.\n",
        "You have no %s/%s.h header file.\n\n"
        "CCAN modules have a name, the same as the directory name.  They're\n"
        "expected to have an interface in the header of the same name.\n",
-                              m->basename, m->basename);
+                                      m->basename, m->basename);
 }
 
 struct ccanlint has_main_header = {
        .key = "has-main-header",
        .name = "Module has main header file",
        .check = check_has_main_header,
 }
 
 struct ccanlint has_main_header = {
        .key = "has-main-header",
        .name = "Module has main header file",
        .check = check_has_main_header,
-       .describe = describe_has_main_header,
 };
 
 REGISTER_TEST(has_main_header, NULL);
 };
 
 REGISTER_TEST(has_main_header, NULL);
index b83e877e0455f6c3006537c90ea023e0034b2aaf..f58dddf241b377c5d99a433abfb5df4e765955a4 100644 (file)
@@ -139,30 +139,6 @@ static void add_files(struct manifest *m, const char *dir)
        closedir(d);
 }
 
        closedir(d);
 }
 
-char *report_on_lines(struct list_head *files,
-                     char *(*report)(const char *),
-                     char *sofar)
-{
-       struct ccan_file *f;
-
-       list_for_each(files, f, list) {
-               unsigned int i;
-               char **lines = get_ccan_file_lines(f);
-
-               for (i = 0; i < f->num_lines; i++) {
-                       char *r = report(lines[i]);
-                       if (!r)
-                               continue;
-
-                       sofar = talloc_asprintf_append(sofar,
-                                                      "%s:%u:%s\n",
-                                                      f->name, i+1, r);
-                       talloc_free(r);
-               }
-       }
-       return sofar;
-}
-
 struct manifest *get_manifest(const void *ctx, const char *dir)
 {
        struct manifest *m = talloc(ctx, struct manifest);
 struct manifest *get_manifest(const void *ctx, const char *dir)
 {
        struct manifest *m = talloc(ctx, struct manifest);
@@ -574,3 +550,12 @@ enum line_compiled get_ccan_line_pp(struct pp_conditions *cond,
        return ret;
 }
 
        return ret;
 }
 
+void score_file_error(struct score *score, struct ccan_file *f, unsigned line,
+                     const char *error)
+{
+       struct file_error *fe = talloc(score, struct file_error);
+       fe->file = f;
+       fe->line = line;
+       fe->error = error;
+       list_add(&score->per_file_errors, &fe->list);
+}
index 98f7948d750b8af20aefce59d5a38c86093e3505..edb3cb22f95f58558ea2910f75a028fef15ec2e2 100644 (file)
@@ -25,8 +25,9 @@ static const char *can_run_coverage(struct manifest *m)
        return NULL;
 }
 
        return NULL;
 }
 
-static char *build_module_objs_with_coverage(struct manifest *m, bool keep,
-                                            char **modobjs)
+static bool build_module_objs_with_coverage(struct manifest *m, bool keep,
+                                           struct score *score,
+                                           char **modobjs)
 {
        struct ccan_file *i;
 
 {
        struct ccan_file *i;
 
@@ -39,13 +40,15 @@ static char *build_module_objs_with_coverage(struct manifest *m, bool keep,
                err = compile_object(m, fullfile, ccan_dir, "",
                                     i->cov_compiled);
                if (err) {
                err = compile_object(m, fullfile, ccan_dir, "",
                                     i->cov_compiled);
                if (err) {
+                       score_file_error(score, i, 0, err);
                        talloc_free(i->cov_compiled);
                        talloc_free(i->cov_compiled);
-                       return err;
+                       i->cov_compiled = NULL;
+                       return false;
                }
                *modobjs = talloc_asprintf_append(*modobjs,
                                                  " %s", i->cov_compiled);
        }
                }
                *modobjs = talloc_asprintf_append(*modobjs,
                                                  " %s", i->cov_compiled);
        }
-       return NULL;
+       return true;
 }
 
 static char *obj_list(const struct manifest *m, const char *modobjs)
 }
 
 static char *obj_list(const struct manifest *m, const char *modobjs)
@@ -101,97 +104,53 @@ static char *cov_compile(const void *ctx,
                                  lib_list(m), file->cov_compiled);
        if (errmsg) {
                talloc_free(file->cov_compiled);
                                  lib_list(m), file->cov_compiled);
        if (errmsg) {
                talloc_free(file->cov_compiled);
+               file->cov_compiled = NULL;
                return errmsg;
        }
 
        return NULL;
 }
 
                return errmsg;
        }
 
        return NULL;
 }
 
-struct compile_tests_result {
-       struct list_node list;
-       const char *filename;
-       const char *description;
-       const char *output;
-};
-
-static void *do_compile_coverage_tests(struct manifest *m,
-                                      bool keep,
-                                      unsigned int *timeleft)
+/* FIXME: Coverage from testable examples as well. */
+static void do_compile_coverage_tests(struct manifest *m,
+                                     bool keep,
+                                     unsigned int *timeleft,
+                                     struct score *score)
 {
 {
-       struct list_head *list = talloc(m, struct list_head);
        char *cmdout, *modobjs = NULL;
        struct ccan_file *i;
        char *cmdout, *modobjs = NULL;
        struct ccan_file *i;
-       struct compile_tests_result *res;
 
 
-       list_head_init(list);
-
-       if (!list_empty(&m->api_tests)) {
-               cmdout = build_module_objs_with_coverage(m, keep, &modobjs);
-               if (cmdout) {
-                       res = talloc(list, struct compile_tests_result);
-                       res->filename = "Module objects with coverage";
-                       res->description = "failed to compile";
-                       res->output = talloc_steal(res, cmdout);
-                       list_add_tail(list, &res->list);
-                       return list;
-               }
+       if (!list_empty(&m->api_tests)
+           && !build_module_objs_with_coverage(m, keep, score, &modobjs)) {
+               score->error = "Failed to compile module objects with coverage";
+               return;
        }
 
        list_for_each(&m->run_tests, i, list) {
        }
 
        list_for_each(&m->run_tests, i, list) {
-               compile_tests.total_score++;
                cmdout = cov_compile(m, m, i, NULL, keep);
                if (cmdout) {
                cmdout = cov_compile(m, m, i, NULL, keep);
                if (cmdout) {
-                       res = talloc(list, struct compile_tests_result);
-                       res->filename = i->name;
-                       res->description = "failed to compile";
-                       res->output = talloc_steal(res, cmdout);
-                       list_add_tail(list, &res->list);
+                       score->error = "Failed to compile test with coverage";
+                       score_file_error(score, i, 0, cmdout);
                }
        }
 
        list_for_each(&m->api_tests, i, list) {
                }
        }
 
        list_for_each(&m->api_tests, i, list) {
-               compile_tests.total_score++;
                cmdout = cov_compile(m, m, i, modobjs, keep);
                if (cmdout) {
                cmdout = cov_compile(m, m, i, modobjs, keep);
                if (cmdout) {
-                       res = talloc(list, struct compile_tests_result);
-                       res->filename = i->name;
-                       res->description = "failed to compile";
-                       res->output = talloc_steal(res, cmdout);
-                       list_add_tail(list, &res->list);
+                       score->error = "Failed to compile test with coverage";
+                       score_file_error(score, i, 0, cmdout);
                }
        }
                }
        }
-
-       if (list_empty(list)) {
-               talloc_free(list);
-               list = NULL;
+       if (!score->error) {
+               score->pass = true;
+               score->score = score->total;
        }
        }
-
-       return list;
-}
-
-static const char *describe_compile_coverage_tests(struct manifest *m,
-                                                  void *check_result)
-{
-       struct list_head *list = check_result;
-       struct compile_tests_result *i;
-       char *descrip;
-
-       descrip = talloc_strdup(list,
-                               "Compilation of tests for coverage failed:\n");
-
-       list_for_each(list, i, list)
-               descrip = talloc_asprintf_append(descrip, "%s %s\n%s",
-                                                i->filename, i->description,
-                                                i->output);
-       return descrip;
 }
 
 struct ccanlint compile_coverage_tests = {
        .key = "compile-coverage-tests",
        .name = "Module tests compile with profiling",
        .check = do_compile_coverage_tests,
 }
 
 struct ccanlint compile_coverage_tests = {
        .key = "compile-coverage-tests",
        .name = "Module tests compile with profiling",
        .check = do_compile_coverage_tests,
-       .total_score = 1,
-       .describe = describe_compile_coverage_tests,
        .can_run = can_run_coverage,
 };
 
        .can_run = can_run_coverage,
 };
 
index 0ab0cade7e96f427ad5d9f3f4dfdf7d336bf3941..e2e3ed843f82a9477da9a2ffef48179e1aea19a7 100644 (file)
@@ -14,7 +14,7 @@
 #include <string.h>
 #include <ctype.h>
 
 #include <string.h>
 #include <ctype.h>
 
-static const char *can_build(struct manifest *m)
+static const char *can_run(struct manifest *m)
 {
        if (safe_mode)
                return "Safe mode enabled";
 {
        if (safe_mode)
                return "Safe mode enabled";
@@ -30,39 +30,35 @@ static char *compile(struct manifest *m,
                              cfile->compiled);
 }
 
                              cfile->compiled);
 }
 
-static void *do_compile_test_helpers(struct manifest *m,
-                                    bool keep,
-                                    unsigned int *timeleft)
+static void do_compile_test_helpers(struct manifest *m,
+                                   bool keep,
+                                   unsigned int *timeleft,
+                                   struct score *score)
 {
 {
-       char *cmdout = NULL;
        struct ccan_file *i;
 
        struct ccan_file *i;
 
-       compile_tests.total_score = 0;
+       if (list_empty(&m->other_test_c_files))
+               score->total = 0;
+
        list_for_each(&m->other_test_c_files, i, list) {
        list_for_each(&m->other_test_c_files, i, list) {
-               compile_tests.total_score++;
-               cmdout = compile(m, keep, i);
-               if (cmdout)
-                       return talloc_asprintf(m,
-                                              "Failed to compile helper C"
-                                              " code file %s:\n%s",
-                                              i->name, cmdout);
+               char *cmdout = compile(m, keep, i);
+               if (cmdout) {
+                       score->error = "Failed to compile helper C files";
+                       score_file_error(score, i, 0, cmdout);
+               }
        }
        }
-       return NULL;
-}
 
 
-static const char *describe_compile_test_helpers(struct manifest *m,
-                                                void *check_result)
-{
-       return check_result;
+       if (!score->error) {
+               score->pass = true;
+               score->score = score->total;
+       }
 }
 
 struct ccanlint compile_test_helpers = {
        .key = "compile-helpers",
        .name = "Module test helper objects compile",
 }
 
 struct ccanlint compile_test_helpers = {
        .key = "compile-helpers",
        .name = "Module test helper objects compile",
-       .total_score = 1,
        .check = do_compile_test_helpers,
        .check = do_compile_test_helpers,
-       .describe = describe_compile_test_helpers,
-       .can_run = can_build,
+       .can_run = can_run,
 };
 
 REGISTER_TEST(compile_test_helpers, &depends_built, &has_tests, NULL);
 };
 
 REGISTER_TEST(compile_test_helpers, &depends_built, &has_tests, NULL);
index 3d6d3881883d5494bffa504050faea9172ab0ca6..062912d427ea3bb49e2b22a71cca94082c606df3 100644 (file)
@@ -82,124 +82,64 @@ static char *compile(const void *ctx,
        return NULL;
 }
 
        return NULL;
 }
 
-struct compile_tests_result {
-       struct list_node list;
-       const char *filename;
-       const char *description;
-       const char *output;
-};
-
-static void *do_compile_tests(struct manifest *m,
-                             bool keep,
-                             unsigned int *timeleft)
+static void do_compile_tests(struct manifest *m,
+                            bool keep,
+                            unsigned int *timeleft, struct score *score)
 {
 {
-       struct list_head *list = talloc(m, struct list_head);
        char *cmdout;
        struct ccan_file *i;
        char *cmdout;
        struct ccan_file *i;
-       struct compile_tests_result *res;
 
 
-       list_head_init(list);
-
-       compile_tests.total_score = 0;
        list_for_each(&m->compile_ok_tests, i, list) {
        list_for_each(&m->compile_ok_tests, i, list) {
-               compile_tests.total_score++;
-               cmdout = compile(list, m, i, false, false, keep);
+               cmdout = compile(score, m, i, false, false, keep);
                if (cmdout) {
                if (cmdout) {
-                       res = talloc(list, struct compile_tests_result);
-                       res->filename = i->name;
-                       res->description = "failed to compile";
-                       res->output = talloc_steal(res, cmdout);
-                       list_add_tail(list, &res->list);
+                       score->error = "Failed to compile tests";
+                       score_file_error(score, i, 0, cmdout);
                }
        }
 
        list_for_each(&m->run_tests, i, list) {
                }
        }
 
        list_for_each(&m->run_tests, i, list) {
-               compile_tests.total_score++;
-               cmdout = compile(m, m, i, false, false, keep);
+               cmdout = compile(score, m, i, false, false, keep);
                if (cmdout) {
                if (cmdout) {
-                       res = talloc(list, struct compile_tests_result);
-                       res->filename = i->name;
-                       res->description = "failed to compile";
-                       res->output = talloc_steal(res, cmdout);
-                       list_add_tail(list, &res->list);
+                       score->error = "Failed to compile tests";
+                       score_file_error(score, i, 0, cmdout);
                }
        }
 
        list_for_each(&m->api_tests, i, list) {
                }
        }
 
        list_for_each(&m->api_tests, i, list) {
-               compile_tests.total_score++;
-               cmdout = compile(m, m, i, false, true, keep);
+               cmdout = compile(score, m, i, false, true, keep);
                if (cmdout) {
                if (cmdout) {
-                       res = talloc(list, struct compile_tests_result);
-                       res->filename = i->name;
-                       res->description = "failed to compile";
-                       res->output = talloc_steal(res, cmdout);
-                       list_add_tail(list, &res->list);
+                       score->error = "Failed to compile tests";
+                       score_file_error(score, i, 0, cmdout);
                }
        }
 
                }
        }
 
+       /* The compile fail tests are a bit weird, handle them separately */
+       if (score->error)
+               return;
+
        list_for_each(&m->compile_fail_tests, i, list) {
        list_for_each(&m->compile_fail_tests, i, list) {
-               compile_tests.total_score++;
-               cmdout = compile(list, m, i, false, false, false);
+               cmdout = compile(score, m, i, false, false, false);
                if (cmdout) {
                if (cmdout) {
-                       res = talloc(list, struct compile_tests_result);
-                       res->filename = i->name;
-                       res->description = "failed to compile without -DFAIL";
-                       res->output = talloc_steal(res, cmdout);
-                       list_add_tail(list, &res->list);
-               } else {
-                       cmdout = compile(list, m, i, true, false, false);
-                       if (!cmdout) {
-                               res = talloc(list, struct compile_tests_result);
-                               res->filename = i->name;
-                               res->description = "compiled successfully"
-                                       " with -DFAIL";
-                               res->output = "";
-                               list_add_tail(list, &res->list);
-                       }
+                       score->error = "Failed to compile without -DFAIL";
+                       score_file_error(score, i, 0, cmdout);
+                       return;
+               }
+               cmdout = compile(score, m, i, true, false, false);
+               if (!cmdout) {
+                       score->error = "Compiled successfully with -DFAIL?";
+                       score_file_error(score, i, 0, NULL);
+                       return;
                }
        }
 
                }
        }
 
-       if (list_empty(list)) {
-               talloc_free(list);
-               list = NULL;
-       }
-
-       return list;
-}
-
-static unsigned int score_compile_tests(struct manifest *m,
-                                       void *check_result)
-{
-       struct list_head *list = check_result;
-       struct compile_tests_result *i;
-       unsigned int score = compile_tests.total_score;
-
-       list_for_each(list, i, list)
-               score--;
-       return score;
-}
-
-static const char *describe_compile_tests(struct manifest *m,
-                                         void *check_result)
-{
-       struct list_head *list = check_result;
-       struct compile_tests_result *i;
-       char *descrip = talloc_strdup(list, "Compilation tests failed:\n");
-
-       list_for_each(list, i, list)
-               descrip = talloc_asprintf_append(descrip, "%s %s\n%s",
-                                                i->filename, i->description,
-                                                i->output);
-       return descrip;
+       score->pass = true;
+       score->score = score->total;
 }
 
 struct ccanlint compile_tests = {
        .key = "compile-tests",
        .name = "Module tests compile",
 }
 
 struct ccanlint compile_tests = {
        .key = "compile-tests",
        .name = "Module tests compile",
-       .score = score_compile_tests,
-       .total_score = 1,
        .check = do_compile_tests,
        .check = do_compile_tests,
-       .describe = describe_compile_tests,
        .can_run = can_build,
 };
 
        .can_run = can_build,
 };
 
index 78dad0b13a9c904cacf661a44658948e84ddfc83..3ba19f60a3240e0385dd720b509887bd4e4b493e 100644 (file)
@@ -46,12 +46,11 @@ static bool has_dep(struct manifest *m, const char *depname, bool tap_ok)
        return false;
 }
 
        return false;
 }
 
-static void *check_depends_accurate(struct manifest *m,
-                                   bool keep,
-                                   unsigned int *timeleft)
+static void check_depends_accurate(struct manifest *m,
+                                  bool keep,
+                                  unsigned int *timeleft, struct score *score)
 {
        struct list_head *list;
 {
        struct list_head *list;
-       char *report = talloc_strdup(m, "");
 
        foreach_ptr(list, &m->c_files, &m->h_files,
                    &m->run_tests, &m->api_tests,
 
        foreach_ptr(list, &m->c_files, &m->h_files,
                    &m->run_tests, &m->api_tests,
@@ -61,7 +60,7 @@ static void *check_depends_accurate(struct manifest *m,
                bool tap_ok;
 
                /* Including ccan/tap is fine for tests. */
                bool tap_ok;
 
                /* Including ccan/tap is fine for tests. */
-               tap_ok =  (list != &m->c_files && list != &m->h_files);
+               tap_ok = (list != &m->c_files && list != &m->h_files);
 
                list_for_each(list, f, list) {
                        unsigned int i;
 
                list_for_each(list, f, list) {
                        unsigned int i;
@@ -79,35 +78,25 @@ static void *check_depends_accurate(struct manifest *m,
                                if (!strchr(strchr(p, '/') + 1, '/'))
                                        continue;
                                *strchr(strchr(p, '/') + 1, '/') = '\0';
                                if (!strchr(strchr(p, '/') + 1, '/'))
                                        continue;
                                *strchr(strchr(p, '/') + 1, '/') = '\0';
-                               if (!has_dep(m, p, tap_ok))
-                                       report = talloc_asprintf_append(report,
-                                              "%s:%u:%s\n",
-                                              f->name, i+1, lines[i]);
+                               if (has_dep(m, p, tap_ok))
+                                       continue;
+                               score->error = "Includes a ccan module"
+                                       " not listed in _info";
+                               score_file_error(score, f, i+1, lines[i]);
                        }
                }
        }
 
                        }
                }
        }
 
-       if (streq(report, "")) {
-               talloc_free(report);
-               report = NULL;
+       if (!score->error) {
+               score->pass = true;
+               score->score = score->total;
        }
        }
-       return report;
-}
-
-static const char *describe_depends_accurage(struct manifest *m,
-                                            void *check_result)
-{
-       return talloc_asprintf(check_result, 
-                              "You include ccan modules you don't list as dependencies:\n"
-                              "%s", (char *)check_result);
 }
 
 struct ccanlint depends_accurate = {
        .key = "depends-accurate",
        .name = "Module's CCAN dependencies are the only ccan files #included",
 }
 
 struct ccanlint depends_accurate = {
        .key = "depends-accurate",
        .name = "Module's CCAN dependencies are the only ccan files #included",
-       .total_score = 1,
        .check = check_depends_accurate,
        .check = check_depends_accurate,
-       .describe = describe_depends_accurage,
 };
 
 REGISTER_TEST(depends_accurate, &depends_exist, NULL);
 };
 
 REGISTER_TEST(depends_accurate, &depends_exist, NULL);
index 80c5ba631375e58bd24d78463b6aa65139434679..b34846371cfb8b96bca5e8ae1d383cfa85bc3067 100644 (file)
@@ -15,6 +15,8 @@ static const char *can_run(struct manifest *m)
 {
        if (safe_mode)
                return "Safe mode enabled";
 {
        if (safe_mode)
                return "Safe mode enabled";
+       if (list_empty(&m->examples))
+               return "No examples to compile";
        return NULL;
 }
 
        return NULL;
 }
 
@@ -128,11 +130,6 @@ static char *compile(const void *ctx,
        return NULL;
 }
 
        return NULL;
 }
 
-struct score {
-       unsigned int score;
-       char *errors;
-};
-
 static char *start_main(char *ret, const char *why)
 {
        return talloc_asprintf_append(ret,
 static char *start_main(char *ret, const char *why)
 {
        return talloc_asprintf_append(ret,
@@ -434,22 +431,21 @@ static struct ccan_file *mangle_example(struct manifest *m,
        return f;
 }
 
        return f;
 }
 
-static void *build_examples(struct manifest *m, bool keep,
-                           unsigned int *timeleft)
+static void build_examples(struct manifest *m, bool keep,
+                          unsigned int *timeleft, struct score *score)
 {
        struct ccan_file *i;
 {
        struct ccan_file *i;
-       struct score *score = talloc(m, struct score);
        char **prev = NULL;
 
        char **prev = NULL;
 
-       score->score = 0;
-       score->errors = talloc_strdup(score, "");
+       score->total = 0;
+       score->pass = true;
 
 
-       examples_compile.total_score = 0;
        list_for_each(&m->examples, i, list) {
                char *ret, *ret1, *ret2 = NULL;
                struct ccan_file *mangle1, *mangle2 = NULL;
        list_for_each(&m->examples, i, list) {
                char *ret, *ret1, *ret2 = NULL;
                struct ccan_file *mangle1, *mangle2 = NULL;
+               char *err;
 
 
-               examples_compile.total_score++;
+               score->total++;
                /* Simplify our dumb parsing. */
                strip_leading_whitespace(get_ccan_file_lines(i));
                ret = compile(i, m, i, keep);
                /* Simplify our dumb parsing. */
                strip_leading_whitespace(get_ccan_file_lines(i));
                ret = compile(i, m, i, keep);
@@ -481,64 +477,46 @@ static void *build_examples(struct manifest *m, bool keep,
                        }
                }
 
                        }
                }
 
-               score->errors = talloc_asprintf_append(score->errors,
-                                      "%s: tried standalone example:\n"
-                                      "%s\n"
-                                      "Errors: %s\n\n",
-                                      i->name,
-                                      get_ccan_file_contents(i),
-                                      ret);
-               score->errors = talloc_asprintf_append(score->errors,
-                                      "%s: tried adding headers, wrappers:\n"
-                                      "%s\n"
-                                      "Errors: %s\n\n",
-                                      i->name,
-                                      get_ccan_file_contents(mangle1),
-                                      ret1);
-
-               if (mangle2) {
-                       score->errors = talloc_asprintf_append(score->errors,
-                                      "%s: tried combining with"
-                                      " previous example:\n"
+               score->pass = false;
+               score->error = "Compiling extracted examples failed";
+               if (!verbose) {
+                       if (mangle2) 
+                               err = "Standalone, adding headers, "
+                                       "and including previous "
+                                       "example all failed";
+                       else
+                               err = "Standalone compile and"
+                                       " adding headers both failed";
+               } else {
+                       err = talloc_asprintf("Standalone example:\n"
+                                             "%s\n"
+                                             "Errors: %s\n\n"
+                                             "Adding headers, wrappers:\n"
+                                             "%s\n"
+                                             "Errors: %s\n\n",
+                                             get_ccan_file_contents(i),
+                                             ret,
+                                             get_ccan_file_contents(mangle1),
+                                             ret1);
+
+                       if (mangle2)
+                               err = talloc_asprintf_append(err, 
+                                      "Combining with previous example:\n"
                                       "%s\n"
                                       "Errors: %s\n\n",
                                       "%s\n"
                                       "Errors: %s\n\n",
-                                      i->name,
                                       get_ccan_file_contents(mangle2),
                                       ret2);
                }
                                       get_ccan_file_contents(mangle2),
                                       ret2);
                }
+               score_file_error(score, i, 0, err);
                /* This didn't work, so not a candidate for combining. */
                prev = NULL;
        }
                /* This didn't work, so not a candidate for combining. */
                prev = NULL;
        }
-
-       if (strcmp(score->errors, "") == 0) {
-               talloc_free(score);
-               return NULL;
-       }
-       return score;
-}
-
-static unsigned int score_examples(struct manifest *m, void *check_result)
-{
-       struct score *score = check_result;
-       return score->score;
-}
-
-static const char *describe(struct manifest *m, void *check_result)
-{
-       struct score *score = check_result;
-       if (verbose >= 2 && score->errors)
-               return talloc_asprintf(m, "Compile errors building examples:\n"
-                                      "%s", score->errors);
-       return NULL;
 }
 
 struct ccanlint examples_compile = {
        .key = "examples-compile",
        .name = "Module examples compile",
 }
 
 struct ccanlint examples_compile = {
        .key = "examples-compile",
        .name = "Module examples compile",
-       .score = score_examples,
-       .total_score = 3, /* This gets changed to # examples, if they exist */
        .check = build_examples,
        .check = build_examples,
-       .describe = describe,
        .can_run = can_run,
 };
 
        .can_run = can_run,
 };
 
index 522feb0a0bf320422c80f4bc5c5fb73335b159b0..3cb456d49fa69d38f073f76229aa706b1ccb8e5a 100644 (file)
 
 static const char *can_run(struct manifest *m)
 {
 
 static const char *can_run(struct manifest *m)
 {
+       struct list_head *list;
+
        if (safe_mode)
                return "Safe mode enabled";
        if (safe_mode)
                return "Safe mode enabled";
-       return NULL;
+       foreach_ptr(list, &m->examples, &m->mangled_examples)
+               if (!list_empty(list))
+                       return NULL;
+       return "No examples";
 }
 
 }
 
-struct score {
-       unsigned int score;
-       char *errors;
-};
-
 /* Very dumb scanner, allocates %s-strings. */
 static bool scan_forv(const void *ctx,
                      const char *input, const char *fmt, const va_list *args)
 /* Very dumb scanner, allocates %s-strings. */
 static bool scan_forv(const void *ctx,
                      const char *input, const char *fmt, const va_list *args)
@@ -224,26 +224,21 @@ static char *unexpected(struct ccan_file *i, const char *input,
        return output;
 }
 
        return output;
 }
 
-static void *run_examples(struct manifest *m, bool keep,
-                         unsigned int *timeleft)
+static void run_examples(struct manifest *m, bool keep,
+                        unsigned int *timeleft, struct score *score)
 {
        struct ccan_file *i;
        struct list_head *list;
 {
        struct ccan_file *i;
        struct list_head *list;
-       struct score *score = talloc(m, struct score);
 
 
-       score->score = 0;
-       score->errors = talloc_strdup(score, "");
+       score->total = 0;
+       score->pass = true;
 
 
-       examples_run.total_score = 0;
        foreach_ptr(list, &m->examples, &m->mangled_examples) {
                list_for_each(list, i, list) {
                        char **lines, *expect, *input, *output;
                        unsigned int linenum = 0;
                        bool exact;
 
        foreach_ptr(list, &m->examples, &m->mangled_examples) {
                list_for_each(list, i, list) {
                        char **lines, *expect, *input, *output;
                        unsigned int linenum = 0;
                        bool exact;
 
-                       if (i->compiled == NULL)
-                               continue;
-
                        lines = get_ccan_file_lines(i);
 
                        for (expect = find_expect(i, lines, &input, &exact,
                        lines = get_ccan_file_lines(i);
 
                        for (expect = find_expect(i, lines, &input, &exact,
@@ -252,52 +247,34 @@ static void *run_examples(struct manifest *m, bool keep,
                             linenum++,
                                     expect = find_expect(i, lines, &input,
                                                          &exact, &linenum)) {
                             linenum++,
                                     expect = find_expect(i, lines, &input,
                                                          &exact, &linenum)) {
-                               examples_run.total_score++;
+                               char *err;
+                               score->total++;
+                               if (i->compiled == NULL)
+                                       continue;
+
                                output = unexpected(i, input, expect, exact);
                                output = unexpected(i, input, expect, exact);
-                               if (!output)
+                               if (!output) {
                                        score->score++;
                                        score->score++;
-                               else {
-                                       score->errors = talloc_asprintf_append(
-                                               score->errors,
-                                               "%s: output '%s' didn't"
-                                               " %s '%s'\n",
-                                               i->name, output,
-                                               exact ? "match" : "contain",
-                                               expect);
+                                       continue;
                                }
                                }
+                               err = talloc_asprintf(score,
+                                                     "output '%s' didn't"
+                                                     " %s '%s'\n",
+                                                     output,
+                                                     exact
+                                                     ? "match" : "contain",
+                                                     expect);
+                               score_file_error(score, i, linenum+1, err);
+                               score->pass = false;
                        }
                }
        }
                        }
                }
        }
-
-       if (strcmp(score->errors, "") == 0) {
-               talloc_free(score);
-               return NULL;
-       }
-       return score;
-}
-
-static unsigned int score_examples(struct manifest *m, void *check_result)
-{
-       struct score *score = check_result;
-       return score->score;
-}
-
-static const char *describe(struct manifest *m, void *check_result)
-{
-       struct score *score = check_result;
-       if (verbose)
-               return talloc_asprintf(m, "Wrong output running examples:\n"
-                                      "%s", score->errors);
-       return NULL;
 }
 
 struct ccanlint examples_run = {
        .key = "examples-run",
        .name = "Module examples with expected output give that output",
 }
 
 struct ccanlint examples_run = {
        .key = "examples-run",
        .name = "Module examples with expected output give that output",
-       .score = score_examples,
-       .total_score = 3, /* This gets changed to # testable, if we run. */
        .check = run_examples,
        .check = run_examples,
-       .describe = describe,
        .can_run = can_run,
 };
 
        .can_run = can_run,
 };
 
index bfb298f2773c390dc8f7088f074a5e3d82051ad6..7c95777222a992aea510b005d3aa7b735dddb78d 100644 (file)
@@ -56,28 +56,22 @@ static char *add_example(struct manifest *m, struct ccan_file *source,
 }
 
 /* FIXME: We should have one example per function in header. */
 }
 
 /* FIXME: We should have one example per function in header. */
-struct score {
-       bool info_example, header_example;
-       char *error;
-};
-
-static void *extract_examples(struct manifest *m,
-                             bool keep,
-                             unsigned int *timeleft)
+static void extract_examples(struct manifest *m,
+                            bool keep,
+                            unsigned int *timeleft,
+                            struct score *score)
 {
        struct ccan_file *f;
        struct doc_section *d;
 {
        struct ccan_file *f;
        struct doc_section *d;
-       struct score *score = talloc(m, struct score);
-
-       score->info_example = score->header_example = false;
-       score->error = NULL;
+       bool have_info_example = false, have_header_example = false;
 
 
+       score->total = 2;
        list_for_each(get_ccan_file_docs(m->info_file), d, list) {
                if (streq(d->type, "example")) {
                        score->error = add_example(m, m->info_file, keep, d);
                        if (score->error)
        list_for_each(get_ccan_file_docs(m->info_file), d, list) {
                if (streq(d->type, "example")) {
                        score->error = add_example(m, m->info_file, keep, d);
                        if (score->error)
-                               return score;
-                       score->info_example = true;
+                               return;
+                       have_info_example = true;
                }
        }
 
                }
        }
 
@@ -91,57 +85,35 @@ static void *extract_examples(struct manifest *m,
                        if (streq(d->type, "example")) {
                                score->error = add_example(m, f, keep, d);
                                if (score->error)
                        if (streq(d->type, "example")) {
                                score->error = add_example(m, f, keep, d);
                                if (score->error)
-                                       return score;
-                               score->header_example = true;
+                                       return;
+                               have_header_example = true;
                        }
                }
        }
                        }
                }
        }
-       return score;
-}
-
-static unsigned int score_examples(struct manifest *m, void *check_result)
-{
-       struct score *score = check_result;
-       int total = 0;
-
-       if (score->error)
-               return 0;
-       total += score->info_example;
-       total += score->header_example;
-       return total;
-}
-
-static const char *describe_examples(struct manifest *m,
-                                    void *check_result)
-{
-       struct score *score = check_result;
-       char *descrip = NULL;
-
-       if (score->error)
-               return score->error;
 
 
-       if (!score->info_example)
-               descrip = talloc_asprintf(score,
-               "Your _info file has no module example.\n\n"
-               "There should be an Example: section of the _info documentation\n"
-               "which provides a concise toy program which uses your module\n");
-
-       if (!score->header_example)
-               descrip = talloc_asprintf(score,
-                "%sMain header file file has no examples\n\n"
-                "There should be an Example: section for each public function\n"
-                 "demonstrating its use\n", descrip ? descrip : "");
-
-       return descrip;
+       if (!have_info_example && !have_header_example) {
+               score->error = "You don't have any Example: sections";
+               score->score = 0;
+       } else if (!have_info_example) {
+               score->error = "You don't have an Example: section in _info";
+               score->score = 1;
+               score->pass = true;
+       } else if (!have_header_example) {
+               score->error = talloc_asprintf(score,
+                              "You don't have an Example: section in %s.h",
+                              m->basename);
+               score->score = 1;
+               score->pass = true;
+       } else {
+               score->score = score->total;
+               score->pass = true;
+       }
 }
 
 struct ccanlint has_examples = {
        .key = "has-examples",
        .name = "_info and header files have examples",
 }
 
 struct ccanlint has_examples = {
        .key = "has-examples",
        .name = "_info and header files have examples",
-       .score = score_examples,
        .check = extract_examples,
        .check = extract_examples,
-       .describe = describe_examples,
-       .total_score = 2,
 };
 
 REGISTER_TEST(has_examples, &has_info, NULL);
 };
 
 REGISTER_TEST(has_examples, &has_info, NULL);
index cdfb0e2c63f3e29c9856887b49badbb356287722..6ecbf84247acf4a21bbe7e05f38fb84a325c8770 100644 (file)
 #include <ccan/noerr/noerr.h>
 #include <ccan/grab_file/grab_file.h>
 
 #include <ccan/noerr/noerr.h>
 #include <ccan/grab_file/grab_file.h>
 
-struct info_docs
-{
-       bool summary;
-       bool description;
-};
-
-static void *check_has_info_documentation(struct manifest *m,
-                                         bool keep,
-                                         unsigned int *timeleft)
-{
-       struct list_head *infodocs = get_ccan_file_docs(m->info_file);
-       struct doc_section *d;
-       struct info_docs id = { false, false };
-
-       list_for_each(infodocs, d, list) {
-               if (!streq(d->function, m->basename))
-                       continue;
-               if (streq(d->type, "summary"))
-                       id.summary = true;
-               if (streq(d->type, "description"))
-                       id.description = true;
-       }
-
-       if (id.summary && id.description)
-               return NULL;
-       return talloc_memdup(m, &id, sizeof(id));
-}
-
-/* This is defined below. */
-extern struct ccanlint has_info_documentation;
-
-static void create_info_template_doc(struct manifest *m, void *check_result)
+static void create_info_template_doc(struct manifest *m, struct score *score)
 {
        int fd = open("_info.new", O_WRONLY|O_CREAT|O_EXCL, 0666);
        FILE *new;
 {
        int fd = open("_info.new", O_WRONLY|O_CREAT|O_EXCL, 0666);
        FILE *new;
@@ -87,42 +56,44 @@ static void create_info_template_doc(struct manifest *m, void *check_result)
        }
 }
 
        }
 }
 
-static const char *describe_has_info_documentation(struct manifest *m,
-                                                  void *check_result)
+static void check_has_info_documentation(struct manifest *m,
+                                        bool keep,
+                                        unsigned int *timeleft,
+                                        struct score *score)
 {
 {
-       struct info_docs *id = check_result;
-       char *reason = talloc_strdup(m, "");
+       struct list_head *infodocs = get_ccan_file_docs(m->info_file);
+       struct doc_section *d;
+       bool summary = false, description = false;
 
 
-       if (!id->summary) {
-               has_info_documentation.handle = create_info_template_doc;
-               reason = talloc_asprintf_append(reason,
-               "Your _info file has no module documentation.\n\n"
+       list_for_each(infodocs, d, list) {
+               if (!streq(d->function, m->basename))
+                       continue;
+               if (streq(d->type, "summary"))
+                       summary = true;
+               if (streq(d->type, "description"))
+                       description = true;
+       }
+
+       if (summary && description) {
+               score->score = score->total;
+               score->pass = true;
+       } else if (!summary) {
+               score->error = "_info file has no module documentation.\n\n"
                "CCAN modules use /**-style comments for documentation: the\n"
                "CCAN modules use /**-style comments for documentation: the\n"
-               "overall documentation belongs in the _info metafile.\n");
+               "overall documentation belongs in the _info metafile.\n";
+               has_info_documentation.handle = create_info_template_doc;
        }
        }
-       if (!id->description)
-               reason = talloc_asprintf_append(reason,
-               "Your _info file has no module description.\n\n"
+       else if (!description) 
+               score->error = "_info file has no module description.\n\n"
                "The lines after the first summary line in the _info file\n"
                "documentation should describe the purpose and use of the\n"
                "The lines after the first summary line in the _info file\n"
                "documentation should describe the purpose and use of the\n"
-               "overall package\n");
-       return reason;
-}
-
-static unsigned int has_info_documentation_score(struct manifest *m,
-                                                void *check_result)
-{
-       struct info_docs *id = check_result;
-       return (unsigned int)id->summary + id->description;
+               "overall package\n";
 }
 
 struct ccanlint has_info_documentation = {
        .key = "info-documentation",
        .name = "Module has documentation in _info",
 }
 
 struct ccanlint has_info_documentation = {
        .key = "info-documentation",
        .name = "Module has documentation in _info",
-       .total_score = 2,
-       .score = has_info_documentation_score,
        .check = check_has_info_documentation,
        .check = check_has_info_documentation,
-       .describe = describe_has_info_documentation,
 };
 
 REGISTER_TEST(has_info_documentation, NULL);
 };
 
 REGISTER_TEST(has_info_documentation, NULL);
index 4237540011fed95d5a351d40246998ae9449ec17..0a510a034629b62d10bffa5ab06b04cfb043b0c3 100644 (file)
 #include <err.h>
 #include <ccan/talloc/talloc.h>
 
 #include <err.h>
 #include <ccan/talloc/talloc.h>
 
-static char test_is_not_dir[] = "test is not a directory";
-
-static void *check_has_tests(struct manifest *m,
-                            bool keep,
-                            unsigned int *timeleft)
+static void handle_no_tests(struct manifest *m, struct score *score)
 {
 {
-       struct stat st;
+       FILE *run;
+       struct ccan_file *i;
        char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
 
        char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
 
-       if (lstat(test_dir, &st) != 0) {
-               if (errno != ENOENT)
-                       err(1, "statting %s", test_dir);
-               return "You have no test directory";
-       }
-
-       if (!S_ISDIR(st.st_mode))
-               return test_is_not_dir;
-
-       if (list_empty(&m->api_tests)
-           && list_empty(&m->run_tests)
-           && list_empty(&m->compile_ok_tests)) {
-               if (list_empty(&m->compile_fail_tests))
-                       return "You have no tests in the test directory";
-               else
-                       return "You have no positive tests in the test directory";
-       }
-       return NULL;
-}
-
-static const char *describe_has_tests(struct manifest *m, void *check_result)
-{
-       return talloc_asprintf(m, "%s\n\n"
-        "CCAN modules have a directory called test/ which contains tests.\n"
+       printf(
+       "CCAN modules have a directory called test/ which contains tests.\n"
        "There are four kinds of tests: api, run, compile_ok and compile_fail:\n"
        "you can tell which type of test a C file is by its name, eg 'run.c'\n"
        "and 'run-simple.c' are both run tests.\n\n"
 
        "The simplest kind of test is a run test, which must compile with no\n"
        "There are four kinds of tests: api, run, compile_ok and compile_fail:\n"
        "you can tell which type of test a C file is by its name, eg 'run.c'\n"
        "and 'run-simple.c' are both run tests.\n\n"
 
        "The simplest kind of test is a run test, which must compile with no\n"
-       "warnings, and then run: it is expected to use libtap to report its\n"
+       "warnings, and then run: it is expected to use ccan/tap to report its\n"
        "results in a simple and portable format.  It should #include the C\n"
        "files from the module directly (so it can probe the internals): the\n"
        "module will not be linked in.  The test will be run in a temporary\n"
        "results in a simple and portable format.  It should #include the C\n"
        "files from the module directly (so it can probe the internals): the\n"
        "module will not be linked in.  The test will be run in a temporary\n"
@@ -67,20 +42,8 @@ static const char *describe_has_tests(struct manifest *m, void *check_result)
        "when it's not defined: this helps ensure unrelated errors don't make\n"
        "compilation fail.\n\n"
 
        "when it's not defined: this helps ensure unrelated errors don't make\n"
        "compilation fail.\n\n"
 
-       "Note that the tests are not linked against the files in the\n"
-       "above: you should directly #include those C files you want.  This\n"
-       "allows access to static functions and use special effects inside\n"
-       "test files\n", (char *)check_result);
-}
-
-static void handle_no_tests(struct manifest *m, void *check_result)
-{
-       FILE *run;
-       struct ccan_file *i;
-       char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
-
-       if (check_result == test_is_not_dir)
-               return;
+       "Note that only API tests are linked against the files in the module!\n"
+               );
 
        if (!ask("Should I create a template test/run.c file for you?"))
                return;
 
        if (!ask("Should I create a template test/run.c file for you?"))
                return;
@@ -101,41 +64,70 @@ static void handle_no_tests(struct manifest *m, void *check_result)
                        fprintf(run, "#include <ccan/%s/%s>\n",
                                m->basename, i->name);
        }
                        fprintf(run, "#include <ccan/%s/%s>\n",
                                m->basename, i->name);
        }
-       fputs("#include <ccan/tap/tap.h>\n", run);
-       fputs("\n", run);
-
-       fputs("int main(void)\n", run);
-       fputs("{\n", run);
-       fputs("\t/* This is how many tests you plan to run */\n", run);
-       fputs("\tplan_tests(3);\n", run);
-       fputs("\n", run);
-       fputs("\t/* Simple thing we expect to succeed */\n", run);
-       fputs("\tok1(some_test())\n", run);
-       fputs("\t/* Same, with an explicit description of the test. */\n", run);
-       fputs("\tok(some_test(), \"%s with no args should return 1\", \"some_test\")\n", run);
-       fputs("\t/* How to print out messages for debugging. */\n", run);
-       fputs("\tdiag(\"Address of some_test is %p\", &some_test)\n", run);
-       fputs("\t/* Conditional tests must be explicitly skipped. */\n", run);
-       fputs("#if HAVE_SOME_FEATURE\n", run);
-       fputs("\tok1(test_some_feature())\n", run);
-       fputs("#else\n", run);
-       fputs("\tskip(1, \"Don\'t have SOME_FEATURE\")\n", run);
-       fputs("#endif\n", run);
-       fputs("\n", run);
-       fputs("\t/* This exits depending on whether all tests passed */\n", run);
-       fputs("\treturn exit_status();\n", run);
-       fputs("}\n", run);
-
+       fprintf(run, "%s",
+               "#include <ccan/tap/tap.h>\n\n"
+               "int main(void)\n"
+               "{\n"
+               "       /* This is how many tests you plan to run */\n"
+               "       plan_tests(3);\n"
+               "\n"
+               "       /* Simple thing we expect to succeed */\n"
+               "       ok1(some_test())\n"
+               "       /* Same, with an explicit description of the test. */\n"
+               "       ok(some_test(), \"%s with no args should return 1\", \"some_test\")\n"
+               "       /* How to print out messages for debugging. */\n"
+               "       diag(\"Address of some_test is %p\", &some_test)\n"
+               "       /* Conditional tests must be explicitly skipped. */\n"
+               "#if HAVE_SOME_FEATURE\n"
+               "       ok1(test_some_feature())\n"
+               "#else\n"
+               "       skip(1, \"Don\'t have SOME_FEATURE\")\n"
+               "#endif\n"
+               "\n"
+               "       /* This exits depending on whether all tests passed */\n"
+               "       return exit_status();\n"
+               "}\n");
        fclose(run);
 }
 
        fclose(run);
 }
 
+static void check_has_tests(struct manifest *m,
+                           bool keep,
+                           unsigned int *timeleft, struct score *score)
+{
+       struct stat st;
+       char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
+
+       if (lstat(test_dir, &st) != 0) {
+               score->error = "No test directory";
+               if (errno != ENOENT)
+                       err(1, "statting %s", test_dir);
+               has_tests.handle = handle_no_tests;
+               return;
+       }
+
+       if (!S_ISDIR(st.st_mode)) {
+               score->error = "test is not a directory";
+               return;
+       }
+
+       if (list_empty(&m->api_tests)
+           && list_empty(&m->run_tests)
+           && list_empty(&m->compile_ok_tests)) {
+               if (list_empty(&m->compile_fail_tests)) {
+                       score->error = "No tests in test directory";
+                       has_tests.handle = handle_no_tests;
+               } else
+                       score->error = "No positive tests in test directory";
+               return;
+       }
+       score->pass = true;
+       score->score = score->total;
+}
+
 struct ccanlint has_tests = {
        .key = "has-tests",
        .name = "Module has tests",
        .check = check_has_tests,
 struct ccanlint has_tests = {
        .key = "has-tests",
        .name = "Module has tests",
        .check = check_has_tests,
-       .describe = describe_has_tests,
-       .total_score = 1,
-       .handle = handle_no_tests,
 };
 
 REGISTER_TEST(has_tests, NULL);
 };
 
 REGISTER_TEST(has_tests, NULL);
index 95040cf62b09b2f012d77d8b3683f6a9712d79e9..7ab4a57b7e31080e5630be981de524f4967524c9 100644 (file)
 static const char explain[] 
 = "Headers usually start with the C preprocessor lines to prevent multiple\n"
   "inclusions.  These look like the following:\n"
 static const char explain[] 
 = "Headers usually start with the C preprocessor lines to prevent multiple\n"
   "inclusions.  These look like the following:\n"
-  "#ifndef MY_HEADER_H\n"
-  "#define MY_HEADER_H\n"
+  "#ifndef CCAN_<MODNAME>_H\n"
+  "#define CCAN_<MODNAME>_H\n"
   "...\n"
   "...\n"
-  "#endif /* MY_HEADER_H */\n";
+  "#endif /* CCAN_<MODNAME>_H */\n";
 
 
-static char *report_idem(struct ccan_file *f, char *sofar)
+static void fix_name(char *name)
+{
+       unsigned int i, j;
+
+       for (i = j = 0; name[i]; i++) {
+               if (isalnum(name[i]) || name[i] == '_')
+                       name[j++] = toupper(name[i]);
+       }
+       name[j] = '\0';
+}
+
+static void handle_idem(struct manifest *m, struct score *score)
+{
+       struct file_error *e;
+
+       list_for_each(&score->per_file_errors, e, list) {
+               char *name, *q, *tmpname;
+               FILE *out;
+               unsigned int i;
+
+               /* Main header gets CCAN_FOO_H, others CCAN_FOO_XXX_H */
+               if (strstarts(e->file->name, m->basename)
+                   || strlen(e->file->name) != strlen(m->basename) + 2)
+                       name = talloc_asprintf(score, "CCAN_%s_H", m->basename);
+               else
+                       name = talloc_asprintf(score, "CCAN_%s_%s_H",
+                                              m->basename, e->file->name);
+               fix_name(name);
+
+               q = talloc_asprintf(score,
+                           "Should I wrap %s in #ifndef/#define %s for you?",
+                           e->file->name, name);
+               if (!ask(q))
+                       continue;
+
+               tmpname = maybe_temp_file(score, ".h", false, e->file->name);
+               out = fopen(tmpname, "w");
+               if (!out)
+                       err(1, "Opening %s", tmpname);
+               if (fprintf(out, "#ifndef %s\n#define %s\n", name, name) < 0)
+                       err(1, "Writing %s", tmpname);
+
+               for (i = 0; i < e->file->num_lines; i++)
+                       if (fprintf(out, "%s\n", e->file->lines[i]) < 0)
+                               err(1, "Writing %s", tmpname);
+
+               if (fprintf(out, "#endif /* %s */\n", name) < 0)
+                       err(1, "Writing %s", tmpname);
+               
+               if (fclose(out) != 0)
+                       err(1, "Closing %s", tmpname);
+
+               if (!move_file(tmpname, e->file->fullname))
+                       err(1, "Moving %s to %s", tmpname, e->file->fullname);
+       }
+}
+
+static bool check_idem(struct ccan_file *f, struct score *score)
 {
        struct line_info *line_info;
        unsigned int i, first_preproc_line;
 {
        struct line_info *line_info;
        unsigned int i, first_preproc_line;
@@ -31,44 +88,48 @@ static char *report_idem(struct ccan_file *f, char *sofar)
        line_info = get_ccan_line_info(f);
        if (f->num_lines < 3)
                /* FIXME: We assume small headers probably uninteresting. */
        line_info = get_ccan_line_info(f);
        if (f->num_lines < 3)
                /* FIXME: We assume small headers probably uninteresting. */
-               return sofar;
+               return true;
 
 
+       score->error = "Headers are not idempotent";
        for (i = 0; i < f->num_lines; i++) {
                if (line_info[i].type == DOC_LINE
                    || line_info[i].type == COMMENT_LINE)
                        continue;
        for (i = 0; i < f->num_lines; i++) {
                if (line_info[i].type == DOC_LINE
                    || line_info[i].type == COMMENT_LINE)
                        continue;
-               if (line_info[i].type == CODE_LINE)
-                       return talloc_asprintf_append(sofar,
-                             "%s:%u:expect first non-comment line to be #ifndef.\n", f->name, i+1);
-               else if (line_info[i].type == PREPROC_LINE)
+               if (line_info[i].type == CODE_LINE) {
+                       score_file_error(score, f, i+1,
+                                        "Expect first non-comment line to be"
+                                        " #ifndef.");
+                       return false;
+               } else if (line_info[i].type == PREPROC_LINE)
                        break;
        }
 
        /* No code at all?  Don't complain. */
        if (i == f->num_lines)
                        break;
        }
 
        /* No code at all?  Don't complain. */
        if (i == f->num_lines)
-               return sofar;
+               return true;
 
        first_preproc_line = i;
        for (i = first_preproc_line+1; i < f->num_lines; i++) {
                if (line_info[i].type == DOC_LINE
                    || line_info[i].type == COMMENT_LINE)
                        continue;
 
        first_preproc_line = i;
        for (i = first_preproc_line+1; i < f->num_lines; i++) {
                if (line_info[i].type == DOC_LINE
                    || line_info[i].type == COMMENT_LINE)
                        continue;
-               if (line_info[i].type == CODE_LINE)
-                       return talloc_asprintf_append(sofar,
-                             "%s:%u:expect second line to be #define.\n", f->name, i+1);
-               else if (line_info[i].type == PREPROC_LINE)
+               if (line_info[i].type == CODE_LINE) {
+                       score_file_error(score, f, i+1,
+                                        "Expect second non-comment line to be"
+                                        " #define.");
+                       return false;
+               } else if (line_info[i].type == PREPROC_LINE)
                        break;
        }
 
        /* No code at all?  Weird. */
        if (i == f->num_lines)
                        break;
        }
 
        /* No code at all?  Weird. */
        if (i == f->num_lines)
-               return sofar;
+               return true;
 
        /* We expect a condition on this line. */
        if (!line_info[i].cond) {
 
        /* We expect a condition on this line. */
        if (!line_info[i].cond) {
-               return talloc_asprintf_append(sofar,
-                                             "%s:%u:expected #ifndef.\n",
-                                             f->name, first_preproc_line+1);
+               score_file_error(score, f, i+1, "Expected #ifndef");
+               return false;
        }
 
        line = f->lines[i];
        }
 
        line = f->lines[i];
@@ -76,24 +137,27 @@ static char *report_idem(struct ccan_file *f, char *sofar)
        /* We expect the condition to be ! IFDEF <symbol>. */
        if (line_info[i].cond->type != PP_COND_IFDEF
            || !line_info[i].cond->inverse) {
        /* We expect the condition to be ! IFDEF <symbol>. */
        if (line_info[i].cond->type != PP_COND_IFDEF
            || !line_info[i].cond->inverse) {
-               return talloc_asprintf_append(sofar,
-                                             "%s:%u:expected #ifndef.\n",
-                                             f->name, first_preproc_line+1);
+               score_file_error(score, f, i+1, "Expected #ifndef");
+               return false;
        }
 
        /* And this to be #define <symbol> */
        if (!get_token(&line, "#"))
                abort();
        if (!get_token(&line, "define")) {
        }
 
        /* And this to be #define <symbol> */
        if (!get_token(&line, "#"))
                abort();
        if (!get_token(&line, "define")) {
-               return talloc_asprintf_append(sofar,
-                             "%s:%u:expected '#define %s'.\n",
-                             f->name, i+1, line_info[i].cond->symbol);
+               char *str = talloc_asprintf(score,
+                                           "expected '#define %s'",
+                                           line_info[i].cond->symbol);
+               score_file_error(score, f, i+1, str);
+               return false;
        }
        sym = get_symbol_token(f, &line);
        if (!sym || !streq(sym, line_info[i].cond->symbol)) {
        }
        sym = get_symbol_token(f, &line);
        if (!sym || !streq(sym, line_info[i].cond->symbol)) {
-               return talloc_asprintf_append(sofar,
-                             "%s:%u:expected '#define %s'.\n",
-                             f->name, i+1, line_info[i].cond->symbol);
+               char *str = talloc_asprintf(score,
+                                           "expected '#define %s'",
+                                           line_info[i].cond->symbol);
+               score_file_error(score, f, i+1, str);
+               return false;
        }
 
        /* Rest of code should all be covered by that conditional. */
        }
 
        /* Rest of code should all be covered by that conditional. */
@@ -103,42 +167,35 @@ static char *report_idem(struct ccan_file *f, char *sofar)
                    || line_info[i].type == COMMENT_LINE)
                        continue;
                if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
                    || line_info[i].type == COMMENT_LINE)
                        continue;
                if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
-                   != NOT_COMPILED)
-                       return talloc_asprintf_append(sofar,
-                             "%s:%u:code outside idempotent region.\n",
-                             f->name, i+1);
+                   != NOT_COMPILED) {
+                       score_file_error(score, f, i+1, "code outside"
+                                        " idempotent region");
+                       return false;
+               }
        }
 
        }
 
-       return sofar;
+       return true;
 }
 
 }
 
-static void *check_idempotent(struct manifest *m,
-                             bool keep,
-                             unsigned int *timeleft)
+static void check_idempotent(struct manifest *m,
+                            bool keep,
+                            unsigned int *timeleft, struct score *score)
 {
        struct ccan_file *f;
 {
        struct ccan_file *f;
-       char *report = NULL;
-
-       list_for_each(&m->h_files, f, list)
-               report = report_idem(f, report);
 
 
-       return report;
-}
-
-static const char *describe_idempotent(struct manifest *m, void *check_result)
-{
-       return talloc_asprintf(check_result, 
-                              "Some headers not idempotent:\n"
-                              "%s\n%s", (char *)check_result,
-                              explain);
+       list_for_each(&m->h_files, f, list) {
+               if (!check_idem(f, score))
+                       return;
+       }
+       score->pass = true;
+       score->score = score->total;
 }
 
 struct ccanlint idempotent = {
        .key = "idempotent",
        .name = "Module headers are #ifndef/#define wrapped",
 }
 
 struct ccanlint idempotent = {
        .key = "idempotent",
        .name = "Module headers are #ifndef/#define wrapped",
-       .total_score = 1,
        .check = check_idempotent,
        .check = check_idempotent,
-       .describe = describe_idempotent,
+       .handle = handle_idem,
 };
 
 REGISTER_TEST(idempotent, NULL);
 };
 
 REGISTER_TEST(idempotent, NULL);
index a1bd8dea59f79a4a145d16594408182f66fe01b4..8e756c7db732faed28d89028c6c9d0b7a2afb48b 100644 (file)
@@ -4,6 +4,7 @@
 #include <ccan/str_talloc/str_talloc.h>
 #include <ccan/grab_file/grab_file.h>
 #include <ccan/str/str.h>
 #include <ccan/str_talloc/str_talloc.h>
 #include <ccan/grab_file/grab_file.h>
 #include <ccan/str/str.h>
+#include <ccan/foreach/foreach.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <string.h>
 #include <ctype.h>
 
 #include <string.h>
 #include <ctype.h>
 
-struct coverage_result {
-       float uncovered;
-       const char *what;
-       char *output;
-};
-
-static bool find_source_file(struct manifest *m, const char *filename)
+static bool find_source_file(const struct manifest *m, const char *filename)
 {
 {
-       struct ccan_file *i;
+       const struct ccan_file *i;
 
        list_for_each(&m->c_files, i, list) {
                if (streq(i->fullname, filename))
 
        list_for_each(&m->c_files, i, list) {
                if (streq(i->fullname, filename))
@@ -37,12 +32,25 @@ static bool find_source_file(struct manifest *m, const char *filename)
        return false;
 }
 
        return false;
 }
 
+/* 1 point for 50%, 2 points for 75%, 3 points for 87.5%... */
+static unsigned int score_coverage(float covered, unsigned total)
+{
+       float thresh, uncovered = 1.0 - covered;
+       unsigned int i;
+
+       for (i = 0, thresh = 0.5; i < total; i++, thresh /= 2) {
+               if (uncovered > thresh)
+                       break;
+       }
+       return i;
+}
+
+
 /* FIXME: Don't know how stable this is.  Read cov files directly? */
 /* FIXME: Don't know how stable this is.  Read cov files directly? */
-static void analyze_coverage(struct manifest *m,
-                            struct coverage_result *res, const char *output,
-                            bool full_gcov)
+static void analyze_coverage(struct manifest *m, bool full_gcov,
+                            const char *output, struct score *score)
 {
 {
-       char **lines = strsplit(res, output, "\n", NULL);
+       char **lines = strsplit(score, output, "\n", NULL);
        float covered_lines = 0.0;
        unsigned int i, total_lines = 0;
        bool lines_matter = false;
        float covered_lines = 0.0;
        unsigned int i, total_lines = 0;
        bool lines_matter = false;
@@ -81,17 +89,14 @@ static void analyze_coverage(struct manifest *m,
                        apostrophe = strchr(filename, '\'');
                        *apostrophe = '\0';
                        if (lines_matter) {
                        apostrophe = strchr(filename, '\'');
                        *apostrophe = '\0';
                        if (lines_matter) {
-                               file = grab_file(res, filename, NULL);
+                               file = grab_file(score, filename, NULL);
                                if (!file) {
                                if (!file) {
-                                       res->what = talloc_asprintf(res,
+                                       score->error = talloc_asprintf(score,
                                                            "Reading %s",
                                                            filename);
                                                            "Reading %s",
                                                            filename);
-                                       res->output = talloc_strdup(res,
-                                                           strerror(errno));
                                        return;
                                }
                                        return;
                                }
-                               res->output = talloc_append_string(res->output,
-                                                                  file);
+                               printf("%s", file);
                        }
                        if (tools_verbose)
                                printf("Unlinking %s", filename);
                        }
                        if (tools_verbose)
                                printf("Unlinking %s", filename);
@@ -99,114 +104,65 @@ static void analyze_coverage(struct manifest *m,
                }
        }
 
                }
        }
 
+       score->pass = true;
+
        /* Nothing covered?  We can't tell if there's a source file which
         * was never executed, or there really is no code to execute, so
         * assume the latter: this test deserves no score. */
        /* Nothing covered?  We can't tell if there's a source file which
         * was never executed, or there really is no code to execute, so
         * assume the latter: this test deserves no score. */
-       if (total_lines == 0) {
-               res->uncovered = 1.0;
-               run_coverage_tests.total_score = 0;
-       } else
-               res->uncovered = 1.0 - covered_lines / total_lines;
+       if (total_lines == 0)
+               score->total = score->score = 0;
+       else {
+               score->total = 5;
+               score->score = score_coverage(covered_lines / total_lines,
+                                             score->total);
+       }
 }
 
 }
 
-static void *do_run_coverage_tests(struct manifest *m,
-                                  bool keep,
-                                  unsigned int *timeleft)
+static void do_run_coverage_tests(struct manifest *m,
+                                 bool keep,
+                                 unsigned int *timeleft, struct score *score)
 {
 {
-       struct coverage_result *res;
        struct ccan_file *i;
        char *cmdout;
        char *covcmd;
        bool ok;
        bool full_gcov = (verbose > 1);
        struct ccan_file *i;
        char *cmdout;
        char *covcmd;
        bool ok;
        bool full_gcov = (verbose > 1);
-
-       res = talloc(m, struct coverage_result);
-       res->what = NULL;
-       res->output = talloc_strdup(res, "");
-       res->uncovered = 1.0;
+       struct list_head *list;
 
        /* This tells gcov where we put those .gcno files. */
        covcmd = talloc_asprintf(m, "gcov %s -o %s",
                                 full_gcov ? "" : "-n",
 
        /* This tells gcov where we put those .gcno files. */
        covcmd = talloc_asprintf(m, "gcov %s -o %s",
                                 full_gcov ? "" : "-n",
-                                talloc_dirname(res, m->info_file->compiled));
+                                talloc_dirname(score, m->info_file->compiled));
 
        /* Run them all. */
 
        /* Run them all. */
-       list_for_each(&m->run_tests, i, list) {
-               cmdout = run_command(m, timeleft, i->cov_compiled);
-               if (cmdout) {
-                       res->what = i->fullname;
-                       res->output = talloc_steal(res, cmdout);
-                       return res;
-               }
-               covcmd = talloc_asprintf_append(covcmd, " %s", i->fullname);
-       }
-
-       list_for_each(&m->api_tests, i, list) {
-               cmdout = run_command(m, timeleft, i->cov_compiled);
-               if (cmdout) {
-                       res->what = i->fullname;
-                       res->output = talloc_steal(res, cmdout);
-                       return res;
+       foreach_ptr(list, &m->run_tests, &m->api_tests) {
+               list_for_each(list, i, list) {
+                       cmdout = run_command(m, timeleft, i->cov_compiled);
+                       if (cmdout) {
+                               score->error = "Running test with coverage";
+                               score_file_error(score, i, 0, cmdout);
+                               return;
+                       }
+                       covcmd = talloc_asprintf_append(covcmd, " %s",
+                                                       i->fullname);
                }
                }
-               covcmd = talloc_asprintf_append(covcmd, " %s", i->fullname);
        }
 
        /* Now run gcov: we want output even if it succeeds. */
        cmdout = run_with_timeout(m, covcmd, &ok, timeleft);
        if (!ok) {
        }
 
        /* Now run gcov: we want output even if it succeeds. */
        cmdout = run_with_timeout(m, covcmd, &ok, timeleft);
        if (!ok) {
-               res->what = "Running gcov";
-               res->output = talloc_steal(res, cmdout);
-               return res;
-       }
-
-       analyze_coverage(m, res, cmdout, full_gcov);
-
-       return res;
-}
-
-/* 1 point for 50%, 2 points for 75%, 3 points for 87.5%... */
-static unsigned int score_coverage(struct manifest *m, void *check_result)
-{
-       struct coverage_result *res = check_result;
-       float thresh;
-       unsigned int i;
-
-       for (i = 0, thresh = 0.5;
-            i < run_coverage_tests.total_score;
-            i++, thresh /= 2) {
-               if (res->uncovered > thresh)
-                       break;
+               score->error = talloc_asprintf(score, "Running gcov: %s",
+                                              cmdout);
+               return;
        }
        }
-       return i;
-}
-
-static const char *describe_run_coverage_tests(struct manifest *m,
-                                              void *check_result)
-{
-       struct coverage_result *res = check_result;
-       bool full_gcov = (verbose > 1);
-       char *ret;
-
-       if (res->what)
-               return talloc_asprintf(m, "%s: %s", res->what, res->output);
-
-       if (!verbose)
-               return NULL;
 
 
-       ret = talloc_asprintf(m, "Tests achieved %0.2f%% coverage",
-                             (1.0 - res->uncovered) * 100);
-       if (full_gcov)
-               ret = talloc_asprintf_append(ret, "\n%s", res->output);
-       return ret;
+       analyze_coverage(m, full_gcov, cmdout, score);
 }
 
 struct ccanlint run_coverage_tests = {
        .key = "test-coverage",
        .name = "Code coverage of module tests",
 }
 
 struct ccanlint run_coverage_tests = {
        .key = "test-coverage",
        .name = "Code coverage of module tests",
-       .total_score = 5,
-       .score = score_coverage,
        .check = do_run_coverage_tests,
        .check = do_run_coverage_tests,
-       .describe = describe_run_coverage_tests,
 };
 
 REGISTER_TEST(run_coverage_tests, &compile_coverage_tests, &run_tests, NULL);
 };
 
 REGISTER_TEST(run_coverage_tests, &compile_coverage_tests, &run_tests, NULL);
index 559a637880915a0e6b39c7fcd789cc855eeab441..0a8434c6e5a35563bfd3eae06f61cbf0e7554c84 100644 (file)
@@ -2,6 +2,7 @@
 #include <tools/tools.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/str/str.h>
 #include <tools/tools.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/str/str.h>
+#include <ccan/foreach/foreach.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -21,91 +22,43 @@ static const char *can_run(struct manifest *m)
        return NULL;
 }
 
        return NULL;
 }
 
-struct run_tests_result {
-       struct list_node list;
-       struct ccan_file *file;
-       const char *output;
-};
-
-static void *do_run_tests(struct manifest *m,
-                         bool keep,
-                         unsigned int *timeleft)
+static void do_run_tests(struct manifest *m,
+                        bool keep,
+                        unsigned int *timeleft,
+                        struct score *score)
 {
 {
-       struct list_head *list = talloc(m, struct list_head);
-       struct run_tests_result *res;
+       struct list_head *list;
        struct ccan_file *i;
        char *cmdout;
 
        struct ccan_file *i;
        char *cmdout;
 
-       list_head_init(list);
-       run_tests.total_score = 0;
-
-       list_for_each(&m->run_tests, i, list) {
-               run_tests.total_score++;
-               cmdout = run_command(m, timeleft, i->compiled);
-               if (cmdout) {
-                       res = talloc(list, struct run_tests_result);
-                       res->file = i;
-                       res->output = talloc_steal(res, cmdout);
-                       list_add_tail(list, &res->list);
-               }
-       }
-
-       list_for_each(&m->api_tests, i, list) {
-               run_tests.total_score++;
-               cmdout = run_command(m, timeleft, i->compiled);
-               if (cmdout) {
-                       res = talloc(list, struct run_tests_result);
-                       res->file = i;
-                       res->output = talloc_steal(res, cmdout);
-                       list_add_tail(list, &res->list);
+       score->total = 0;
+       foreach_ptr(list, &m->run_tests, &m->api_tests) {
+               list_for_each(list, i, list) {
+                       score->total++;
+                       cmdout = run_command(m, timeleft, i->compiled);
+                       if (cmdout)
+                               score_file_error(score, i, 0, cmdout);
+                       else
+                               score->score++;
                }
        }
 
                }
        }
 
-       if (list_empty(list)) {
-               talloc_free(list);
-               list = NULL;
-       }
-
-       return list;
-}
-
-static unsigned int score_run_tests(struct manifest *m, void *check_result)
-{
-       struct list_head *list = check_result;
-       struct run_tests_result *i;
-       unsigned int score = run_tests.total_score;
-
-       list_for_each(list, i, list)
-               score--;
-       return score;
-}
-
-static const char *describe_run_tests(struct manifest *m,
-                                         void *check_result)
-{
-       struct list_head *list = check_result;
-       char *descrip = talloc_strdup(check_result, "Running tests failed:\n");
-       struct run_tests_result *i;
-
-       list_for_each(list, i, list)
-               descrip = talloc_asprintf_append(descrip, "Running %s:\n%s",
-                                                i->file->name, i->output);
-       return descrip;
+       if (score->score == score->total)
+               score->pass = true;
 }
 
 /* Gcc's warn_unused_result is fascist bullshit. */
 #define doesnt_matter()
 
 }
 
 /* Gcc's warn_unused_result is fascist bullshit. */
 #define doesnt_matter()
 
-static void run_under_debugger(struct manifest *m, void *check_result)
+static void run_under_debugger(struct manifest *m, struct score *score)
 {
        char *command;
 {
        char *command;
-       struct list_head *list = check_result;
-       struct run_tests_result *first;
+       struct file_error *first;
 
        if (!ask("Should I run the first failing test under the debugger?"))
                return;
 
 
        if (!ask("Should I run the first failing test under the debugger?"))
                return;
 
-       first = list_top(list, struct run_tests_result, list);
+       first = list_top(&score->per_file_errors, struct file_error, list);
        command = talloc_asprintf(m, "gdb -ex 'break tap.c:136' -ex 'run' %s",
                                  first->file->compiled);
        if (system(command))
        command = talloc_asprintf(m, "gdb -ex 'break tap.c:136' -ex 'run' %s",
                                  first->file->compiled);
        if (system(command))
@@ -115,12 +68,9 @@ static void run_under_debugger(struct manifest *m, void *check_result)
 struct ccanlint run_tests = {
        .key = "run",
        .name = "Module's run and api tests pass",
 struct ccanlint run_tests = {
        .key = "run",
        .name = "Module's run and api tests pass",
-       .score = score_run_tests,
-       .total_score = 1,
        .check = do_run_tests,
        .check = do_run_tests,
-       .describe = describe_run_tests,
+       .handle = run_under_debugger,
        .can_run = can_run,
        .can_run = can_run,
-       .handle = run_under_debugger
 };
 
 REGISTER_TEST(run_tests, &compile_tests, NULL);
 };
 
 REGISTER_TEST(run_tests, &compile_tests, NULL);
index caf8323c2cfa26cf53257d226f698e69ac6a9a6c..e0c2ab9197db29d8fbe60c2194d1cbbd9e68e4d1 100644 (file)
@@ -2,6 +2,7 @@
 #include <tools/tools.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/str/str.h>
 #include <tools/tools.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/str/str.h>
+#include <ccan/foreach/foreach.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 static const char *can_run_vg(struct manifest *m)
 {
        unsigned int timeleft = default_timeout_ms;
 static const char *can_run_vg(struct manifest *m)
 {
        unsigned int timeleft = default_timeout_ms;
-       char *output = run_command(m, &timeleft, "valgrind -q --error-exitcode=0 true");
+       char *output = run_command(m, &timeleft,
+                                  "valgrind -q --error-exitcode=0 true");
 
        if (output)
                return talloc_asprintf(m, "No valgrind support: %s", output);
        return NULL;
 }
 
 
        if (output)
                return talloc_asprintf(m, "No valgrind support: %s", output);
        return NULL;
 }
 
-struct run_tests_result {
-       struct list_node list;
-       struct ccan_file *file;
-       const char *output;
-};
-
-static void *do_run_tests_vg(struct manifest *m,
+/* FIXME: Run examples, too! */
+static void do_run_tests_vg(struct manifest *m,
                             bool keep,
                             bool keep,
-                            unsigned int *timeleft)
+                           unsigned int *timeleft,
+                           struct score *score)
 {
 {
-       struct list_head *list = talloc(m, struct list_head);
-       struct run_tests_result *res;
        struct ccan_file *i;
        struct ccan_file *i;
+       struct list_head *list;
        char *cmdout;
 
        char *cmdout;
 
-       list_head_init(list);
-       run_tests_vg.total_score = 0;
-
-       list_for_each(&m->run_tests, i, list) {
-               run_tests_vg.total_score++;
-               cmdout = run_command(m, timeleft,
-                                    "valgrind -q --error-exitcode=100 %s",
-                                    i->compiled);
-               if (cmdout) {
-                       res = talloc(list, struct run_tests_result);
-                       res->file = i;
-                       res->output = talloc_steal(res, cmdout);
-                       list_add_tail(list, &res->list);
-               }
-       }
-
-       list_for_each(&m->api_tests, i, list) {
-               run_tests_vg.total_score++;
-               cmdout = run_command(m, timeleft,
+       score->total = 0;
+       foreach_ptr(list, &m->run_tests, &m->api_tests) {
+               list_for_each(list, i, list) {
+                       score->total++;
+                       cmdout = run_command(m, timeleft,
                                     "valgrind -q --error-exitcode=100 %s",
                                     i->compiled);
                                     "valgrind -q --error-exitcode=100 %s",
                                     i->compiled);
-               if (cmdout) {
-                       res = talloc(list, struct run_tests_result);
-                       res->file = i;
-                       res->output = talloc_steal(res, cmdout);
-                       list_add_tail(list, &res->list);
+                       if (cmdout) {
+                               score->error = "Running under valgrind";
+                               score_file_error(score, i, 0, cmdout);
+                       } else
+                               score->score++;
                }
        }
 
                }
        }
 
-       if (list_empty(list)) {
-               talloc_free(list);
-               list = NULL;
-       }
-
-       return list;
-}
-
-static unsigned int score_run_tests_vg(struct manifest *m, void *check_result)
-{
-       struct list_head *list = check_result;
-       struct run_tests_result *i;
-       unsigned int score = run_tests_vg.total_score;
-
-       list_for_each(list, i, list)
-               score--;
-       return score;
-}
-
-static const char *describe_run_tests_vg(struct manifest *m,
-                                        void *check_result)
-{
-       struct list_head *list = check_result;
-       char *descrip = talloc_strdup(check_result, "Running tests under valgrind failed:\n");
-       struct run_tests_result *i;
-
-       list_for_each(list, i, list)
-               descrip = talloc_asprintf_append(descrip, "Running %s:\n%s",
-                                                i->file->name, i->output);
-       return descrip;
+       if (score->score == score->total)
+               score->pass = true;
 }
 
 /* Gcc's warn_unused_result is fascist bullshit. */
 #define doesnt_matter()
 
 }
 
 /* Gcc's warn_unused_result is fascist bullshit. */
 #define doesnt_matter()
 
-static void run_under_debugger_vg(struct manifest *m, void *check_result)
+static void run_under_debugger_vg(struct manifest *m, struct score *score)
 {
 {
-       struct list_head *list = check_result;
-       struct run_tests_result *first;
+       struct file_error *first;
        char *command;
 
        if (!ask("Should I run the first failing test under the debugger?"))
                return;
 
        char *command;
 
        if (!ask("Should I run the first failing test under the debugger?"))
                return;
 
-       first = list_top(list, struct run_tests_result, list);
+       first = list_top(&score->per_file_errors, struct file_error, list);
        command = talloc_asprintf(m, "valgrind --db-attach=yes %s",
                                  first->file->compiled);
        if (system(command))
        command = talloc_asprintf(m, "valgrind --db-attach=yes %s",
                                  first->file->compiled);
        if (system(command))
@@ -123,11 +77,8 @@ static void run_under_debugger_vg(struct manifest *m, void *check_result)
 struct ccanlint run_tests_vg = {
        .key = "valgrind-tests",
        .name = "Module's run and api tests succeed under valgrind",
 struct ccanlint run_tests_vg = {
        .key = "valgrind-tests",
        .name = "Module's run and api tests succeed under valgrind",
-       .score = score_run_tests_vg,
-       .total_score = 1,
-       .check = do_run_tests_vg,
-       .describe = describe_run_tests_vg,
        .can_run = can_run_vg,
        .can_run = can_run_vg,
+       .check = do_run_tests_vg,
        .handle = run_under_debugger_vg
 };
 
        .handle = run_under_debugger_vg
 };
 
index 439fc06d1b67907bf5096a2c4457afb89a533a6a..2d344435e9ff908233f9d82babb08a801e5fa4ca 100644 (file)
@@ -1,10 +1,11 @@
 /* Trailing whitespace test.  Almost embarrassing, but trivial. */
 #include <tools/ccanlint/ccanlint.h>
 #include <ccan/talloc/talloc.h>
 /* Trailing whitespace test.  Almost embarrassing, but trivial. */
 #include <tools/ccanlint/ccanlint.h>
 #include <ccan/talloc/talloc.h>
+#include <ccan/foreach/foreach.h>
 #include <ccan/str/str.h>
 
 /* FIXME: only print full analysis if verbose >= 2.  */
 #include <ccan/str/str.h>
 
 /* FIXME: only print full analysis if verbose >= 2.  */
-static char *report_on_trailing_whitespace(const char *line)
+static char *get_trailing_whitespace(const char *line)
 {
        const char *e = strchr(line, 0);
        while (e>line && (e[-1]==' ' || e[-1]=='\t'))
 {
        const char *e = strchr(line, 0);
        while (e>line && (e[-1]==' ' || e[-1]=='\t'))
@@ -20,36 +21,38 @@ static char *report_on_trailing_whitespace(const char *line)
        return talloc_asprintf(line, "'%s'", line);
 }
 
        return talloc_asprintf(line, "'%s'", line);
 }
 
-static void *check_trailing_whitespace(struct manifest *m,
-                                      bool keep,
-                                      unsigned int *timeleft)
+static void check_trailing_whitespace(struct manifest *m,
+                                     bool keep,
+                                     unsigned int *timeleft,
+                                     struct score *score)
 {
 {
-       char *report;
-
-       report = report_on_lines(&m->c_files, report_on_trailing_whitespace,
-                                NULL);
-       report = report_on_lines(&m->h_files, report_on_trailing_whitespace,
-                                report);
-
-       return report;
-}
-
-static const char *describe_trailing_whitespace(struct manifest *m,
-                                               void *check_result)
-{
-       if (!verbose)
-               return NULL;
-       return talloc_asprintf(check_result, 
-                              "Some source files have trailing whitespace:\n"
-                              "%s", (char *)check_result);
+       struct list_head *list;
+       struct ccan_file *f;
+       unsigned int i;
+
+       foreach_ptr(list, &m->c_files, &m->h_files) {
+               list_for_each(list, f, list) {
+                       char **lines = get_ccan_file_lines(f);
+                       for (i = 0; i < f->num_lines; i++) {
+                               char *err = get_trailing_whitespace(lines[i]);
+                               if (err) {
+                                       score->error = "Trailing whitespace"
+                                               "found";
+                                       score_file_error(score, f, i+1, err);
+                               }
+                       }
+               }
+       }
+       if (!score->error) {
+               score->pass = true;
+               score->score = score->total;
+       }
 }
 
 struct ccanlint trailing_whitespace = {
        .key = "trailing-whitespace",
        .name = "Module's source code has no trailing whitespace",
 }
 
 struct ccanlint trailing_whitespace = {
        .key = "trailing-whitespace",
        .name = "Module's source code has no trailing whitespace",
-       .total_score = 1,
        .check = check_trailing_whitespace,
        .check = check_trailing_whitespace,
-       .describe = describe_trailing_whitespace,
 };
 
 
 };