ccanlint: rename files to match keys
authorRusty Russell <rusty@rustcorp.com.au>
Fri, 7 Jan 2011 11:50:13 +0000 (22:20 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Fri, 7 Jan 2011 11:50:13 +0000 (22:20 +1030)
40 files changed:
tools/ccanlint/compulsory_tests/build.c [deleted file]
tools/ccanlint/compulsory_tests/build_objs.c [deleted file]
tools/ccanlint/compulsory_tests/check_build.c [deleted file]
tools/ccanlint/compulsory_tests/check_depends_built.c [deleted file]
tools/ccanlint/compulsory_tests/check_depends_exist.c [deleted file]
tools/ccanlint/compulsory_tests/check_includes_build.c [deleted file]
tools/ccanlint/compulsory_tests/depends_build.c [new file with mode: 0644]
tools/ccanlint/compulsory_tests/depends_exist.c [new file with mode: 0644]
tools/ccanlint/compulsory_tests/has_info.c [deleted file]
tools/ccanlint/compulsory_tests/has_main_header.c [deleted file]
tools/ccanlint/compulsory_tests/info_exists.c [new file with mode: 0644]
tools/ccanlint/compulsory_tests/main_header_compiles.c [new file with mode: 0644]
tools/ccanlint/compulsory_tests/main_header_exists.c [new file with mode: 0644]
tools/ccanlint/compulsory_tests/module_builds.c [new file with mode: 0644]
tools/ccanlint/compulsory_tests/module_links.c [new file with mode: 0644]
tools/ccanlint/compulsory_tests/objects_build.c [new file with mode: 0644]
tools/ccanlint/tests/build-coverage.c [deleted file]
tools/ccanlint/tests/compile_test_helpers.c [deleted file]
tools/ccanlint/tests/compile_tests.c [deleted file]
tools/ccanlint/tests/examples_exist.c [new file with mode: 0644]
tools/ccanlint/tests/has_examples.c [deleted file]
tools/ccanlint/tests/has_info_documentation.c [deleted file]
tools/ccanlint/tests/has_tests.c [deleted file]
tools/ccanlint/tests/headers_idempotent.c [new file with mode: 0644]
tools/ccanlint/tests/idempotent.c [deleted file]
tools/ccanlint/tests/info_documentation_exists.c [new file with mode: 0644]
tools/ccanlint/tests/license.c [deleted file]
tools/ccanlint/tests/license_exists.c [new file with mode: 0644]
tools/ccanlint/tests/no_trailing_whitespace.c [new file with mode: 0644]
tools/ccanlint/tests/run-coverage.c [deleted file]
tools/ccanlint/tests/run_tests.c [deleted file]
tools/ccanlint/tests/run_tests_valgrind.c [deleted file]
tools/ccanlint/tests/tests_compile.c [new file with mode: 0644]
tools/ccanlint/tests/tests_compile_coverage.c [new file with mode: 0644]
tools/ccanlint/tests/tests_coverage.c [new file with mode: 0644]
tools/ccanlint/tests/tests_exist.c [new file with mode: 0644]
tools/ccanlint/tests/tests_helpers_compile.c [new file with mode: 0644]
tools/ccanlint/tests/tests_pass.c [new file with mode: 0644]
tools/ccanlint/tests/tests_pass_valgrind.c [new file with mode: 0644]
tools/ccanlint/tests/trailing_whitespace.c [deleted file]

diff --git a/tools/ccanlint/compulsory_tests/build.c b/tools/ccanlint/compulsory_tests/build.c
deleted file mode 100644 (file)
index 9d697a3..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-#include "build.h"
-
-static const char *can_build(struct manifest *m)
-{
-       if (safe_mode)
-               return "Safe mode enabled";
-       return NULL;
-}
-
-static char *obj_list(const struct manifest *m)
-{
-       char *list = talloc_strdup(m, "");
-       struct ccan_file *i;
-
-       /* Objects from all the C files. */
-       list_for_each(&m->c_files, i, list)
-               list = talloc_asprintf_append(list, "%s ", i->compiled);
-
-       return list;
-}
-
-char *build_module(struct manifest *m, bool keep, char **errstr)
-{
-       char *name = link_objects(m, m->basename, false, obj_list(m), errstr);
-       if (name) {
-               if (keep) {
-                       char *realname = talloc_asprintf(m, "%s.o", m->dir);
-                       /* We leave this object file around, all built. */
-                       if (!move_file(name, realname))
-                               err(1, "Renaming %s to %s", name, realname);
-                       name = realname;
-               }
-       }
-       return name;
-}
-
-static void do_build(struct manifest *m,
-                    bool keep,
-                    unsigned int *timeleft,
-                    struct score *score)
-{
-       char *errstr;
-
-       if (list_empty(&m->c_files)) {
-               /* No files?  No score, but we "pass". */
-               score->total = 0;
-               score->pass = true;
-               return;
-       }
-
-       m->compiled = build_module(m, keep, &errstr);
-       if (!m->compiled) {
-               score_file_error(score, NULL, 0, errstr);
-               return;
-       }
-
-       score->pass = true;
-       score->score = score->total;
-}
-
-struct ccanlint module_builds = {
-       .key = "module_builds",
-       .name = "Module can be built from object files",
-       .check = do_build,
-       .can_run = can_build,
-       .needs = "objects_build"
-};
-
-REGISTER_TEST(module_builds);
diff --git a/tools/ccanlint/compulsory_tests/build_objs.c b/tools/ccanlint/compulsory_tests/build_objs.c
deleted file mode 100644 (file)
index 13d34a1..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-static const char *can_build(struct manifest *m)
-{
-       if (safe_mode)
-               return "Safe mode enabled";
-       return NULL;
-}
-
-static void check_objs_build(struct manifest *m,
-                            bool keep,
-                            unsigned int *timeleft, struct score *score)
-{
-       struct ccan_file *i;
-       bool errors = false, warnings = false;
-
-       if (list_empty(&m->c_files))
-               score->total = 0;
-       else
-               score->total = 2;
-
-       list_for_each(&m->c_files, i, list) {
-               char *output;
-               char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
-
-               i->compiled = maybe_temp_file(m, "", keep, fullfile);
-               if (!compile_object(score, fullfile, ccan_dir, "", i->compiled,
-                                   &output)) {
-                       talloc_free(i->compiled);
-                       score->error = "Compiling object files";
-                       score_file_error(score, i, 0, output);
-                       errors = true;
-               } else if (!streq(output, "")) {
-                       score->error = "Compiling object files gave warnings";
-                       score_file_error(score, i, 0, output);
-                       warnings = true;
-               }
-       }
-
-       if (!errors) {
-               score->pass = true;
-               score->score = score->total - warnings;
-       }
-}
-
-struct ccanlint objects_build = {
-       .key = "objects_build",
-       .name = "Module object files can be built",
-       .check = check_objs_build,
-       .can_run = can_build,
-       .needs = "depends_exist"
-};
-
-REGISTER_TEST(objects_build);
diff --git a/tools/ccanlint/compulsory_tests/check_build.c b/tools/ccanlint/compulsory_tests/check_build.c
deleted file mode 100644 (file)
index 83b1c78..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-static const char *can_build(struct manifest *m)
-{
-       if (safe_mode)
-               return "Safe mode enabled";
-       return NULL;
-}
-
-static char *obj_list(const struct manifest *m)
-{
-       char *list = talloc_strdup(m, "");
-       struct manifest *i;
-
-       /* Other CCAN deps. */
-       list_for_each(&m->deps, i, list) {
-               if (i->compiled)
-                       list = talloc_asprintf_append(list, "%s ",
-                                                     i->compiled);
-       }
-       return list;
-}
-
-static char *lib_list(const struct manifest *m)
-{
-       unsigned int i, num;
-       char **libs = get_libs(m, ".", &num, &m->info_file->compiled);
-       char *ret = talloc_strdup(m, "");
-
-       for (i = 0; i < num; i++)
-               ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
-       return ret;
-}
-
-static void check_use_build(struct manifest *m,
-                           bool keep,
-                           unsigned int *timeleft, struct score *score)
-{
-       char *contents;
-       char *tmpfile, *cmdout;
-       char *basename = talloc_asprintf(m, "%s/example.c", m->dir);
-       int fd;
-
-       tmpfile = maybe_temp_file(m, ".c", keep, basename);
-
-       fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
-       if (fd < 0)
-               err(1, "Creating temporary file %s", tmpfile);
-
-       contents = talloc_asprintf(tmpfile,
-                                  "#include <ccan/%s/%s.h>\n"
-                                  "int main(void)\n"
-                                  "{\n"
-                                  "    return 0;\n"
-                                  "}\n",
-                                  m->basename, m->basename);
-       if (write(fd, contents, strlen(contents)) != strlen(contents))
-               err(1, "Failure writing to temporary file %s", tmpfile);
-       close(fd);
-
-       if (compile_and_link(score, tmpfile, ccan_dir, obj_list(m), "",
-                            lib_list(m),
-                            maybe_temp_file(m, "", keep, tmpfile),
-                            &cmdout)) {
-               score->pass = true;
-               score->score = score->total;
-       } else {
-               score->error = cmdout;
-       }
-}
-
-struct ccanlint module_links = {
-       .key = "module_links",
-       .name = "Module can be linked against trivial program",
-       .check = check_use_build,
-       .can_run = can_build,
-       .needs = "module_builds depends_build"
-};
-
-REGISTER_TEST(module_links);
diff --git a/tools/ccanlint/compulsory_tests/check_depends_built.c b/tools/ccanlint/compulsory_tests/check_depends_built.c
deleted file mode 100644 (file)
index bb1f6ca..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-#include "build.h"
-
-static const char *can_build(struct manifest *m)
-{
-       if (safe_mode)
-               return "Safe mode enabled";
-       return NULL;
-}
-
-static bool expect_obj_file(struct manifest *m)
-{
-       /* If it has C files, we expect an object file built from them. */
-       return !list_empty(&m->c_files);
-}
-
-static char *build_subdir_objs(struct manifest *m)
-{
-       struct ccan_file *i;
-
-       list_for_each(&m->c_files, i, list) {
-               char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
-               char *output;
-
-               i->compiled = maybe_temp_file(m, "", false, fullfile);
-               if (!compile_object(m, fullfile, ccan_dir, "", i->compiled,
-                                   &output)) {
-                       talloc_free(i->compiled);
-                       i->compiled = NULL;
-                       return talloc_asprintf(m,
-                                              "Dependency %s"
-                                              " did not build:\n%s",
-                                              m->basename, output);
-               }
-       }
-       return NULL;
-}
-
-char *build_submodule(struct manifest *m)
-{
-       char *errstr;
-       struct stat st;
-
-       if (m->compiled)
-               return NULL;
-
-       if (!expect_obj_file(m))
-               return NULL;
-
-       m->compiled = talloc_asprintf(m, "%s.o", m->dir);
-       if (stat(m->compiled, &st) == 0)
-               return NULL;
-
-       if (verbose >= 2)
-               printf("  Building dependency %s\n", m->dir);
-
-       errstr = build_subdir_objs(m);
-       if (errstr)
-               return errstr;
-
-       m->compiled = build_module(m, false, &errstr);
-       if (!m->compiled)
-               return errstr;
-       return NULL;
-}
-
-static void check_depends_built(struct manifest *m,
-                               bool keep,
-                               unsigned int *timeleft, struct score *score)
-{
-       struct manifest *i;
-
-       list_for_each(&m->deps, i, list) {
-               char *errstr = build_submodule(i);
-
-               if (errstr) {
-                       score->error = talloc_asprintf(score,
-                                                      "Dependency %s"
-                                                      " did not build:\n%s",
-                                                      i->basename, errstr);
-                       return;
-               }
-       }
-
-       score->pass = true;
-       score->score = score->total;
-}
-
-struct ccanlint depends_build = {
-       .key = "depends_build",
-       .name = "Module's CCAN dependencies can be found or built",
-       .check = check_depends_built,
-       .can_run = can_build,
-       .needs = "depends_exist"
-};
-
-REGISTER_TEST(depends_build);
diff --git a/tools/ccanlint/compulsory_tests/check_depends_exist.c b/tools/ccanlint/compulsory_tests/check_depends_exist.c
deleted file mode 100644 (file)
index 12eaf02..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-static bool add_dep(struct manifest *m, const char *dep, struct score *score)
-{
-       struct stat st;
-       struct manifest *subm;
-       char *dir = talloc_asprintf(m, "%s/%s", ccan_dir, dep);
-
-       /* FIXME: get_manifest has a tendency to exit. */
-       if (stat(dir, &st) != 0) {
-               score->error
-                       = talloc_asprintf(m,
-                                         "Could not stat dependency %s: %s",
-                                         dir, strerror(errno));
-               return false;
-       }
-       subm = get_manifest(m, dir);
-       list_add_tail(&m->deps, &subm->list);
-       return true;
-}
-
-static void check_depends_exist(struct manifest *m,
-                               bool keep,
-                               unsigned int *timeleft, struct score *score)
-{
-       unsigned int i;
-       char **deps;
-       char *updir = talloc_strdup(m, m->dir);
-
-       *strrchr(updir, '/') = '\0';
-
-       if (safe_mode)
-               deps = get_safe_ccan_deps(m, m->dir, true,
-                                         &m->info_file->compiled);
-       else
-               deps = get_deps(m, m->dir, true, &m->info_file->compiled);
-
-       for (i = 0; deps[i]; i++) {
-               if (!strstarts(deps[i], "ccan/"))
-                       continue;
-
-               if (!add_dep(m, deps[i], score))
-                       return;
-       }
-
-       /* We may need libtap for testing, unless we're "tap" */
-       if (!streq(m->basename, "tap")
-           && (!list_empty(&m->run_tests) || !list_empty(&m->api_tests))) {
-               if (!add_dep(m, "ccan/tap", score))
-                       return;
-       }
-
-       score->pass = true;
-       score->score = score->total;
-}
-
-struct ccanlint depends_exist = {
-       .key = "depends_exist",
-       .name = "Module's CCAN dependencies can be found",
-       .check = check_depends_exist,
-       .needs = "info_exists"
-};
-
-REGISTER_TEST(depends_exist);
diff --git a/tools/ccanlint/compulsory_tests/check_includes_build.c b/tools/ccanlint/compulsory_tests/check_includes_build.c
deleted file mode 100644 (file)
index 296b0a3..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/grab_file/grab_file.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-static const char *can_build(struct manifest *m)
-{
-       if (safe_mode)
-               return "Safe mode enabled";
-       return NULL;
-}
-
-static struct ccan_file *main_header(struct manifest *m)
-{
-       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 f;
-       }
-       /* Should not happen: we depend on has_main_header */
-       abort();
-}
-
-static void check_includes_build(struct manifest *m,
-                                bool keep,
-                                unsigned int *timeleft, struct score *score)
-{
-       char *contents;
-       char *tmpsrc, *tmpobj, *cmdout;
-       int fd;
-       struct ccan_file *mainh = main_header(m);
-
-       tmpsrc = maybe_temp_file(m, "-included.c", keep, mainh->fullname);
-       tmpobj = maybe_temp_file(m, ".o", keep, tmpsrc);
-
-       fd = open(tmpsrc, O_WRONLY | O_CREAT | O_EXCL, 0600);
-       if (fd < 0)
-               err(1, "Creating temporary file %s", tmpsrc);
-
-       contents = talloc_asprintf(tmpsrc, "#include <ccan/%s/%s.h>\n",
-                                  m->basename, m->basename);
-       if (write(fd, contents, strlen(contents)) != strlen(contents))
-               err(1, "writing to temporary file %s", tmpsrc);
-       close(fd);
-
-       if (compile_object(score, tmpsrc, ccan_dir, "", tmpobj, &cmdout)) {
-               score->pass = true;
-               score->score = score->total;
-       } else {
-               score->error = talloc_asprintf(score,
-                                      "#include of the main header file:\n%s",
-                                      cmdout);
-       }
-}
-
-struct ccanlint main_header_compiles = {
-       .key = "main_header_compiles",
-       .name = "Modules main header compiles",
-       .check = check_includes_build,
-       .can_run = can_build,
-       .needs = "depends_exist main_header_exists"
-};
-
-REGISTER_TEST(main_header_compiles);
diff --git a/tools/ccanlint/compulsory_tests/depends_build.c b/tools/ccanlint/compulsory_tests/depends_build.c
new file mode 100644 (file)
index 0000000..bb1f6ca
--- /dev/null
@@ -0,0 +1,111 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+#include "build.h"
+
+static const char *can_build(struct manifest *m)
+{
+       if (safe_mode)
+               return "Safe mode enabled";
+       return NULL;
+}
+
+static bool expect_obj_file(struct manifest *m)
+{
+       /* If it has C files, we expect an object file built from them. */
+       return !list_empty(&m->c_files);
+}
+
+static char *build_subdir_objs(struct manifest *m)
+{
+       struct ccan_file *i;
+
+       list_for_each(&m->c_files, i, list) {
+               char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
+               char *output;
+
+               i->compiled = maybe_temp_file(m, "", false, fullfile);
+               if (!compile_object(m, fullfile, ccan_dir, "", i->compiled,
+                                   &output)) {
+                       talloc_free(i->compiled);
+                       i->compiled = NULL;
+                       return talloc_asprintf(m,
+                                              "Dependency %s"
+                                              " did not build:\n%s",
+                                              m->basename, output);
+               }
+       }
+       return NULL;
+}
+
+char *build_submodule(struct manifest *m)
+{
+       char *errstr;
+       struct stat st;
+
+       if (m->compiled)
+               return NULL;
+
+       if (!expect_obj_file(m))
+               return NULL;
+
+       m->compiled = talloc_asprintf(m, "%s.o", m->dir);
+       if (stat(m->compiled, &st) == 0)
+               return NULL;
+
+       if (verbose >= 2)
+               printf("  Building dependency %s\n", m->dir);
+
+       errstr = build_subdir_objs(m);
+       if (errstr)
+               return errstr;
+
+       m->compiled = build_module(m, false, &errstr);
+       if (!m->compiled)
+               return errstr;
+       return NULL;
+}
+
+static void check_depends_built(struct manifest *m,
+                               bool keep,
+                               unsigned int *timeleft, struct score *score)
+{
+       struct manifest *i;
+
+       list_for_each(&m->deps, i, list) {
+               char *errstr = build_submodule(i);
+
+               if (errstr) {
+                       score->error = talloc_asprintf(score,
+                                                      "Dependency %s"
+                                                      " did not build:\n%s",
+                                                      i->basename, errstr);
+                       return;
+               }
+       }
+
+       score->pass = true;
+       score->score = score->total;
+}
+
+struct ccanlint depends_build = {
+       .key = "depends_build",
+       .name = "Module's CCAN dependencies can be found or built",
+       .check = check_depends_built,
+       .can_run = can_build,
+       .needs = "depends_exist"
+};
+
+REGISTER_TEST(depends_build);
diff --git a/tools/ccanlint/compulsory_tests/depends_exist.c b/tools/ccanlint/compulsory_tests/depends_exist.c
new file mode 100644 (file)
index 0000000..12eaf02
--- /dev/null
@@ -0,0 +1,78 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static bool add_dep(struct manifest *m, const char *dep, struct score *score)
+{
+       struct stat st;
+       struct manifest *subm;
+       char *dir = talloc_asprintf(m, "%s/%s", ccan_dir, dep);
+
+       /* FIXME: get_manifest has a tendency to exit. */
+       if (stat(dir, &st) != 0) {
+               score->error
+                       = talloc_asprintf(m,
+                                         "Could not stat dependency %s: %s",
+                                         dir, strerror(errno));
+               return false;
+       }
+       subm = get_manifest(m, dir);
+       list_add_tail(&m->deps, &subm->list);
+       return true;
+}
+
+static void check_depends_exist(struct manifest *m,
+                               bool keep,
+                               unsigned int *timeleft, struct score *score)
+{
+       unsigned int i;
+       char **deps;
+       char *updir = talloc_strdup(m, m->dir);
+
+       *strrchr(updir, '/') = '\0';
+
+       if (safe_mode)
+               deps = get_safe_ccan_deps(m, m->dir, true,
+                                         &m->info_file->compiled);
+       else
+               deps = get_deps(m, m->dir, true, &m->info_file->compiled);
+
+       for (i = 0; deps[i]; i++) {
+               if (!strstarts(deps[i], "ccan/"))
+                       continue;
+
+               if (!add_dep(m, deps[i], score))
+                       return;
+       }
+
+       /* We may need libtap for testing, unless we're "tap" */
+       if (!streq(m->basename, "tap")
+           && (!list_empty(&m->run_tests) || !list_empty(&m->api_tests))) {
+               if (!add_dep(m, "ccan/tap", score))
+                       return;
+       }
+
+       score->pass = true;
+       score->score = score->total;
+}
+
+struct ccanlint depends_exist = {
+       .key = "depends_exist",
+       .name = "Module's CCAN dependencies can be found",
+       .check = check_depends_exist,
+       .needs = "info_exists"
+};
+
+REGISTER_TEST(depends_exist);
diff --git a/tools/ccanlint/compulsory_tests/has_info.c b/tools/ccanlint/compulsory_tests/has_info.c
deleted file mode 100644 (file)
index a9a6c1c..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ccan/noerr/noerr.h>
-#include <ccan/talloc/talloc.h>
-
-static void check_has_info(struct manifest *m,
-                          bool keep,
-                          unsigned int *timeleft,
-                          struct score *score)
-{
-       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";
-       }
-}
-
-static const char template[] = 
-       "#include <string.h>\n"
-       "#include \"config.h\"\n"
-       "\n"
-       "/**\n"
-       " * %s - YOUR-ONE-LINE-DESCRIPTION-HERE\n"
-       " *\n"
-       " * This code ... YOUR-BRIEF-SUMMARY-HERE\n"
-       " *\n"
-       " * Example:\n"
-       " *     FULLY-COMPILABLE-INDENTED-TRIVIAL-BUT-USEFUL-EXAMPLE-HERE\n"
-       " */\n"
-       "int main(int argc, char *argv[])\n"
-       "{\n"
-       "       /* Expect exactly one argument */\n"
-       "       if (argc != 2)\n"
-       "               return 1;\n"
-       "\n"
-       "       if (strcmp(argv[1], \"depends\") == 0) {\n"
-       "               PRINTF-CCAN-PACKAGES-YOU-NEED-ONE-PER-LINE-IF-ANY\n"
-       "               return 0;\n"
-       "       }\n"
-       "\n"
-       "       return 1;\n"
-       "}\n";
-
-static void create_info_template(struct manifest *m, struct score *score)
-{
-       FILE *info;
-       const char *filename;
-
-       if (!ask("Should I create a template _info file for you?"))
-               return;
-
-       filename = talloc_asprintf(m, "%s/%s", m->dir, "_info");
-       info = fopen(filename, "w");
-       if (!info)
-               err(1, "Trying to create a template _info in %s", filename);
-
-       if (fprintf(info, template, m->basename) < 0) {
-               unlink_noerr(filename);
-               err(1, "Writing template into %s", filename);
-       }
-       fclose(info);
-}
-
-struct ccanlint info_exists = {
-       .key = "info_exists",
-       .name = "Module has _info file",
-       .check = check_has_info,
-       .handle = create_info_template,
-       .needs = ""
-};
-
-REGISTER_TEST(info_exists);
diff --git a/tools/ccanlint/compulsory_tests/has_main_header.c b/tools/ccanlint/compulsory_tests/has_main_header.c
deleted file mode 100644 (file)
index 68ea135..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <ccan/str/str.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, struct score *score)
-{
-       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) {
-                       score->pass = true;
-                       score->score = score->total;
-                       return;
-               } 
-       }
-       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",
-                                      m->basename, m->basename);
-}
-
-struct ccanlint main_header_exists = {
-       .key = "main_header_exists",
-       .name = "Module has main header file",
-       .check = check_has_main_header,
-       .needs = ""
-};
-
-REGISTER_TEST(main_header_exists);
diff --git a/tools/ccanlint/compulsory_tests/info_exists.c b/tools/ccanlint/compulsory_tests/info_exists.c
new file mode 100644 (file)
index 0000000..a9a6c1c
--- /dev/null
@@ -0,0 +1,85 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ccan/noerr/noerr.h>
+#include <ccan/talloc/talloc.h>
+
+static void check_has_info(struct manifest *m,
+                          bool keep,
+                          unsigned int *timeleft,
+                          struct score *score)
+{
+       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";
+       }
+}
+
+static const char template[] = 
+       "#include <string.h>\n"
+       "#include \"config.h\"\n"
+       "\n"
+       "/**\n"
+       " * %s - YOUR-ONE-LINE-DESCRIPTION-HERE\n"
+       " *\n"
+       " * This code ... YOUR-BRIEF-SUMMARY-HERE\n"
+       " *\n"
+       " * Example:\n"
+       " *     FULLY-COMPILABLE-INDENTED-TRIVIAL-BUT-USEFUL-EXAMPLE-HERE\n"
+       " */\n"
+       "int main(int argc, char *argv[])\n"
+       "{\n"
+       "       /* Expect exactly one argument */\n"
+       "       if (argc != 2)\n"
+       "               return 1;\n"
+       "\n"
+       "       if (strcmp(argv[1], \"depends\") == 0) {\n"
+       "               PRINTF-CCAN-PACKAGES-YOU-NEED-ONE-PER-LINE-IF-ANY\n"
+       "               return 0;\n"
+       "       }\n"
+       "\n"
+       "       return 1;\n"
+       "}\n";
+
+static void create_info_template(struct manifest *m, struct score *score)
+{
+       FILE *info;
+       const char *filename;
+
+       if (!ask("Should I create a template _info file for you?"))
+               return;
+
+       filename = talloc_asprintf(m, "%s/%s", m->dir, "_info");
+       info = fopen(filename, "w");
+       if (!info)
+               err(1, "Trying to create a template _info in %s", filename);
+
+       if (fprintf(info, template, m->basename) < 0) {
+               unlink_noerr(filename);
+               err(1, "Writing template into %s", filename);
+       }
+       fclose(info);
+}
+
+struct ccanlint info_exists = {
+       .key = "info_exists",
+       .name = "Module has _info file",
+       .check = check_has_info,
+       .handle = create_info_template,
+       .needs = ""
+};
+
+REGISTER_TEST(info_exists);
diff --git a/tools/ccanlint/compulsory_tests/main_header_compiles.c b/tools/ccanlint/compulsory_tests/main_header_compiles.c
new file mode 100644 (file)
index 0000000..296b0a3
--- /dev/null
@@ -0,0 +1,78 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/grab_file/grab_file.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *can_build(struct manifest *m)
+{
+       if (safe_mode)
+               return "Safe mode enabled";
+       return NULL;
+}
+
+static struct ccan_file *main_header(struct manifest *m)
+{
+       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 f;
+       }
+       /* Should not happen: we depend on has_main_header */
+       abort();
+}
+
+static void check_includes_build(struct manifest *m,
+                                bool keep,
+                                unsigned int *timeleft, struct score *score)
+{
+       char *contents;
+       char *tmpsrc, *tmpobj, *cmdout;
+       int fd;
+       struct ccan_file *mainh = main_header(m);
+
+       tmpsrc = maybe_temp_file(m, "-included.c", keep, mainh->fullname);
+       tmpobj = maybe_temp_file(m, ".o", keep, tmpsrc);
+
+       fd = open(tmpsrc, O_WRONLY | O_CREAT | O_EXCL, 0600);
+       if (fd < 0)
+               err(1, "Creating temporary file %s", tmpsrc);
+
+       contents = talloc_asprintf(tmpsrc, "#include <ccan/%s/%s.h>\n",
+                                  m->basename, m->basename);
+       if (write(fd, contents, strlen(contents)) != strlen(contents))
+               err(1, "writing to temporary file %s", tmpsrc);
+       close(fd);
+
+       if (compile_object(score, tmpsrc, ccan_dir, "", tmpobj, &cmdout)) {
+               score->pass = true;
+               score->score = score->total;
+       } else {
+               score->error = talloc_asprintf(score,
+                                      "#include of the main header file:\n%s",
+                                      cmdout);
+       }
+}
+
+struct ccanlint main_header_compiles = {
+       .key = "main_header_compiles",
+       .name = "Modules main header compiles",
+       .check = check_includes_build,
+       .can_run = can_build,
+       .needs = "depends_exist main_header_exists"
+};
+
+REGISTER_TEST(main_header_compiles);
diff --git a/tools/ccanlint/compulsory_tests/main_header_exists.c b/tools/ccanlint/compulsory_tests/main_header_exists.c
new file mode 100644 (file)
index 0000000..68ea135
--- /dev/null
@@ -0,0 +1,43 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <ccan/str/str.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, struct score *score)
+{
+       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) {
+                       score->pass = true;
+                       score->score = score->total;
+                       return;
+               } 
+       }
+       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",
+                                      m->basename, m->basename);
+}
+
+struct ccanlint main_header_exists = {
+       .key = "main_header_exists",
+       .name = "Module has main header file",
+       .check = check_has_main_header,
+       .needs = ""
+};
+
+REGISTER_TEST(main_header_exists);
diff --git a/tools/ccanlint/compulsory_tests/module_builds.c b/tools/ccanlint/compulsory_tests/module_builds.c
new file mode 100644 (file)
index 0000000..9d697a3
--- /dev/null
@@ -0,0 +1,84 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+#include "build.h"
+
+static const char *can_build(struct manifest *m)
+{
+       if (safe_mode)
+               return "Safe mode enabled";
+       return NULL;
+}
+
+static char *obj_list(const struct manifest *m)
+{
+       char *list = talloc_strdup(m, "");
+       struct ccan_file *i;
+
+       /* Objects from all the C files. */
+       list_for_each(&m->c_files, i, list)
+               list = talloc_asprintf_append(list, "%s ", i->compiled);
+
+       return list;
+}
+
+char *build_module(struct manifest *m, bool keep, char **errstr)
+{
+       char *name = link_objects(m, m->basename, false, obj_list(m), errstr);
+       if (name) {
+               if (keep) {
+                       char *realname = talloc_asprintf(m, "%s.o", m->dir);
+                       /* We leave this object file around, all built. */
+                       if (!move_file(name, realname))
+                               err(1, "Renaming %s to %s", name, realname);
+                       name = realname;
+               }
+       }
+       return name;
+}
+
+static void do_build(struct manifest *m,
+                    bool keep,
+                    unsigned int *timeleft,
+                    struct score *score)
+{
+       char *errstr;
+
+       if (list_empty(&m->c_files)) {
+               /* No files?  No score, but we "pass". */
+               score->total = 0;
+               score->pass = true;
+               return;
+       }
+
+       m->compiled = build_module(m, keep, &errstr);
+       if (!m->compiled) {
+               score_file_error(score, NULL, 0, errstr);
+               return;
+       }
+
+       score->pass = true;
+       score->score = score->total;
+}
+
+struct ccanlint module_builds = {
+       .key = "module_builds",
+       .name = "Module can be built from object files",
+       .check = do_build,
+       .can_run = can_build,
+       .needs = "objects_build"
+};
+
+REGISTER_TEST(module_builds);
diff --git a/tools/ccanlint/compulsory_tests/module_links.c b/tools/ccanlint/compulsory_tests/module_links.c
new file mode 100644 (file)
index 0000000..83b1c78
--- /dev/null
@@ -0,0 +1,94 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *can_build(struct manifest *m)
+{
+       if (safe_mode)
+               return "Safe mode enabled";
+       return NULL;
+}
+
+static char *obj_list(const struct manifest *m)
+{
+       char *list = talloc_strdup(m, "");
+       struct manifest *i;
+
+       /* Other CCAN deps. */
+       list_for_each(&m->deps, i, list) {
+               if (i->compiled)
+                       list = talloc_asprintf_append(list, "%s ",
+                                                     i->compiled);
+       }
+       return list;
+}
+
+static char *lib_list(const struct manifest *m)
+{
+       unsigned int i, num;
+       char **libs = get_libs(m, ".", &num, &m->info_file->compiled);
+       char *ret = talloc_strdup(m, "");
+
+       for (i = 0; i < num; i++)
+               ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
+       return ret;
+}
+
+static void check_use_build(struct manifest *m,
+                           bool keep,
+                           unsigned int *timeleft, struct score *score)
+{
+       char *contents;
+       char *tmpfile, *cmdout;
+       char *basename = talloc_asprintf(m, "%s/example.c", m->dir);
+       int fd;
+
+       tmpfile = maybe_temp_file(m, ".c", keep, basename);
+
+       fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
+       if (fd < 0)
+               err(1, "Creating temporary file %s", tmpfile);
+
+       contents = talloc_asprintf(tmpfile,
+                                  "#include <ccan/%s/%s.h>\n"
+                                  "int main(void)\n"
+                                  "{\n"
+                                  "    return 0;\n"
+                                  "}\n",
+                                  m->basename, m->basename);
+       if (write(fd, contents, strlen(contents)) != strlen(contents))
+               err(1, "Failure writing to temporary file %s", tmpfile);
+       close(fd);
+
+       if (compile_and_link(score, tmpfile, ccan_dir, obj_list(m), "",
+                            lib_list(m),
+                            maybe_temp_file(m, "", keep, tmpfile),
+                            &cmdout)) {
+               score->pass = true;
+               score->score = score->total;
+       } else {
+               score->error = cmdout;
+       }
+}
+
+struct ccanlint module_links = {
+       .key = "module_links",
+       .name = "Module can be linked against trivial program",
+       .check = check_use_build,
+       .can_run = can_build,
+       .needs = "module_builds depends_build"
+};
+
+REGISTER_TEST(module_links);
diff --git a/tools/ccanlint/compulsory_tests/objects_build.c b/tools/ccanlint/compulsory_tests/objects_build.c
new file mode 100644 (file)
index 0000000..13d34a1
--- /dev/null
@@ -0,0 +1,68 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *can_build(struct manifest *m)
+{
+       if (safe_mode)
+               return "Safe mode enabled";
+       return NULL;
+}
+
+static void check_objs_build(struct manifest *m,
+                            bool keep,
+                            unsigned int *timeleft, struct score *score)
+{
+       struct ccan_file *i;
+       bool errors = false, warnings = false;
+
+       if (list_empty(&m->c_files))
+               score->total = 0;
+       else
+               score->total = 2;
+
+       list_for_each(&m->c_files, i, list) {
+               char *output;
+               char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
+
+               i->compiled = maybe_temp_file(m, "", keep, fullfile);
+               if (!compile_object(score, fullfile, ccan_dir, "", i->compiled,
+                                   &output)) {
+                       talloc_free(i->compiled);
+                       score->error = "Compiling object files";
+                       score_file_error(score, i, 0, output);
+                       errors = true;
+               } else if (!streq(output, "")) {
+                       score->error = "Compiling object files gave warnings";
+                       score_file_error(score, i, 0, output);
+                       warnings = true;
+               }
+       }
+
+       if (!errors) {
+               score->pass = true;
+               score->score = score->total - warnings;
+       }
+}
+
+struct ccanlint objects_build = {
+       .key = "objects_build",
+       .name = "Module object files can be built",
+       .check = check_objs_build,
+       .can_run = can_build,
+       .needs = "depends_exist"
+};
+
+REGISTER_TEST(objects_build);
diff --git a/tools/ccanlint/tests/build-coverage.c b/tools/ccanlint/tests/build-coverage.c
deleted file mode 100644 (file)
index 115ae94..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-/* Note: we already test safe_mode in run_tests.c */
-static const char *can_run_coverage(struct manifest *m)
-{
-       unsigned int timeleft = default_timeout_ms;
-       char *output;
-
-       if (!run_command(m, &timeleft, &output, "gcov -h"))
-               return talloc_asprintf(m, "No gcov support: %s", output);
-       return NULL;
-}
-
-static bool build_module_objs_with_coverage(struct manifest *m, bool keep,
-                                           struct score *score,
-                                           char **modobjs)
-{
-       struct ccan_file *i;
-
-       *modobjs = talloc_strdup(m, "");
-       list_for_each(&m->c_files, i, list) {
-               char *err;
-               char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
-
-               i->cov_compiled = maybe_temp_file(m, "", keep, fullfile);
-               if (!compile_object(m, fullfile, ccan_dir, "",
-                                   i->cov_compiled, &err)) {
-                       score_file_error(score, i, 0, err);
-                       talloc_free(i->cov_compiled);
-                       i->cov_compiled = NULL;
-                       return false;
-               }
-               *modobjs = talloc_asprintf_append(*modobjs,
-                                                 " %s", i->cov_compiled);
-       }
-       return true;
-}
-
-/* FIXME: Merge this into one place. */
-static char *obj_list(const struct manifest *m, const char *modobjs)
-{
-       char *list = talloc_strdup(m, "");
-       struct ccan_file *i;
-       struct manifest *subm;
-
-       /* Objects from any other C files. */
-       list_for_each(&m->other_test_c_files, i, list)
-               list = talloc_asprintf_append(list, " %s", i->compiled);
-
-       if (modobjs)
-               list = talloc_append_string(list, modobjs);
-
-       /* Other ccan modules (don't need coverage versions of those). */
-       list_for_each(&m->deps, subm, list) {
-               if (subm->compiled)
-                       list = talloc_asprintf_append(list, " %s",
-                                                     subm->compiled);
-       }
-
-       return list;
-}
-
-static char *lib_list(const struct manifest *m)
-{
-       unsigned int i, num;
-       char **libs = get_libs(m, m->dir, &num, &m->info_file->compiled);
-       char *ret = talloc_strdup(m, "");
-
-       for (i = 0; i < num; i++)
-               ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
-       return ret;
-}
-
-static char *cov_compile(const void *ctx,
-                        struct manifest *m,
-                        struct ccan_file *file,
-                        const char *modobjs,
-                        bool keep)
-{
-       char *output;
-
-       file->cov_compiled = maybe_temp_file(ctx, "", keep, file->fullname);
-       if (!compile_and_link(ctx, file->fullname, ccan_dir,
-                             obj_list(m, modobjs),
-                             COVERAGE_CFLAGS,
-                             lib_list(m), file->cov_compiled, &output)) {
-               talloc_free(file->cov_compiled);
-               file->cov_compiled = NULL;
-               return output;
-       }
-       talloc_free(output);
-       return NULL;
-}
-
-/* FIXME: Coverage from testable examples as well. */
-static void do_compile_coverage_tests(struct manifest *m,
-                                     bool keep,
-                                     unsigned int *timeleft,
-                                     struct score *score)
-{
-       char *cmdout, *modobjs = NULL;
-       struct ccan_file *i;
-
-       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) {
-               cmdout = cov_compile(m, m, i, NULL, keep);
-               if (cmdout) {
-                       score->error = "Failed to compile test with coverage";
-                       score_file_error(score, i, 0, cmdout);
-               }
-       }
-
-       list_for_each(&m->api_tests, i, list) {
-               cmdout = cov_compile(m, m, i, modobjs, keep);
-               if (cmdout) {
-                       score->error = "Failed to compile test with coverage";
-                       score_file_error(score, i, 0, cmdout);
-               }
-       }
-       if (!score->error) {
-               score->pass = true;
-               score->score = score->total;
-       }
-}
-
-struct ccanlint tests_compile_coverage = {
-       .key = "tests_compile_coverage",
-       .name = "Module tests compile with " COVERAGE_CFLAGS,
-       .check = do_compile_coverage_tests,
-       .can_run = can_run_coverage,
-       .needs = "tests_compile"
-};
-
-REGISTER_TEST(tests_compile_coverage);
diff --git a/tools/ccanlint/tests/compile_test_helpers.c b/tools/ccanlint/tests/compile_test_helpers.c
deleted file mode 100644 (file)
index 0ad5a7e..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-static const char *can_run(struct manifest *m)
-{
-       if (safe_mode)
-               return "Safe mode enabled";
-       return NULL;
-}
-
-static bool compile(struct manifest *m,
-                   bool keep,
-                   struct ccan_file *cfile,
-                   char **output)
-{
-       cfile->compiled = maybe_temp_file(m, ".o", keep, cfile->fullname);
-       return compile_object(m, cfile->fullname, ccan_dir, "",
-                             cfile->compiled, output);
-}
-
-static void do_compile_test_helpers(struct manifest *m,
-                                   bool keep,
-                                   unsigned int *timeleft,
-                                   struct score *score)
-{
-       struct ccan_file *i;
-       bool errors = false, warnings = false;
-
-       if (list_empty(&m->other_test_c_files))
-               score->total = 0;
-       else
-               score->total = 2;
-
-       list_for_each(&m->other_test_c_files, i, list) {
-               char *cmdout;
-
-               if (!compile(m, keep, i, &cmdout)) {
-                       errors = true;
-                       score->error = "Failed to compile helper C files";
-                       score_file_error(score, i, 0, cmdout);
-               } else if (!streq(cmdout, "")) {
-                       warnings = true;
-                       score->error = "Helper C files gave warnings";
-                       score_file_error(score, i, 0, cmdout);
-               }
-       }
-
-       if (!errors) {
-               score->pass = true;
-               score->score = score->total - warnings;
-       }
-}
-
-struct ccanlint tests_helpers_compile = {
-       .key = "tests_helpers_compile",
-       .name = "Module test helper objects compile",
-       .check = do_compile_test_helpers,
-       .can_run = can_run,
-       .needs = "depends_build tests_exist"
-};
-
-REGISTER_TEST(tests_helpers_compile);
diff --git a/tools/ccanlint/tests/compile_tests.c b/tools/ccanlint/tests/compile_tests.c
deleted file mode 100644 (file)
index 4d86867..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-#include <tools/ccanlint/ccanlint.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 <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-static const char *can_build(struct manifest *m)
-{
-       if (safe_mode)
-               return "Safe mode enabled";
-       return NULL;
-}
-
-/* FIXME: Merge this into one place. */
-static char *obj_list(const struct manifest *m, bool link_with_module)
-{
-       char *list = talloc_strdup(m, "");
-       struct ccan_file *i;
-       struct manifest *subm;
-
-       /* Objects from any other C files. */
-       list_for_each(&m->other_test_c_files, i, list)
-               list = talloc_asprintf_append(list, " %s", i->compiled);
-
-       /* Our own object files. */
-       if (link_with_module)
-               list_for_each(&m->c_files, i, list)
-                       list = talloc_asprintf_append(list, " %s", i->compiled);
-
-       /* Other ccan modules. */
-       list_for_each(&m->deps, subm, list) {
-               if (subm->compiled)
-                       list = talloc_asprintf_append(list, " %s",
-                                                     subm->compiled);
-       }
-
-       return list;
-}
-
-static char *lib_list(const struct manifest *m)
-{
-       unsigned int i, num;
-       char **libs = get_libs(m, m->dir, &num, &m->info_file->compiled);
-       char *ret = talloc_strdup(m, "");
-
-       for (i = 0; i < num; i++)
-               ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
-       return ret;
-}
-
-static bool compile(const void *ctx,
-                   struct manifest *m,
-                   struct ccan_file *file,
-                   bool fail,
-                   bool link_with_module,
-                   bool keep, char **output)
-{
-       file->compiled = maybe_temp_file(ctx, "", keep, file->fullname);
-       if (!compile_and_link(ctx, file->fullname, ccan_dir,
-                             obj_list(m, link_with_module),
-                             fail ? "-DFAIL" : "",
-                             lib_list(m), file->compiled, output)) {
-               talloc_free(file->compiled);
-               return false;
-       }
-       return true;
-}
-
-static void do_compile_tests(struct manifest *m,
-                            bool keep,
-                            unsigned int *timeleft, struct score *score)
-{
-       char *cmdout;
-       struct ccan_file *i;
-       struct list_head *list;
-       bool errors = false, warnings = false;
-
-       foreach_ptr(list, &m->compile_ok_tests, &m->run_tests, &m->api_tests) {
-               list_for_each(list, i, list) {
-                       if (!compile(score, m, i, false, list == &m->api_tests,
-                                    keep, &cmdout)) {
-                               score->error = "Failed to compile tests";
-                               score_file_error(score, i, 0, cmdout);
-                               errors = true;
-                       } else if (!streq(cmdout, "")) {
-                               score->error = "Test compiled with warnings";
-                               score_file_error(score, i, 0, cmdout);
-                               warnings = true;
-                       }
-               }
-       }
-
-       /* The compile fail tests are a bit weird, handle them separately */
-       if (errors)
-               return;
-
-       /* For historical reasons, "fail" often means "gives warnings" */
-       list_for_each(&m->compile_fail_tests, i, list) {
-               if (!compile(score, m, i, false, false, false, &cmdout)) {
-                       score->error = "Failed to compile without -DFAIL";
-                       score_file_error(score, i, 0, cmdout);
-                       return;
-               }
-               if (!streq(cmdout, "")) {
-                       score->error = "Compile with warnigns without -DFAIL";
-                       score_file_error(score, i, 0, cmdout);
-                       return;
-               }
-               if (compile(score, m, i, true, false, false, &cmdout)
-                   && streq(cmdout, "")) {
-                       score->error = "Compiled successfully with -DFAIL?";
-                       score_file_error(score, i, 0, NULL);
-                       return;
-               }
-       }
-
-       score->pass = true;
-       score->total = 2;
-       score->score = 1 + !warnings;
-}
-
-struct ccanlint tests_compile = {
-       .key = "tests_compile",
-       .name = "Module tests compile",
-       .check = do_compile_tests,
-       .can_run = can_build,
-       .needs = "tests_helpers_compile objects_build"
-};
-
-REGISTER_TEST(tests_compile);
diff --git a/tools/ccanlint/tests/examples_exist.c b/tools/ccanlint/tests/examples_exist.c
new file mode 100644 (file)
index 0000000..947d76a
--- /dev/null
@@ -0,0 +1,119 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Creates and adds an example file. */
+static char *add_example(struct manifest *m, struct ccan_file *source,
+                        bool keep,
+                        struct doc_section *example)
+{
+       char *name;
+       unsigned int i;
+       int fd;
+       struct ccan_file *f;
+
+       name = talloc_asprintf(m, "%s/example-%s-%s.c",
+                              talloc_dirname(m,
+                                             source->fullname),
+                              source->name,
+                              example->function);
+       /* example->function == 'struct foo' */
+       while (strchr(name, ' '))
+               *strchr(name, ' ') = '_';
+
+       name = maybe_temp_file(m, ".c", keep, name);
+       f = new_ccan_file(m, talloc_dirname(m, name), talloc_basename(m, name));
+       talloc_steal(f, name);
+       list_add_tail(&m->examples, &f->list);
+
+       fd = open(f->fullname, O_WRONLY | O_CREAT | O_EXCL, 0600);
+       if (fd < 0)
+               return talloc_asprintf(m, "Creating temporary file %s: %s",
+                                      f->fullname, strerror(errno));
+
+       for (i = 0; i < example->num_lines; i++) {
+               if (write(fd, example->lines[i], strlen(example->lines[i]))
+                   != strlen(example->lines[i])
+                   || write(fd, "\n", 1) != 1) {
+                       close(fd);
+                       return "Failure writing to temporary file";
+               }
+       }
+       close(fd);
+       return NULL;
+}
+
+/* FIXME: We should have one example per function in header. */
+static void extract_examples(struct manifest *m,
+                            bool keep,
+                            unsigned int *timeleft,
+                            struct score *score)
+{
+       struct ccan_file *f, *mainh = NULL; /* gcc complains uninitialized */
+       struct doc_section *d;
+       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)
+                               return;
+                       have_info_example = true;
+               }
+       }
+
+       /* Check main header. */
+       list_for_each(&m->h_files, f, list) {
+               if (!strstarts(f->name, m->basename)
+                   || strlen(f->name) != strlen(m->basename) + 2)
+                       continue;
+
+               mainh = f;
+               list_for_each(get_ccan_file_docs(f), d, list) {
+                       if (streq(d->type, "example")) {
+                               score->error = add_example(m, f, keep, d);
+                               if (score->error)
+                                       return;
+                               have_header_example = true;
+                       }
+               }
+       }
+
+       if (have_info_example && have_header_example) {
+               score->score = score->total;
+               score->pass = true;
+               return;
+       }
+
+       score->error = "Expect examples in header and _info";
+       if (!have_info_example)
+               score_file_error(score, m->info_file, 0, "No Example: section");
+       if (!have_header_example)
+               score_file_error(score, mainh, 0, "No Example: section");
+
+       score->score = have_info_example + have_header_example;
+       /* We pass if we find any example. */
+       score->pass = score->score != 0;
+}
+
+struct ccanlint examples_exist = {
+       .key = "examples_exist",
+       .name = "_info and main header file have Example: sections",
+       .check = extract_examples,
+       .needs = "info_exists"
+};
+
+REGISTER_TEST(examples_exist);
diff --git a/tools/ccanlint/tests/has_examples.c b/tools/ccanlint/tests/has_examples.c
deleted file mode 100644 (file)
index 947d76a..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-/* Creates and adds an example file. */
-static char *add_example(struct manifest *m, struct ccan_file *source,
-                        bool keep,
-                        struct doc_section *example)
-{
-       char *name;
-       unsigned int i;
-       int fd;
-       struct ccan_file *f;
-
-       name = talloc_asprintf(m, "%s/example-%s-%s.c",
-                              talloc_dirname(m,
-                                             source->fullname),
-                              source->name,
-                              example->function);
-       /* example->function == 'struct foo' */
-       while (strchr(name, ' '))
-               *strchr(name, ' ') = '_';
-
-       name = maybe_temp_file(m, ".c", keep, name);
-       f = new_ccan_file(m, talloc_dirname(m, name), talloc_basename(m, name));
-       talloc_steal(f, name);
-       list_add_tail(&m->examples, &f->list);
-
-       fd = open(f->fullname, O_WRONLY | O_CREAT | O_EXCL, 0600);
-       if (fd < 0)
-               return talloc_asprintf(m, "Creating temporary file %s: %s",
-                                      f->fullname, strerror(errno));
-
-       for (i = 0; i < example->num_lines; i++) {
-               if (write(fd, example->lines[i], strlen(example->lines[i]))
-                   != strlen(example->lines[i])
-                   || write(fd, "\n", 1) != 1) {
-                       close(fd);
-                       return "Failure writing to temporary file";
-               }
-       }
-       close(fd);
-       return NULL;
-}
-
-/* FIXME: We should have one example per function in header. */
-static void extract_examples(struct manifest *m,
-                            bool keep,
-                            unsigned int *timeleft,
-                            struct score *score)
-{
-       struct ccan_file *f, *mainh = NULL; /* gcc complains uninitialized */
-       struct doc_section *d;
-       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)
-                               return;
-                       have_info_example = true;
-               }
-       }
-
-       /* Check main header. */
-       list_for_each(&m->h_files, f, list) {
-               if (!strstarts(f->name, m->basename)
-                   || strlen(f->name) != strlen(m->basename) + 2)
-                       continue;
-
-               mainh = f;
-               list_for_each(get_ccan_file_docs(f), d, list) {
-                       if (streq(d->type, "example")) {
-                               score->error = add_example(m, f, keep, d);
-                               if (score->error)
-                                       return;
-                               have_header_example = true;
-                       }
-               }
-       }
-
-       if (have_info_example && have_header_example) {
-               score->score = score->total;
-               score->pass = true;
-               return;
-       }
-
-       score->error = "Expect examples in header and _info";
-       if (!have_info_example)
-               score_file_error(score, m->info_file, 0, "No Example: section");
-       if (!have_header_example)
-               score_file_error(score, mainh, 0, "No Example: section");
-
-       score->score = have_info_example + have_header_example;
-       /* We pass if we find any example. */
-       score->pass = score->score != 0;
-}
-
-struct ccanlint examples_exist = {
-       .key = "examples_exist",
-       .name = "_info and main header file have Example: sections",
-       .check = extract_examples,
-       .needs = "info_exists"
-};
-
-REGISTER_TEST(examples_exist);
diff --git a/tools/ccanlint/tests/has_info_documentation.c b/tools/ccanlint/tests/has_info_documentation.c
deleted file mode 100644 (file)
index a5316fe..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/doc_extract.h>
-#include <tools/tools.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <ccan/str/str.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/noerr/noerr.h>
-#include <ccan/grab_file/grab_file.h>
-
-extern struct ccanlint info_documentation_exists;
-
-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;
-       char *oldcontents;
-
-       if (fd < 0 || !(new = fdopen(fd, "w")))
-               err(1, "Creating _info.new to insert documentation");
-
-       if (fprintf(new,
-                   "/**\n"
-                   " * %s - [[ONE LINE DESCRIPTION HERE]]\n"
-                   " *\n"
-                   " * Paragraphs why %s exists and where to use it.\n"
-                   " *\n"
-                   " * Followed by an Example: section with a standalone\n"
-                   " * (trivial and usually useless) program\n"
-                   " */\n", m->basename, m->basename) < 0) {
-               unlink_noerr("_info.new");
-               err(1, "Writing to _info.new to insert documentation");
-       }
-
-       oldcontents = grab_file(m, "_info", NULL);
-       if (!oldcontents) {
-               unlink_noerr("_info.new");
-               err(1, "Reading _info");
-       }
-       if (fprintf(new, "%s", oldcontents) < 0) {
-               unlink_noerr("_info.new");
-               err(1, "Appending _info to _info.new");
-       }
-       if (fclose(new) != 0) {
-               unlink_noerr("_info.new");
-               err(1, "Closing _info.new");
-       }
-       if (!move_file("_info.new", "_info")) {
-               unlink_noerr("_info.new");
-               err(1, "Renaming _info.new to _info");
-       }
-}
-
-static void check_info_documentation_exists(struct manifest *m,
-                                        bool keep,
-                                        unsigned int *timeleft,
-                                        struct score *score)
-{
-       struct list_head *infodocs = get_ccan_file_docs(m->info_file);
-       struct doc_section *d;
-       bool summary = false, description = false;
-
-       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"
-               "overall documentation belongs in the _info metafile.\n";
-               info_documentation_exists.handle = create_info_template_doc;
-       } 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"
-               "overall package\n";
-       }
-}
-
-struct ccanlint info_documentation_exists = {
-       .key = "info_documentation_exists",
-       .name = "Module has documentation in _info",
-       .check = check_info_documentation_exists,
-       .needs = "info_exists"
-};
-
-REGISTER_TEST(info_documentation_exists);
diff --git a/tools/ccanlint/tests/has_tests.c b/tools/ccanlint/tests/has_tests.c
deleted file mode 100644 (file)
index bdfe49d..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <ccan/talloc/talloc.h>
-
-extern struct ccanlint tests_exist;
-
-static void handle_no_tests(struct manifest *m, struct score *score)
-{
-       FILE *run;
-       struct ccan_file *i;
-       char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
-
-       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"
-       "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"
-       "directory, with the test directory symlinked under test/.\n\n"
-
-       "api tests are just like a run test, except it is a guarantee of API\n"
-       "stability: this test should pass on all future versions of the\n"
-       "module.  They *are* linked to the module, since they should only\n"
-       "test the API, not the internal state.\n\n"
-
-       "compile_ok tests are a subset of run tests: they must compile and\n"
-       "link, but aren't run.\n\n"
-
-       "compile_fail tests are tests which should fail to compile (or emit\n"
-       "warnings) or link when FAIL is defined, but should compile and link\n"
-       "when it's not defined: this helps ensure unrelated errors don't make\n"
-       "compilation fail.\n\n"
-
-       "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 (mkdir(test_dir, 0700) != 0) {
-               if (errno != EEXIST)
-                       err(1, "Creating test/ directory");
-       }
-
-       run = fopen("test/run.c", "w");
-       if (!run)
-               err(1, "Trying to create a test/run.c");
-
-       fprintf(run, "#include <ccan/%s/%s.h>\n", m->basename, m->basename);
-       if (!list_empty(&m->c_files)) {
-               fputs("/* Include the C files directly. */\n", run);
-               list_for_each(&m->c_files, i, list)
-                       fprintf(run, "#include <ccan/%s/%s>\n",
-                               m->basename, i->name);
-       }
-       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);
-}
-
-static void check_tests_exist(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);
-               tests_exist.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";
-                       tests_exist.handle = handle_no_tests;
-               } else
-                       score->error = "No positive tests in test directory";
-               return;
-       }
-       score->pass = true;
-       score->score = score->total;
-}
-
-struct ccanlint tests_exist = {
-       .key = "tests_exist",
-       .name = "Module has test directory with tests in it",
-       .check = check_tests_exist,
-       .needs = ""
-};
-
-REGISTER_TEST(tests_exist);
diff --git a/tools/ccanlint/tests/headers_idempotent.c b/tools/ccanlint/tests/headers_idempotent.c
new file mode 100644 (file)
index 0000000..340a069
--- /dev/null
@@ -0,0 +1,204 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char explain[] 
+= "Headers usually start with the C preprocessor lines to prevent multiple\n"
+  "inclusions.  These look like the following:\n"
+  "#ifndef CCAN_<MODNAME>_H\n"
+  "#define CCAN_<MODNAME>_H\n"
+  "...\n"
+  "#endif /* CCAN_<MODNAME>_H */\n";
+
+static void fix_name(char *name)
+{
+       unsigned int i;
+
+       for (i = 0; name[i]; i++) {
+               if (isalnum(name[i]))
+                       name[i] = toupper(name[i]);
+               else
+                       name[i] = '_';
+       }
+}
+
+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",
+                                              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;
+       const char *line, *sym;
+
+       line_info = get_ccan_line_info(f);
+       if (f->num_lines < 3)
+               /* FIXME: We assume small headers probably uninteresting. */
+               return true;
+
+       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) {
+                       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)
+               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;
+               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)
+               return true;
+
+       /* We expect a condition on this line. */
+       if (!line_info[i].cond) {
+               score_file_error(score, f, i+1, "Expected #ifndef");
+               return false;
+       }
+
+       line = f->lines[i];
+
+       /* We expect the condition to be ! IFDEF <symbol>. */
+       if (line_info[i].cond->type != PP_COND_IFDEF
+           || !line_info[i].cond->inverse) {
+               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")) {
+               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)) {
+               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. */
+       for (i++; i < f->num_lines; i++) {
+               unsigned int val = 0;
+               if (line_info[i].type == DOC_LINE
+                   || line_info[i].type == COMMENT_LINE)
+                       continue;
+               if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
+                   != NOT_COMPILED) {
+                       score_file_error(score, f, i+1, "code outside"
+                                        " idempotent region");
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static void check_idempotent(struct manifest *m,
+                            bool keep,
+                            unsigned int *timeleft, struct score *score)
+{
+       struct ccan_file *f;
+
+       list_for_each(&m->h_files, f, list) {
+               if (!check_idem(f, score))
+                       score->error = "Headers are not idempotent";
+       }
+       if (!score->error) {
+               score->pass = true;
+               score->score = score->total;
+       }
+}
+
+struct ccanlint headers_idempotent = {
+       .key = "headers_idempotent",
+       .name = "Module headers are #ifndef/#define wrapped",
+       .check = check_idempotent,
+       .handle = handle_idem,
+       .needs = ""
+};
+
+REGISTER_TEST(headers_idempotent);
diff --git a/tools/ccanlint/tests/idempotent.c b/tools/ccanlint/tests/idempotent.c
deleted file mode 100644 (file)
index 340a069..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-static const char explain[] 
-= "Headers usually start with the C preprocessor lines to prevent multiple\n"
-  "inclusions.  These look like the following:\n"
-  "#ifndef CCAN_<MODNAME>_H\n"
-  "#define CCAN_<MODNAME>_H\n"
-  "...\n"
-  "#endif /* CCAN_<MODNAME>_H */\n";
-
-static void fix_name(char *name)
-{
-       unsigned int i;
-
-       for (i = 0; name[i]; i++) {
-               if (isalnum(name[i]))
-                       name[i] = toupper(name[i]);
-               else
-                       name[i] = '_';
-       }
-}
-
-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",
-                                              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;
-       const char *line, *sym;
-
-       line_info = get_ccan_line_info(f);
-       if (f->num_lines < 3)
-               /* FIXME: We assume small headers probably uninteresting. */
-               return true;
-
-       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) {
-                       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)
-               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;
-               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)
-               return true;
-
-       /* We expect a condition on this line. */
-       if (!line_info[i].cond) {
-               score_file_error(score, f, i+1, "Expected #ifndef");
-               return false;
-       }
-
-       line = f->lines[i];
-
-       /* We expect the condition to be ! IFDEF <symbol>. */
-       if (line_info[i].cond->type != PP_COND_IFDEF
-           || !line_info[i].cond->inverse) {
-               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")) {
-               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)) {
-               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. */
-       for (i++; i < f->num_lines; i++) {
-               unsigned int val = 0;
-               if (line_info[i].type == DOC_LINE
-                   || line_info[i].type == COMMENT_LINE)
-                       continue;
-               if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
-                   != NOT_COMPILED) {
-                       score_file_error(score, f, i+1, "code outside"
-                                        " idempotent region");
-                       return false;
-               }
-       }
-
-       return true;
-}
-
-static void check_idempotent(struct manifest *m,
-                            bool keep,
-                            unsigned int *timeleft, struct score *score)
-{
-       struct ccan_file *f;
-
-       list_for_each(&m->h_files, f, list) {
-               if (!check_idem(f, score))
-                       score->error = "Headers are not idempotent";
-       }
-       if (!score->error) {
-               score->pass = true;
-               score->score = score->total;
-       }
-}
-
-struct ccanlint headers_idempotent = {
-       .key = "headers_idempotent",
-       .name = "Module headers are #ifndef/#define wrapped",
-       .check = check_idempotent,
-       .handle = handle_idem,
-       .needs = ""
-};
-
-REGISTER_TEST(headers_idempotent);
diff --git a/tools/ccanlint/tests/info_documentation_exists.c b/tools/ccanlint/tests/info_documentation_exists.c
new file mode 100644 (file)
index 0000000..a5316fe
--- /dev/null
@@ -0,0 +1,102 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/doc_extract.h>
+#include <tools/tools.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <ccan/str/str.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/noerr/noerr.h>
+#include <ccan/grab_file/grab_file.h>
+
+extern struct ccanlint info_documentation_exists;
+
+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;
+       char *oldcontents;
+
+       if (fd < 0 || !(new = fdopen(fd, "w")))
+               err(1, "Creating _info.new to insert documentation");
+
+       if (fprintf(new,
+                   "/**\n"
+                   " * %s - [[ONE LINE DESCRIPTION HERE]]\n"
+                   " *\n"
+                   " * Paragraphs why %s exists and where to use it.\n"
+                   " *\n"
+                   " * Followed by an Example: section with a standalone\n"
+                   " * (trivial and usually useless) program\n"
+                   " */\n", m->basename, m->basename) < 0) {
+               unlink_noerr("_info.new");
+               err(1, "Writing to _info.new to insert documentation");
+       }
+
+       oldcontents = grab_file(m, "_info", NULL);
+       if (!oldcontents) {
+               unlink_noerr("_info.new");
+               err(1, "Reading _info");
+       }
+       if (fprintf(new, "%s", oldcontents) < 0) {
+               unlink_noerr("_info.new");
+               err(1, "Appending _info to _info.new");
+       }
+       if (fclose(new) != 0) {
+               unlink_noerr("_info.new");
+               err(1, "Closing _info.new");
+       }
+       if (!move_file("_info.new", "_info")) {
+               unlink_noerr("_info.new");
+               err(1, "Renaming _info.new to _info");
+       }
+}
+
+static void check_info_documentation_exists(struct manifest *m,
+                                        bool keep,
+                                        unsigned int *timeleft,
+                                        struct score *score)
+{
+       struct list_head *infodocs = get_ccan_file_docs(m->info_file);
+       struct doc_section *d;
+       bool summary = false, description = false;
+
+       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"
+               "overall documentation belongs in the _info metafile.\n";
+               info_documentation_exists.handle = create_info_template_doc;
+       } 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"
+               "overall package\n";
+       }
+}
+
+struct ccanlint info_documentation_exists = {
+       .key = "info_documentation_exists",
+       .name = "Module has documentation in _info",
+       .check = check_info_documentation_exists,
+       .needs = "info_exists"
+};
+
+REGISTER_TEST(info_documentation_exists);
diff --git a/tools/ccanlint/tests/license.c b/tools/ccanlint/tests/license.c
deleted file mode 100644 (file)
index 965a159..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-
-struct ccanlint has_license;
-
-static struct doc_section *find_license(const struct manifest *m)
-{
-       struct doc_section *d;
-
-       list_for_each(m->info_file->doc_sections, d, list) {
-               if (!streq(d->function, m->basename))
-                       continue;
-               if (streq(d->type, "license"))
-                       return d;
-       }
-       return NULL;
-}
-
-static const char *expected_link(const struct manifest *m,
-                                struct doc_section *d)
-{
-       if (streq(d->lines[0], "GPL")
-           || streq(d->lines[0], "GPLv3")
-           || streq(d->lines[0], "GPLv3 or later")
-           || streq(d->lines[0], "GPLv3 (or later)")
-           || streq(d->lines[0], "GPL (3 or any later version)"))
-               return "../../licenses/GPL-3";
-       if (streq(d->lines[0], "GPLv2")
-           || streq(d->lines[0], "GPLv2 or later")
-           || streq(d->lines[0], "GPLv2 (or later)")
-           || streq(d->lines[0], "GPL (2 or any later version)"))
-               return "../../licenses/GPL-3";
-       if (streq(d->lines[0], "LGPL")
-           || streq(d->lines[0], "LGPLv3")
-           || streq(d->lines[0], "LGPLv3 or later")
-           || streq(d->lines[0], "LGPLv3 (or later)")
-           || streq(d->lines[0], "LGPL (3 or any later version)"))
-               return "../../licenses/LGPL-3";
-       if (streq(d->lines[0], "LGPLv2")
-           || streq(d->lines[0], "LGPLv2 or later")
-           || streq(d->lines[0], "LGPLv2 (or later)")
-           || streq(d->lines[0], "LGPL (2 or any later version)"))
-               return "../../licenses/LGPL-2.1";
-       if (streq(d->lines[0], "BSD")
-           || streq(d->lines[0], "BSD-MIT")
-           || streq(d->lines[0], "MIT"))
-               return "../../licenses/BSD-MIT";
-       return NULL;
-}
-
-static void handle_license_link(struct manifest *m, struct score *score)
-{
-       const char *link = talloc_asprintf(m, "%s/LICENSE", m->dir);
-       struct doc_section *d = find_license(m);
-       const char *ldest = expected_link(m, d);
-       char *q;
-
-       printf(
-       "Most modules want a copy of their license, so usually we create a\n"
-       "LICENSE symlink into ../../licenses to avoid too many copies.\n");
-
-       /* FIXME: make ask printf-like */
-       q = talloc_asprintf(m, "Set up link to %s (license is %s)?",
-                           ldest, d->lines[0]);
-       if (ask(q)) {
-               if (symlink(ldest, link) != 0)
-                       err(1, "Creating symlink %s -> %s", link, ldest);
-       }
-}
-
-static void check_has_license(struct manifest *m,
-                             bool keep,
-                             unsigned int *timeleft, struct score *score)
-{
-       char buf[PATH_MAX];
-       ssize_t len;
-       char *license = talloc_asprintf(m, "%s/LICENSE", m->dir);
-       const char *expected;
-       struct doc_section *d;
-
-       d = find_license(m);
-       if (!d) {
-               score->error = "No License: tag in _info";
-               return;
-       }
-       expected = expected_link(m, d);
-
-       len = readlink(license, buf, sizeof(buf));
-       if (len < 0) {
-               /* Could be a real file... OK if not a standard license. */
-               if (errno == EINVAL) {
-                       if (!expected) {
-                               score->pass = true;
-                               return;
-                       }
-                       score->error
-                               = talloc_asprintf(score,
-                                         "License in _info is '%s',"
-                                         " expect LICENSE symlink '%s'",
-                                         d->lines[0], expected);
-                       return;
-               }
-               if (errno == ENOENT) {
-                       score->error = "LICENSE does not exist";
-                       if (expected)
-                               has_license.handle = handle_license_link;
-                       return;
-               }
-               err(1, "readlink on %s", license);
-       }
-       if (len >= sizeof(buf))
-               errx(1, "Reading symlink %s gave huge result", license);
-
-       buf[len] = '\0';
-
-       if (!strstarts(buf, "../../licenses/")) {
-               score->error = talloc_asprintf(score,
-                                              "Expected symlink to"
-                                              " ../../licenses/..."
-                                              " not %s", buf);
-               return;
-       }
-
-       if (!expected) {
-               score->error = talloc_asprintf(score,
-                                         "License in _info is unknown '%s',"
-                                         " but LICENSE symlink is '%s'",
-                                         d->lines[0], buf);
-               return;
-       }
-
-       if (!streq(buf, expected)) {
-               score->error = talloc_asprintf(score,
-                                      "Expected symlink to %s not %s",
-                                      expected, buf);
-               return;
-       }
-       score->pass = true;
-       score->score = score->total;
-}
-
-struct ccanlint license_exists = {
-       .key = "license_exists",
-       .name = "Module has License: entry in _info, and LICENSE symlink/file",
-       .check = check_has_license,
-       .needs = "info_exists"
-};
-
-REGISTER_TEST(license_exists);
diff --git a/tools/ccanlint/tests/license_exists.c b/tools/ccanlint/tests/license_exists.c
new file mode 100644 (file)
index 0000000..965a159
--- /dev/null
@@ -0,0 +1,159 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+
+struct ccanlint has_license;
+
+static struct doc_section *find_license(const struct manifest *m)
+{
+       struct doc_section *d;
+
+       list_for_each(m->info_file->doc_sections, d, list) {
+               if (!streq(d->function, m->basename))
+                       continue;
+               if (streq(d->type, "license"))
+                       return d;
+       }
+       return NULL;
+}
+
+static const char *expected_link(const struct manifest *m,
+                                struct doc_section *d)
+{
+       if (streq(d->lines[0], "GPL")
+           || streq(d->lines[0], "GPLv3")
+           || streq(d->lines[0], "GPLv3 or later")
+           || streq(d->lines[0], "GPLv3 (or later)")
+           || streq(d->lines[0], "GPL (3 or any later version)"))
+               return "../../licenses/GPL-3";
+       if (streq(d->lines[0], "GPLv2")
+           || streq(d->lines[0], "GPLv2 or later")
+           || streq(d->lines[0], "GPLv2 (or later)")
+           || streq(d->lines[0], "GPL (2 or any later version)"))
+               return "../../licenses/GPL-3";
+       if (streq(d->lines[0], "LGPL")
+           || streq(d->lines[0], "LGPLv3")
+           || streq(d->lines[0], "LGPLv3 or later")
+           || streq(d->lines[0], "LGPLv3 (or later)")
+           || streq(d->lines[0], "LGPL (3 or any later version)"))
+               return "../../licenses/LGPL-3";
+       if (streq(d->lines[0], "LGPLv2")
+           || streq(d->lines[0], "LGPLv2 or later")
+           || streq(d->lines[0], "LGPLv2 (or later)")
+           || streq(d->lines[0], "LGPL (2 or any later version)"))
+               return "../../licenses/LGPL-2.1";
+       if (streq(d->lines[0], "BSD")
+           || streq(d->lines[0], "BSD-MIT")
+           || streq(d->lines[0], "MIT"))
+               return "../../licenses/BSD-MIT";
+       return NULL;
+}
+
+static void handle_license_link(struct manifest *m, struct score *score)
+{
+       const char *link = talloc_asprintf(m, "%s/LICENSE", m->dir);
+       struct doc_section *d = find_license(m);
+       const char *ldest = expected_link(m, d);
+       char *q;
+
+       printf(
+       "Most modules want a copy of their license, so usually we create a\n"
+       "LICENSE symlink into ../../licenses to avoid too many copies.\n");
+
+       /* FIXME: make ask printf-like */
+       q = talloc_asprintf(m, "Set up link to %s (license is %s)?",
+                           ldest, d->lines[0]);
+       if (ask(q)) {
+               if (symlink(ldest, link) != 0)
+                       err(1, "Creating symlink %s -> %s", link, ldest);
+       }
+}
+
+static void check_has_license(struct manifest *m,
+                             bool keep,
+                             unsigned int *timeleft, struct score *score)
+{
+       char buf[PATH_MAX];
+       ssize_t len;
+       char *license = talloc_asprintf(m, "%s/LICENSE", m->dir);
+       const char *expected;
+       struct doc_section *d;
+
+       d = find_license(m);
+       if (!d) {
+               score->error = "No License: tag in _info";
+               return;
+       }
+       expected = expected_link(m, d);
+
+       len = readlink(license, buf, sizeof(buf));
+       if (len < 0) {
+               /* Could be a real file... OK if not a standard license. */
+               if (errno == EINVAL) {
+                       if (!expected) {
+                               score->pass = true;
+                               return;
+                       }
+                       score->error
+                               = talloc_asprintf(score,
+                                         "License in _info is '%s',"
+                                         " expect LICENSE symlink '%s'",
+                                         d->lines[0], expected);
+                       return;
+               }
+               if (errno == ENOENT) {
+                       score->error = "LICENSE does not exist";
+                       if (expected)
+                               has_license.handle = handle_license_link;
+                       return;
+               }
+               err(1, "readlink on %s", license);
+       }
+       if (len >= sizeof(buf))
+               errx(1, "Reading symlink %s gave huge result", license);
+
+       buf[len] = '\0';
+
+       if (!strstarts(buf, "../../licenses/")) {
+               score->error = talloc_asprintf(score,
+                                              "Expected symlink to"
+                                              " ../../licenses/..."
+                                              " not %s", buf);
+               return;
+       }
+
+       if (!expected) {
+               score->error = talloc_asprintf(score,
+                                         "License in _info is unknown '%s',"
+                                         " but LICENSE symlink is '%s'",
+                                         d->lines[0], buf);
+               return;
+       }
+
+       if (!streq(buf, expected)) {
+               score->error = talloc_asprintf(score,
+                                      "Expected symlink to %s not %s",
+                                      expected, buf);
+               return;
+       }
+       score->pass = true;
+       score->score = score->total;
+}
+
+struct ccanlint license_exists = {
+       .key = "license_exists",
+       .name = "Module has License: entry in _info, and LICENSE symlink/file",
+       .check = check_has_license,
+       .needs = "info_exists"
+};
+
+REGISTER_TEST(license_exists);
diff --git a/tools/ccanlint/tests/no_trailing_whitespace.c b/tools/ccanlint/tests/no_trailing_whitespace.c
new file mode 100644 (file)
index 0000000..a66fd74
--- /dev/null
@@ -0,0 +1,60 @@
+/* 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.  */
+static char *get_trailing_whitespace(const char *line)
+{
+       const char *e = strchr(line, 0);
+       while (e>line && (e[-1]==' ' || e[-1]=='\t'))
+               e--;
+       if (*e == 0)
+               return NULL; //there were no trailing spaces
+       if (e == line)
+               return NULL; //the line only consists of spaces
+
+       if (strlen(line) > 20)
+               return talloc_asprintf(line, "...'%s'",
+                                      line + strlen(line) - 20);
+       return talloc_asprintf(line, "'%s'", line);
+}
+
+static void check_trailing_whitespace(struct manifest *m,
+                                     bool keep,
+                                     unsigned int *timeleft,
+                                     struct score *score)
+{
+       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 no_trailing_whitespace = {
+       .key = "no_trailing_whitespace",
+       .name = "Module's source code has no trailing whitespace",
+       .check = check_trailing_whitespace,
+       .needs = ""
+};
+
+
+REGISTER_TEST(no_trailing_whitespace);
diff --git a/tools/ccanlint/tests/run-coverage.c b/tools/ccanlint/tests/run-coverage.c
deleted file mode 100644 (file)
index af8d6f2..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.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 <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-static bool find_source_file(const struct manifest *m, const char *filename)
-{
-       const struct ccan_file *i;
-
-       list_for_each(&m->c_files, i, list) {
-               if (streq(i->fullname, filename))
-                       return true;
-       }
-       list_for_each(&m->h_files, i, list) {
-               if (streq(i->fullname, filename))
-                       return true;
-       }
-       return false;
-}
-
-/* 1 point for 50%, 2 points for 75%, 3 points for 87.5%...  Bonus for 100%. */
-static unsigned int score_coverage(float covered, unsigned total)
-{
-       float thresh, uncovered = 1.0 - covered;
-       unsigned int i;
-
-       if (covered == 1.0)
-               return total;
-
-       total--;
-       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? */
-static void analyze_coverage(struct manifest *m, bool full_gcov,
-                            const char *output, struct score *score)
-{
-       char **lines = strsplit(score, output, "\n", NULL);
-       float covered_lines = 0.0;
-       unsigned int i, total_lines = 0;
-       bool lines_matter = false;
-
-       /*
-         Output looks like:
-          File '../../../ccan/tdb2/private.h'
-          Lines executed:0.00% of 8
-          /home/ccan/ccan/tdb2/test/run-simple-delete.c:creating 'run-simple-delete.c.gcov'
-
-          File '../../../ccan/tdb2/tdb.c'
-          Lines executed:0.00% of 450
-       */
-
-       for (i = 0; lines[i]; i++) {
-               if (strstarts(lines[i], "File '")) {
-                       char *file = lines[i] + strlen("File '");
-                       file[strcspn(file, "'")] = '\0';
-                       if (find_source_file(m, file))
-                               lines_matter = true;
-                       else
-                               lines_matter = false;
-               } else if (lines_matter
-                          && strstarts(lines[i], "Lines executed:")) {
-                       float ex;
-                       unsigned of;
-                       if (sscanf(lines[i], "Lines executed:%f%% of %u",
-                                  &ex, &of) != 2)
-                               errx(1, "Could not parse line '%s'", lines[i]);
-                       total_lines += of;
-                       covered_lines += ex / 100.0 * of;
-               } else if (full_gcov && strstr(lines[i], ":creating '")) {
-                       char *file, *filename, *apostrophe;
-                       apostrophe = strchr(lines[i], '\'');
-                       filename = apostrophe + 1;
-                       apostrophe = strchr(filename, '\'');
-                       *apostrophe = '\0';
-                       if (lines_matter) {
-                               file = grab_file(score, filename, NULL);
-                               if (!file) {
-                                       score->error = talloc_asprintf(score,
-                                                           "Reading %s",
-                                                           filename);
-                                       return;
-                               }
-                               printf("%s", file);
-                       }
-                       if (tools_verbose)
-                               printf("Unlinking %s", filename);
-                       unlink(filename);
-               }
-       }
-
-       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. */
-       if (total_lines == 0)
-               score->total = score->score = 0;
-       else {
-               score->total = 6;
-               score->score = score_coverage(covered_lines / total_lines,
-                                             score->total);
-       }
-}
-
-static void do_run_coverage_tests(struct manifest *m,
-                                 bool keep,
-                                 unsigned int *timeleft, struct score *score)
-{
-       struct ccan_file *i;
-       char *cmdout;
-       char *covcmd;
-       bool full_gcov = (verbose > 1);
-       struct list_head *list;
-
-       /* This tells gcov where we put those .gcno files. */
-       covcmd = talloc_asprintf(m, "gcov %s -o %s",
-                                full_gcov ? "" : "-n",
-                                talloc_dirname(score, m->info_file->compiled));
-
-       /* Run them all. */
-       foreach_ptr(list, &m->run_tests, &m->api_tests) {
-               list_for_each(list, i, list) {
-                       if (run_command(score, timeleft, &cmdout,
-                                       "%s", i->cov_compiled)) {
-                               covcmd = talloc_asprintf_append(covcmd, " %s",
-                                                               i->fullname);
-                       } else {
-                               score->error = "Running test with coverage";
-                               score_file_error(score, i, 0, cmdout);
-                               return;
-                       }
-               }
-       }
-
-       /* Now run gcov: we want output even if it succeeds. */
-       if (!run_command(score, timeleft, &cmdout, "%s", covcmd)) {
-               score->error = talloc_asprintf(score, "Running gcov: %s",
-                                              cmdout);
-               return;
-       }
-
-       analyze_coverage(m, full_gcov, cmdout, score);
-}
-
-struct ccanlint tests_coverage = {
-       .key = "tests_coverage",
-       .name = "Module's tests cover all the code",
-       .check = do_run_coverage_tests,
-       .needs = "tests_compile_coverage tests_pass"
-};
-
-REGISTER_TEST(tests_coverage);
diff --git a/tools/ccanlint/tests/run_tests.c b/tools/ccanlint/tests/run_tests.c
deleted file mode 100644 (file)
index 0edfad2..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#include <tools/ccanlint/ccanlint.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 <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-static const char *can_run(struct manifest *m)
-{
-       if (safe_mode)
-               return "Safe mode enabled";
-       return NULL;
-}
-
-static void do_run_tests(struct manifest *m,
-                        bool keep,
-                        unsigned int *timeleft,
-                        struct score *score)
-{
-       struct list_head *list;
-       struct ccan_file *i;
-       char *cmdout;
-
-       score->total = 0;
-       foreach_ptr(list, &m->run_tests, &m->api_tests) {
-               list_for_each(list, i, list) {
-                       score->total++;
-                       if (run_command(m, timeleft, &cmdout, "%s",
-                                       i->compiled))
-                               score->score++;
-                       else
-                               score_file_error(score, i, 0, cmdout);
-               }
-       }
-
-       if (score->score == score->total)
-               score->pass = true;
-}
-
-/* Gcc's warn_unused_result is fascist bullshit. */
-#define doesnt_matter()
-
-static void run_under_debugger(struct manifest *m, struct score *score)
-{
-       char *command;
-       struct file_error *first;
-
-       if (!ask("Should I run the first failing test under the debugger?"))
-               return;
-
-       first = list_top(&score->per_file_errors, struct file_error, list);
-       command = talloc_asprintf(m, "gdb -ex 'break tap.c:136' -ex 'run' %s",
-                                 first->file->compiled);
-       if (system(command))
-               doesnt_matter();
-}
-
-struct ccanlint tests_pass = {
-       .key = "tests_pass",
-       .name = "Module's run and api tests pass",
-       .check = do_run_tests,
-       .handle = run_under_debugger,
-       .can_run = can_run,
-       .needs = "tests_compile"
-};
-
-REGISTER_TEST(tests_pass);
diff --git a/tools/ccanlint/tests/run_tests_valgrind.c b/tools/ccanlint/tests/run_tests_valgrind.c
deleted file mode 100644 (file)
index 30a806e..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-#include <tools/ccanlint/ccanlint.h>
-#include <tools/tools.h>
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-#include <ccan/foreach/foreach.h>
-#include <ccan/grab_file/grab_file.h>
-#include <ccan/str_talloc/str_talloc.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <err.h>
-#include <string.h>
-#include <ctype.h>
-
-struct ccanlint run_tests_vg;
-
-/* Note: we already test safe_mode in run_tests.c */
-static const char *can_run_vg(struct manifest *m)
-{
-       unsigned int timeleft = default_timeout_ms;
-       char *output;
-
-       if (!run_command(m, &timeleft, &output,
-                        "valgrind -q --error-exitcode=0 true"))
-               return talloc_asprintf(m, "No valgrind support: %s", output);
-       return NULL;
-}
-
-/* Example output:
-==2749== Conditional jump or move depends on uninitialised value(s)
-==2749==    at 0x4026C60: strnlen (mc_replace_strmem.c:263)
-==2749==    by 0x40850E3: vfprintf (vfprintf.c:1614)
-==2749==    by 0x408EACF: printf (printf.c:35)
-==2749==    by 0x8048465: main (in /tmp/foo)
-==2749== 
-==2749== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
-==2749==    at 0x4025BD3: malloc (vg_replace_malloc.c:236)
-==2749==    by 0x8048444: main (in /tmp/foo)
-==2749== 
-*/
-
-static bool blank_line(const char *line)
-{
-       return line[strspn(line, "=0123456789 ")] == '\0';
-}
-
-static char *get_leaks(const char *output, char **errs)
-{
-       char *leaks = talloc_strdup(output, "");
-       unsigned int i, num;
-       char **lines = strsplit(output, output, "\n", &num);
-
-       *errs = talloc_strdup(output, "");
-       for (i = 0; i < num; i++) {
-               if (strstr(lines[i], " lost ")) {
-                       /* A leak... */
-                       if (strstr(lines[i], " definitely lost ")) {
-                               /* Definite leak, report. */
-                               while (lines[i] && !blank_line(lines[i])) {
-                                       leaks = talloc_append_string(leaks,
-                                                                    lines[i]);
-                                       leaks = talloc_append_string(leaks,
-                                                                    "\n");
-                                       i++;
-                               }
-                       } else
-                               /* Not definite, ignore. */
-                               while (lines[i] && !blank_line(lines[i]))
-                                       i++;
-               } else {
-                       /* A real error. */
-                       while (lines[i] && !blank_line(lines[i])) {
-                               *errs = talloc_append_string(*errs, lines[i]);
-                               *errs = talloc_append_string(*errs, "\n");
-                               i++;
-                       }
-               }
-       }
-       if (!leaks[0]) {
-               talloc_free(leaks);
-               leaks = NULL;
-       }
-       if (!(*errs)[0]) {
-               talloc_free(*errs);
-               *errs = NULL;
-       }
-       return leaks;
-}
-
-/* FIXME: Run examples, too! */
-static void do_run_tests_vg(struct manifest *m,
-                            bool keep,
-                           unsigned int *timeleft,
-                           struct score *score)
-{
-       struct ccan_file *i;
-       struct list_head *list;
-       char *cmdout;
-
-       /* This is slow, so we run once but grab leak info. */
-       score->total = 0;
-       foreach_ptr(list, &m->run_tests, &m->api_tests) {
-               list_for_each(list, i, list) {
-                       char *output, *err;
-                       score->total++;
-                       /* FIXME: Valgrind's output sucks.  XML is unreadable by
-                        * humans, and you can't have both. */
-                       run_command(score, timeleft, &cmdout,
-                                   "valgrind -q --error-exitcode=101"
-                                   " --leak-check=full"
-                                   " --log-fd=3 %s %s"
-                                   " 3> valgrind.log",
-                                   run_tests_vg.options ?
-                                   run_tests_vg.options : "",
-                                   i->compiled);
-                       output = grab_file(i, "valgrind.log", NULL);
-                       if (!output || output[0] == '\0') {
-                               err = NULL;
-                       } else {
-                               i->leak_info = get_leaks(output, &err);
-                       }
-                       if (err)
-                               score_file_error(score, i, 0, err);
-                       else
-                               score->score++;
-               }
-       }
-
-       if (score->score == score->total)
-               score->pass = true;
-}
-
-static void do_leakcheck_vg(struct manifest *m,
-                           bool keep,
-                           unsigned int *timeleft,
-                           struct score *score)
-{
-       struct ccan_file *i;
-       struct list_head *list;
-       bool leaks = false;
-
-       foreach_ptr(list, &m->run_tests, &m->api_tests) {
-               list_for_each(list, i, list) {
-                       if (i->leak_info) {
-                               score_file_error(score, i, 0, i->leak_info);
-                               leaks = true;
-                       }
-               }
-       }
-
-       if (!leaks) {
-               score->score = 1;
-               score->pass = true;
-       }
-}
-
-/* Gcc's warn_unused_result is fascist bullshit. */
-#define doesnt_matter()
-
-static void run_under_debugger_vg(struct manifest *m, struct score *score)
-{
-       struct file_error *first;
-       char *command;
-
-       if (!ask("Should I run the first failing test under the debugger?"))
-               return;
-
-       first = list_top(&score->per_file_errors, struct file_error, list);
-       command = talloc_asprintf(m, "valgrind --db-attach=yes%s %s",
-                                 run_tests_vg.options ?
-                                 run_tests_vg.options : "",
-                                 first->file->compiled);
-       if (system(command))
-               doesnt_matter();
-}
-
-struct ccanlint tests_pass_valgrind = {
-       .key = "tests_pass_valgrind",
-       .name = "Module's run and api tests succeed under valgrind",
-       .can_run = can_run_vg,
-       .check = do_run_tests_vg,
-       .handle = run_under_debugger_vg,
-       .takes_options = true,
-       .needs = "tests_pass"
-};
-
-REGISTER_TEST(tests_pass_valgrind);
-
-struct ccanlint tests_pass_valgrind_noleaks = {
-       .key = "tests_pass_valgrind_noleaks",
-       .name = "Module's run and api tests leak memory",
-       .check = do_leakcheck_vg,
-       .needs = "tests_pass_valgrind"
-};
-
-REGISTER_TEST(tests_pass_valgrind_noleaks);
diff --git a/tools/ccanlint/tests/tests_compile.c b/tools/ccanlint/tests/tests_compile.c
new file mode 100644 (file)
index 0000000..4d86867
--- /dev/null
@@ -0,0 +1,141 @@
+#include <tools/ccanlint/ccanlint.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 <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *can_build(struct manifest *m)
+{
+       if (safe_mode)
+               return "Safe mode enabled";
+       return NULL;
+}
+
+/* FIXME: Merge this into one place. */
+static char *obj_list(const struct manifest *m, bool link_with_module)
+{
+       char *list = talloc_strdup(m, "");
+       struct ccan_file *i;
+       struct manifest *subm;
+
+       /* Objects from any other C files. */
+       list_for_each(&m->other_test_c_files, i, list)
+               list = talloc_asprintf_append(list, " %s", i->compiled);
+
+       /* Our own object files. */
+       if (link_with_module)
+               list_for_each(&m->c_files, i, list)
+                       list = talloc_asprintf_append(list, " %s", i->compiled);
+
+       /* Other ccan modules. */
+       list_for_each(&m->deps, subm, list) {
+               if (subm->compiled)
+                       list = talloc_asprintf_append(list, " %s",
+                                                     subm->compiled);
+       }
+
+       return list;
+}
+
+static char *lib_list(const struct manifest *m)
+{
+       unsigned int i, num;
+       char **libs = get_libs(m, m->dir, &num, &m->info_file->compiled);
+       char *ret = talloc_strdup(m, "");
+
+       for (i = 0; i < num; i++)
+               ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
+       return ret;
+}
+
+static bool compile(const void *ctx,
+                   struct manifest *m,
+                   struct ccan_file *file,
+                   bool fail,
+                   bool link_with_module,
+                   bool keep, char **output)
+{
+       file->compiled = maybe_temp_file(ctx, "", keep, file->fullname);
+       if (!compile_and_link(ctx, file->fullname, ccan_dir,
+                             obj_list(m, link_with_module),
+                             fail ? "-DFAIL" : "",
+                             lib_list(m), file->compiled, output)) {
+               talloc_free(file->compiled);
+               return false;
+       }
+       return true;
+}
+
+static void do_compile_tests(struct manifest *m,
+                            bool keep,
+                            unsigned int *timeleft, struct score *score)
+{
+       char *cmdout;
+       struct ccan_file *i;
+       struct list_head *list;
+       bool errors = false, warnings = false;
+
+       foreach_ptr(list, &m->compile_ok_tests, &m->run_tests, &m->api_tests) {
+               list_for_each(list, i, list) {
+                       if (!compile(score, m, i, false, list == &m->api_tests,
+                                    keep, &cmdout)) {
+                               score->error = "Failed to compile tests";
+                               score_file_error(score, i, 0, cmdout);
+                               errors = true;
+                       } else if (!streq(cmdout, "")) {
+                               score->error = "Test compiled with warnings";
+                               score_file_error(score, i, 0, cmdout);
+                               warnings = true;
+                       }
+               }
+       }
+
+       /* The compile fail tests are a bit weird, handle them separately */
+       if (errors)
+               return;
+
+       /* For historical reasons, "fail" often means "gives warnings" */
+       list_for_each(&m->compile_fail_tests, i, list) {
+               if (!compile(score, m, i, false, false, false, &cmdout)) {
+                       score->error = "Failed to compile without -DFAIL";
+                       score_file_error(score, i, 0, cmdout);
+                       return;
+               }
+               if (!streq(cmdout, "")) {
+                       score->error = "Compile with warnigns without -DFAIL";
+                       score_file_error(score, i, 0, cmdout);
+                       return;
+               }
+               if (compile(score, m, i, true, false, false, &cmdout)
+                   && streq(cmdout, "")) {
+                       score->error = "Compiled successfully with -DFAIL?";
+                       score_file_error(score, i, 0, NULL);
+                       return;
+               }
+       }
+
+       score->pass = true;
+       score->total = 2;
+       score->score = 1 + !warnings;
+}
+
+struct ccanlint tests_compile = {
+       .key = "tests_compile",
+       .name = "Module tests compile",
+       .check = do_compile_tests,
+       .can_run = can_build,
+       .needs = "tests_helpers_compile objects_build"
+};
+
+REGISTER_TEST(tests_compile);
diff --git a/tools/ccanlint/tests/tests_compile_coverage.c b/tools/ccanlint/tests/tests_compile_coverage.c
new file mode 100644 (file)
index 0000000..115ae94
--- /dev/null
@@ -0,0 +1,153 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Note: we already test safe_mode in run_tests.c */
+static const char *can_run_coverage(struct manifest *m)
+{
+       unsigned int timeleft = default_timeout_ms;
+       char *output;
+
+       if (!run_command(m, &timeleft, &output, "gcov -h"))
+               return talloc_asprintf(m, "No gcov support: %s", output);
+       return NULL;
+}
+
+static bool build_module_objs_with_coverage(struct manifest *m, bool keep,
+                                           struct score *score,
+                                           char **modobjs)
+{
+       struct ccan_file *i;
+
+       *modobjs = talloc_strdup(m, "");
+       list_for_each(&m->c_files, i, list) {
+               char *err;
+               char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
+
+               i->cov_compiled = maybe_temp_file(m, "", keep, fullfile);
+               if (!compile_object(m, fullfile, ccan_dir, "",
+                                   i->cov_compiled, &err)) {
+                       score_file_error(score, i, 0, err);
+                       talloc_free(i->cov_compiled);
+                       i->cov_compiled = NULL;
+                       return false;
+               }
+               *modobjs = talloc_asprintf_append(*modobjs,
+                                                 " %s", i->cov_compiled);
+       }
+       return true;
+}
+
+/* FIXME: Merge this into one place. */
+static char *obj_list(const struct manifest *m, const char *modobjs)
+{
+       char *list = talloc_strdup(m, "");
+       struct ccan_file *i;
+       struct manifest *subm;
+
+       /* Objects from any other C files. */
+       list_for_each(&m->other_test_c_files, i, list)
+               list = talloc_asprintf_append(list, " %s", i->compiled);
+
+       if (modobjs)
+               list = talloc_append_string(list, modobjs);
+
+       /* Other ccan modules (don't need coverage versions of those). */
+       list_for_each(&m->deps, subm, list) {
+               if (subm->compiled)
+                       list = talloc_asprintf_append(list, " %s",
+                                                     subm->compiled);
+       }
+
+       return list;
+}
+
+static char *lib_list(const struct manifest *m)
+{
+       unsigned int i, num;
+       char **libs = get_libs(m, m->dir, &num, &m->info_file->compiled);
+       char *ret = talloc_strdup(m, "");
+
+       for (i = 0; i < num; i++)
+               ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
+       return ret;
+}
+
+static char *cov_compile(const void *ctx,
+                        struct manifest *m,
+                        struct ccan_file *file,
+                        const char *modobjs,
+                        bool keep)
+{
+       char *output;
+
+       file->cov_compiled = maybe_temp_file(ctx, "", keep, file->fullname);
+       if (!compile_and_link(ctx, file->fullname, ccan_dir,
+                             obj_list(m, modobjs),
+                             COVERAGE_CFLAGS,
+                             lib_list(m), file->cov_compiled, &output)) {
+               talloc_free(file->cov_compiled);
+               file->cov_compiled = NULL;
+               return output;
+       }
+       talloc_free(output);
+       return NULL;
+}
+
+/* FIXME: Coverage from testable examples as well. */
+static void do_compile_coverage_tests(struct manifest *m,
+                                     bool keep,
+                                     unsigned int *timeleft,
+                                     struct score *score)
+{
+       char *cmdout, *modobjs = NULL;
+       struct ccan_file *i;
+
+       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) {
+               cmdout = cov_compile(m, m, i, NULL, keep);
+               if (cmdout) {
+                       score->error = "Failed to compile test with coverage";
+                       score_file_error(score, i, 0, cmdout);
+               }
+       }
+
+       list_for_each(&m->api_tests, i, list) {
+               cmdout = cov_compile(m, m, i, modobjs, keep);
+               if (cmdout) {
+                       score->error = "Failed to compile test with coverage";
+                       score_file_error(score, i, 0, cmdout);
+               }
+       }
+       if (!score->error) {
+               score->pass = true;
+               score->score = score->total;
+       }
+}
+
+struct ccanlint tests_compile_coverage = {
+       .key = "tests_compile_coverage",
+       .name = "Module tests compile with " COVERAGE_CFLAGS,
+       .check = do_compile_coverage_tests,
+       .can_run = can_run_coverage,
+       .needs = "tests_compile"
+};
+
+REGISTER_TEST(tests_compile_coverage);
diff --git a/tools/ccanlint/tests/tests_coverage.c b/tools/ccanlint/tests/tests_coverage.c
new file mode 100644 (file)
index 0000000..af8d6f2
--- /dev/null
@@ -0,0 +1,172 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.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 <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static bool find_source_file(const struct manifest *m, const char *filename)
+{
+       const struct ccan_file *i;
+
+       list_for_each(&m->c_files, i, list) {
+               if (streq(i->fullname, filename))
+                       return true;
+       }
+       list_for_each(&m->h_files, i, list) {
+               if (streq(i->fullname, filename))
+                       return true;
+       }
+       return false;
+}
+
+/* 1 point for 50%, 2 points for 75%, 3 points for 87.5%...  Bonus for 100%. */
+static unsigned int score_coverage(float covered, unsigned total)
+{
+       float thresh, uncovered = 1.0 - covered;
+       unsigned int i;
+
+       if (covered == 1.0)
+               return total;
+
+       total--;
+       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? */
+static void analyze_coverage(struct manifest *m, bool full_gcov,
+                            const char *output, struct score *score)
+{
+       char **lines = strsplit(score, output, "\n", NULL);
+       float covered_lines = 0.0;
+       unsigned int i, total_lines = 0;
+       bool lines_matter = false;
+
+       /*
+         Output looks like:
+          File '../../../ccan/tdb2/private.h'
+          Lines executed:0.00% of 8
+          /home/ccan/ccan/tdb2/test/run-simple-delete.c:creating 'run-simple-delete.c.gcov'
+
+          File '../../../ccan/tdb2/tdb.c'
+          Lines executed:0.00% of 450
+       */
+
+       for (i = 0; lines[i]; i++) {
+               if (strstarts(lines[i], "File '")) {
+                       char *file = lines[i] + strlen("File '");
+                       file[strcspn(file, "'")] = '\0';
+                       if (find_source_file(m, file))
+                               lines_matter = true;
+                       else
+                               lines_matter = false;
+               } else if (lines_matter
+                          && strstarts(lines[i], "Lines executed:")) {
+                       float ex;
+                       unsigned of;
+                       if (sscanf(lines[i], "Lines executed:%f%% of %u",
+                                  &ex, &of) != 2)
+                               errx(1, "Could not parse line '%s'", lines[i]);
+                       total_lines += of;
+                       covered_lines += ex / 100.0 * of;
+               } else if (full_gcov && strstr(lines[i], ":creating '")) {
+                       char *file, *filename, *apostrophe;
+                       apostrophe = strchr(lines[i], '\'');
+                       filename = apostrophe + 1;
+                       apostrophe = strchr(filename, '\'');
+                       *apostrophe = '\0';
+                       if (lines_matter) {
+                               file = grab_file(score, filename, NULL);
+                               if (!file) {
+                                       score->error = talloc_asprintf(score,
+                                                           "Reading %s",
+                                                           filename);
+                                       return;
+                               }
+                               printf("%s", file);
+                       }
+                       if (tools_verbose)
+                               printf("Unlinking %s", filename);
+                       unlink(filename);
+               }
+       }
+
+       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. */
+       if (total_lines == 0)
+               score->total = score->score = 0;
+       else {
+               score->total = 6;
+               score->score = score_coverage(covered_lines / total_lines,
+                                             score->total);
+       }
+}
+
+static void do_run_coverage_tests(struct manifest *m,
+                                 bool keep,
+                                 unsigned int *timeleft, struct score *score)
+{
+       struct ccan_file *i;
+       char *cmdout;
+       char *covcmd;
+       bool full_gcov = (verbose > 1);
+       struct list_head *list;
+
+       /* This tells gcov where we put those .gcno files. */
+       covcmd = talloc_asprintf(m, "gcov %s -o %s",
+                                full_gcov ? "" : "-n",
+                                talloc_dirname(score, m->info_file->compiled));
+
+       /* Run them all. */
+       foreach_ptr(list, &m->run_tests, &m->api_tests) {
+               list_for_each(list, i, list) {
+                       if (run_command(score, timeleft, &cmdout,
+                                       "%s", i->cov_compiled)) {
+                               covcmd = talloc_asprintf_append(covcmd, " %s",
+                                                               i->fullname);
+                       } else {
+                               score->error = "Running test with coverage";
+                               score_file_error(score, i, 0, cmdout);
+                               return;
+                       }
+               }
+       }
+
+       /* Now run gcov: we want output even if it succeeds. */
+       if (!run_command(score, timeleft, &cmdout, "%s", covcmd)) {
+               score->error = talloc_asprintf(score, "Running gcov: %s",
+                                              cmdout);
+               return;
+       }
+
+       analyze_coverage(m, full_gcov, cmdout, score);
+}
+
+struct ccanlint tests_coverage = {
+       .key = "tests_coverage",
+       .name = "Module's tests cover all the code",
+       .check = do_run_coverage_tests,
+       .needs = "tests_compile_coverage tests_pass"
+};
+
+REGISTER_TEST(tests_coverage);
diff --git a/tools/ccanlint/tests/tests_exist.c b/tools/ccanlint/tests/tests_exist.c
new file mode 100644 (file)
index 0000000..bdfe49d
--- /dev/null
@@ -0,0 +1,136 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <ccan/talloc/talloc.h>
+
+extern struct ccanlint tests_exist;
+
+static void handle_no_tests(struct manifest *m, struct score *score)
+{
+       FILE *run;
+       struct ccan_file *i;
+       char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
+
+       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"
+       "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"
+       "directory, with the test directory symlinked under test/.\n\n"
+
+       "api tests are just like a run test, except it is a guarantee of API\n"
+       "stability: this test should pass on all future versions of the\n"
+       "module.  They *are* linked to the module, since they should only\n"
+       "test the API, not the internal state.\n\n"
+
+       "compile_ok tests are a subset of run tests: they must compile and\n"
+       "link, but aren't run.\n\n"
+
+       "compile_fail tests are tests which should fail to compile (or emit\n"
+       "warnings) or link when FAIL is defined, but should compile and link\n"
+       "when it's not defined: this helps ensure unrelated errors don't make\n"
+       "compilation fail.\n\n"
+
+       "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 (mkdir(test_dir, 0700) != 0) {
+               if (errno != EEXIST)
+                       err(1, "Creating test/ directory");
+       }
+
+       run = fopen("test/run.c", "w");
+       if (!run)
+               err(1, "Trying to create a test/run.c");
+
+       fprintf(run, "#include <ccan/%s/%s.h>\n", m->basename, m->basename);
+       if (!list_empty(&m->c_files)) {
+               fputs("/* Include the C files directly. */\n", run);
+               list_for_each(&m->c_files, i, list)
+                       fprintf(run, "#include <ccan/%s/%s>\n",
+                               m->basename, i->name);
+       }
+       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);
+}
+
+static void check_tests_exist(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);
+               tests_exist.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";
+                       tests_exist.handle = handle_no_tests;
+               } else
+                       score->error = "No positive tests in test directory";
+               return;
+       }
+       score->pass = true;
+       score->score = score->total;
+}
+
+struct ccanlint tests_exist = {
+       .key = "tests_exist",
+       .name = "Module has test directory with tests in it",
+       .check = check_tests_exist,
+       .needs = ""
+};
+
+REGISTER_TEST(tests_exist);
diff --git a/tools/ccanlint/tests/tests_helpers_compile.c b/tools/ccanlint/tests/tests_helpers_compile.c
new file mode 100644 (file)
index 0000000..0ad5a7e
--- /dev/null
@@ -0,0 +1,75 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *can_run(struct manifest *m)
+{
+       if (safe_mode)
+               return "Safe mode enabled";
+       return NULL;
+}
+
+static bool compile(struct manifest *m,
+                   bool keep,
+                   struct ccan_file *cfile,
+                   char **output)
+{
+       cfile->compiled = maybe_temp_file(m, ".o", keep, cfile->fullname);
+       return compile_object(m, cfile->fullname, ccan_dir, "",
+                             cfile->compiled, output);
+}
+
+static void do_compile_test_helpers(struct manifest *m,
+                                   bool keep,
+                                   unsigned int *timeleft,
+                                   struct score *score)
+{
+       struct ccan_file *i;
+       bool errors = false, warnings = false;
+
+       if (list_empty(&m->other_test_c_files))
+               score->total = 0;
+       else
+               score->total = 2;
+
+       list_for_each(&m->other_test_c_files, i, list) {
+               char *cmdout;
+
+               if (!compile(m, keep, i, &cmdout)) {
+                       errors = true;
+                       score->error = "Failed to compile helper C files";
+                       score_file_error(score, i, 0, cmdout);
+               } else if (!streq(cmdout, "")) {
+                       warnings = true;
+                       score->error = "Helper C files gave warnings";
+                       score_file_error(score, i, 0, cmdout);
+               }
+       }
+
+       if (!errors) {
+               score->pass = true;
+               score->score = score->total - warnings;
+       }
+}
+
+struct ccanlint tests_helpers_compile = {
+       .key = "tests_helpers_compile",
+       .name = "Module test helper objects compile",
+       .check = do_compile_test_helpers,
+       .can_run = can_run,
+       .needs = "depends_build tests_exist"
+};
+
+REGISTER_TEST(tests_helpers_compile);
diff --git a/tools/ccanlint/tests/tests_pass.c b/tools/ccanlint/tests/tests_pass.c
new file mode 100644 (file)
index 0000000..0edfad2
--- /dev/null
@@ -0,0 +1,77 @@
+#include <tools/ccanlint/ccanlint.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 <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *can_run(struct manifest *m)
+{
+       if (safe_mode)
+               return "Safe mode enabled";
+       return NULL;
+}
+
+static void do_run_tests(struct manifest *m,
+                        bool keep,
+                        unsigned int *timeleft,
+                        struct score *score)
+{
+       struct list_head *list;
+       struct ccan_file *i;
+       char *cmdout;
+
+       score->total = 0;
+       foreach_ptr(list, &m->run_tests, &m->api_tests) {
+               list_for_each(list, i, list) {
+                       score->total++;
+                       if (run_command(m, timeleft, &cmdout, "%s",
+                                       i->compiled))
+                               score->score++;
+                       else
+                               score_file_error(score, i, 0, cmdout);
+               }
+       }
+
+       if (score->score == score->total)
+               score->pass = true;
+}
+
+/* Gcc's warn_unused_result is fascist bullshit. */
+#define doesnt_matter()
+
+static void run_under_debugger(struct manifest *m, struct score *score)
+{
+       char *command;
+       struct file_error *first;
+
+       if (!ask("Should I run the first failing test under the debugger?"))
+               return;
+
+       first = list_top(&score->per_file_errors, struct file_error, list);
+       command = talloc_asprintf(m, "gdb -ex 'break tap.c:136' -ex 'run' %s",
+                                 first->file->compiled);
+       if (system(command))
+               doesnt_matter();
+}
+
+struct ccanlint tests_pass = {
+       .key = "tests_pass",
+       .name = "Module's run and api tests pass",
+       .check = do_run_tests,
+       .handle = run_under_debugger,
+       .can_run = can_run,
+       .needs = "tests_compile"
+};
+
+REGISTER_TEST(tests_pass);
diff --git a/tools/ccanlint/tests/tests_pass_valgrind.c b/tools/ccanlint/tests/tests_pass_valgrind.c
new file mode 100644 (file)
index 0000000..30a806e
--- /dev/null
@@ -0,0 +1,201 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <ccan/foreach/foreach.h>
+#include <ccan/grab_file/grab_file.h>
+#include <ccan/str_talloc/str_talloc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+struct ccanlint run_tests_vg;
+
+/* Note: we already test safe_mode in run_tests.c */
+static const char *can_run_vg(struct manifest *m)
+{
+       unsigned int timeleft = default_timeout_ms;
+       char *output;
+
+       if (!run_command(m, &timeleft, &output,
+                        "valgrind -q --error-exitcode=0 true"))
+               return talloc_asprintf(m, "No valgrind support: %s", output);
+       return NULL;
+}
+
+/* Example output:
+==2749== Conditional jump or move depends on uninitialised value(s)
+==2749==    at 0x4026C60: strnlen (mc_replace_strmem.c:263)
+==2749==    by 0x40850E3: vfprintf (vfprintf.c:1614)
+==2749==    by 0x408EACF: printf (printf.c:35)
+==2749==    by 0x8048465: main (in /tmp/foo)
+==2749== 
+==2749== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
+==2749==    at 0x4025BD3: malloc (vg_replace_malloc.c:236)
+==2749==    by 0x8048444: main (in /tmp/foo)
+==2749== 
+*/
+
+static bool blank_line(const char *line)
+{
+       return line[strspn(line, "=0123456789 ")] == '\0';
+}
+
+static char *get_leaks(const char *output, char **errs)
+{
+       char *leaks = talloc_strdup(output, "");
+       unsigned int i, num;
+       char **lines = strsplit(output, output, "\n", &num);
+
+       *errs = talloc_strdup(output, "");
+       for (i = 0; i < num; i++) {
+               if (strstr(lines[i], " lost ")) {
+                       /* A leak... */
+                       if (strstr(lines[i], " definitely lost ")) {
+                               /* Definite leak, report. */
+                               while (lines[i] && !blank_line(lines[i])) {
+                                       leaks = talloc_append_string(leaks,
+                                                                    lines[i]);
+                                       leaks = talloc_append_string(leaks,
+                                                                    "\n");
+                                       i++;
+                               }
+                       } else
+                               /* Not definite, ignore. */
+                               while (lines[i] && !blank_line(lines[i]))
+                                       i++;
+               } else {
+                       /* A real error. */
+                       while (lines[i] && !blank_line(lines[i])) {
+                               *errs = talloc_append_string(*errs, lines[i]);
+                               *errs = talloc_append_string(*errs, "\n");
+                               i++;
+                       }
+               }
+       }
+       if (!leaks[0]) {
+               talloc_free(leaks);
+               leaks = NULL;
+       }
+       if (!(*errs)[0]) {
+               talloc_free(*errs);
+               *errs = NULL;
+       }
+       return leaks;
+}
+
+/* FIXME: Run examples, too! */
+static void do_run_tests_vg(struct manifest *m,
+                            bool keep,
+                           unsigned int *timeleft,
+                           struct score *score)
+{
+       struct ccan_file *i;
+       struct list_head *list;
+       char *cmdout;
+
+       /* This is slow, so we run once but grab leak info. */
+       score->total = 0;
+       foreach_ptr(list, &m->run_tests, &m->api_tests) {
+               list_for_each(list, i, list) {
+                       char *output, *err;
+                       score->total++;
+                       /* FIXME: Valgrind's output sucks.  XML is unreadable by
+                        * humans, and you can't have both. */
+                       run_command(score, timeleft, &cmdout,
+                                   "valgrind -q --error-exitcode=101"
+                                   " --leak-check=full"
+                                   " --log-fd=3 %s %s"
+                                   " 3> valgrind.log",
+                                   run_tests_vg.options ?
+                                   run_tests_vg.options : "",
+                                   i->compiled);
+                       output = grab_file(i, "valgrind.log", NULL);
+                       if (!output || output[0] == '\0') {
+                               err = NULL;
+                       } else {
+                               i->leak_info = get_leaks(output, &err);
+                       }
+                       if (err)
+                               score_file_error(score, i, 0, err);
+                       else
+                               score->score++;
+               }
+       }
+
+       if (score->score == score->total)
+               score->pass = true;
+}
+
+static void do_leakcheck_vg(struct manifest *m,
+                           bool keep,
+                           unsigned int *timeleft,
+                           struct score *score)
+{
+       struct ccan_file *i;
+       struct list_head *list;
+       bool leaks = false;
+
+       foreach_ptr(list, &m->run_tests, &m->api_tests) {
+               list_for_each(list, i, list) {
+                       if (i->leak_info) {
+                               score_file_error(score, i, 0, i->leak_info);
+                               leaks = true;
+                       }
+               }
+       }
+
+       if (!leaks) {
+               score->score = 1;
+               score->pass = true;
+       }
+}
+
+/* Gcc's warn_unused_result is fascist bullshit. */
+#define doesnt_matter()
+
+static void run_under_debugger_vg(struct manifest *m, struct score *score)
+{
+       struct file_error *first;
+       char *command;
+
+       if (!ask("Should I run the first failing test under the debugger?"))
+               return;
+
+       first = list_top(&score->per_file_errors, struct file_error, list);
+       command = talloc_asprintf(m, "valgrind --db-attach=yes%s %s",
+                                 run_tests_vg.options ?
+                                 run_tests_vg.options : "",
+                                 first->file->compiled);
+       if (system(command))
+               doesnt_matter();
+}
+
+struct ccanlint tests_pass_valgrind = {
+       .key = "tests_pass_valgrind",
+       .name = "Module's run and api tests succeed under valgrind",
+       .can_run = can_run_vg,
+       .check = do_run_tests_vg,
+       .handle = run_under_debugger_vg,
+       .takes_options = true,
+       .needs = "tests_pass"
+};
+
+REGISTER_TEST(tests_pass_valgrind);
+
+struct ccanlint tests_pass_valgrind_noleaks = {
+       .key = "tests_pass_valgrind_noleaks",
+       .name = "Module's run and api tests leak memory",
+       .check = do_leakcheck_vg,
+       .needs = "tests_pass_valgrind"
+};
+
+REGISTER_TEST(tests_pass_valgrind_noleaks);
diff --git a/tools/ccanlint/tests/trailing_whitespace.c b/tools/ccanlint/tests/trailing_whitespace.c
deleted file mode 100644 (file)
index a66fd74..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/* 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.  */
-static char *get_trailing_whitespace(const char *line)
-{
-       const char *e = strchr(line, 0);
-       while (e>line && (e[-1]==' ' || e[-1]=='\t'))
-               e--;
-       if (*e == 0)
-               return NULL; //there were no trailing spaces
-       if (e == line)
-               return NULL; //the line only consists of spaces
-
-       if (strlen(line) > 20)
-               return talloc_asprintf(line, "...'%s'",
-                                      line + strlen(line) - 20);
-       return talloc_asprintf(line, "'%s'", line);
-}
-
-static void check_trailing_whitespace(struct manifest *m,
-                                     bool keep,
-                                     unsigned int *timeleft,
-                                     struct score *score)
-{
-       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 no_trailing_whitespace = {
-       .key = "no_trailing_whitespace",
-       .name = "Module's source code has no trailing whitespace",
-       .check = check_trailing_whitespace,
-       .needs = ""
-};
-
-
-REGISTER_TEST(no_trailing_whitespace);