]> git.ozlabs.org Git - ccan/commitdiff
Move ccanlint tests into subdirectories.
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 28 May 2009 04:14:35 +0000 (13:44 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 28 May 2009 04:14:35 +0000 (13:44 +0930)
13 files changed:
tools/ccanlint/Makefile
tools/ccanlint/compulsory_tests/has_info.c [new file with mode: 0644]
tools/ccanlint/compulsory_tests/has_main_header.c [new file with mode: 0644]
tools/ccanlint/compulsory_tests/has_tests.c [new file with mode: 0644]
tools/ccanlint/has_info.c [deleted file]
tools/ccanlint/has_info_documentation.c [deleted file]
tools/ccanlint/has_main_header.c [deleted file]
tools/ccanlint/has_tests.c [deleted file]
tools/ccanlint/idempotent.c [deleted file]
tools/ccanlint/tests/has_info_documentation.c [new file with mode: 0644]
tools/ccanlint/tests/idempotent.c [new file with mode: 0644]
tools/ccanlint/tests/trailing_whitespace.c [new file with mode: 0644]
tools/ccanlint/trailing_whitespace.c [deleted file]

index 87ff2d0edce161571b0e7956869e583a88ff75ab..cd507e5d08d8237ba06b0c138a85c39dd39d25c4 100644 (file)
@@ -1,32 +1,21 @@
-OBJS := tools/ccanlint/has_info.o \
-       tools/ccanlint/has_main_header.o \
-       tools/ccanlint/has_tests.o \
-       tools/ccanlint/trailing_whitespace.o \
-       tools/ccanlint/idempotent.o \
-       tools/ccanlint/has_info_documentation.o \
+TEST_CFILES := $(wildcard tools/ccanlint/compulsory_tests/*.c) \
+       $(wildcard tools/ccanlint/tests/*.c)
+TEST_OBJS := $(TEST_CFILES:.c=.o)
 
 
-FUTURE:=tools/ccanlint/if_have_not_ifdef.o \
-       tools/ccanlint/needs_depends.o \
-       tools/ccanlint/has_info_documentation.o \
-       tools/ccanlint/has_header_documentation.o \
-       tools/ccanlint/has_tests.o \
-       tools/ccanlint/builds_ok.o \
-       tools/ccanlint/builds_ok_all_have_variants.o \
-       tools/ccanlint/run_tests.o \
-       tools/ccanlint/test_coverage.o \
+CORE_OBJS := tools/ccanlint/ccanlint.o \
+       tools/ccanlint/file_analysis.o \
+       tools/doc_extract-core.o \
+       ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o \
+       ccan/talloc/talloc.o ccan/noerr/noerr.o
+
+OBJS := $(CORE_OBJS) $(TEST_OBJS)
 
 tools/ccanlint/generated-init-tests: $(OBJS)
        cat $(OBJS:.o=.c) | sed -n 's/^struct ccanlint \([A-Za-z0-9_]*\) = {/{ extern struct ccanlint \1; list_add(\&tests, \&\1.list); }/p' >$@
 
 tools/ccanlint/ccanlint.o: tools/ccanlint/generated-init-tests
 
 
 tools/ccanlint/generated-init-tests: $(OBJS)
        cat $(OBJS:.o=.c) | sed -n 's/^struct ccanlint \([A-Za-z0-9_]*\) = {/{ extern struct ccanlint \1; list_add(\&tests, \&\1.list); }/p' >$@
 
 tools/ccanlint/ccanlint.o: tools/ccanlint/generated-init-tests
 
-tools/ccanlint/ccanlint: \
-       $(OBJS)                 \
-       tools/ccanlint/ccanlint.o \
-       tools/ccanlint/file_analysis.o \
-       tools/doc_extract-core.o \
-       ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o \
-       ccan/talloc/talloc.o ccan/noerr/noerr.o
+tools/ccanlint/ccanlint: $(OBJS)
 
 ccanlint-clean:
        $(RM) tools/ccanlint/generated-init-tests
 
 ccanlint-clean:
        $(RM) tools/ccanlint/generated-init-tests
diff --git a/tools/ccanlint/compulsory_tests/has_info.c b/tools/ccanlint/compulsory_tests/has_info.c
new file mode 100644 (file)
index 0000000..e7f2622
--- /dev/null
@@ -0,0 +1,78 @@
+#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>
+
+static void *check_has_info(struct manifest *m)
+{
+       if (m->info_file)
+               return NULL;
+       return m;
+}
+
+static const char *describe_has_info(struct manifest *m, void *check_result)
+{
+       return "You have no _info.c file.\n\n"
+       "The file _info.c 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, void *check_result)
+{
+       FILE *info;
+
+       if (!ask("Should I create a template _info.c file for you?"))
+               return;
+
+       info = fopen("_info.c", "w");
+       if (!info)
+               err(1, "Trying to create a template _info.c");
+
+       if (fprintf(info, template, m->basename) < 0) {
+               unlink_noerr("_info.c");
+               err(1, "Writing template into _info.c");
+       }
+       fclose(info);
+}
+
+struct ccanlint has_info = {
+       .name = "Has _info.c file",
+       .check = check_has_info,
+       .describe = describe_has_info,
+       .handle = create_info_template,
+};
diff --git a/tools/ccanlint/compulsory_tests/has_main_header.c b/tools/ccanlint/compulsory_tests/has_main_header.c
new file mode 100644 (file)
index 0000000..978bfcb
--- /dev/null
@@ -0,0 +1,41 @@
+#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)
+{
+       struct ccan_file *f;
+
+       list_for_each(&m->h_files, f, list) {
+               if (strstarts(f->name, m->basename)
+                   && strlen(f->name) == strlen(m->basename) + 2)
+                       return NULL;
+       }
+       return m;
+}
+
+static const char *describe_has_main_header(struct manifest *m,
+                                           void *check_result)
+{
+       return talloc_asprintf(m,
+       "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 has_main_header = {
+       .name = "No main header file",
+       .check = check_has_main_header,
+       .describe = describe_has_main_header,
+};
diff --git a/tools/ccanlint/compulsory_tests/has_tests.c b/tools/ccanlint/compulsory_tests/has_tests.c
new file mode 100644 (file)
index 0000000..a71a156
--- /dev/null
@@ -0,0 +1,130 @@
+#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>
+
+static char test_is_not_dir[] = "test is not a directory";
+
+static void *check_has_tests(struct manifest *m)
+{
+       struct stat st;
+
+       if (lstat("test", &st) != 0) {
+               if (errno != ENOENT)
+                       err(1, "statting test/");
+               return "You have no test directory";
+       }
+
+       if (!S_ISDIR(st.st_mode))
+               return test_is_not_dir;
+
+       if (list_empty(&m->api_tests)
+           && list_empty(&m->run_tests)
+           && list_empty(&m->compile_ok_tests)) {
+               if (list_empty(&m->compile_fail_tests)) 
+                       return "You have no tests in the test directory";
+               else
+                       return "You have no positive tests in the test directory";
+       }
+       return NULL;
+}
+
+static const char *describe_has_tests(struct manifest *m, void *check_result)
+{
+       return talloc_asprintf(m, "%s\n\n"
+        "CCAN modules have a directory called test/ which contains tests.\n"
+       "There are four kinds of tests: api, run, compile_ok and compile_fail:\n"
+       "you can tell which type of test a C file is by its name, eg 'run.c'\n"
+       "and 'run-simple.c' are both run tests.\n\n"
+
+       "The simplest kind of test is a run test, which must compile with no\n"
+       "warnings, and then run: it is expected to use libtap to report its\n"
+       "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.\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 the tests are not linked against the files in the\n"
+       "above: you should directly #include those C files you want.  This\n"
+       "allows access to static functions and use special effects inside\n"
+       "test files\n", (char *)check_result);
+}
+
+static void handle_no_tests(struct manifest *m, void *check_result)
+{
+       FILE *run;
+       struct ccan_file *i;
+
+       if (check_result == test_is_not_dir)
+               return;
+
+       if (!ask("Should I create a template test/run.c file for you?"))
+               return;
+
+       if (mkdir("test", 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");
+
+       fputs("/* Include the main header first, to test it works */\n", run);
+       fprintf(run, "#include \"%s/%s.h\"\n", m->basename, m->basename);
+       fputs("/* Include the C files directly. */\n", run);
+       list_for_each(&m->c_files, i, list)
+               fprintf(run, "#include \"%s/%s\"\n", m->basename, i->name);
+       fputs("#include \"tap/tap.h\"\n", run);
+       fputs("\n", run);
+
+       fputs("int main(void)\n", run);
+       fputs("{\n", run);
+       fputs("\t/* This is how many tests you plan to run */\n", run);
+       fputs("\tplan_tests(3);\n", run);
+       fputs("\n", run);
+       fputs("\t/* Simple thing we expect to succeed */\n", run);
+       fputs("\tok1(some_test())\n", run);
+       fputs("\t/* Same, with an explicit description of the test. */\n", run);
+       fputs("\tok(some_test(), \"%s with no args should return 1\", \"some_test\")\n", run);
+       fputs("\t/* How to print out messages for debugging. */\n", run);
+       fputs("\tdiag(\"Address of some_test is %p\", &some_test)\n", run);
+       fputs("\t/* Conditional tests must be explicitly skipped. */\n", run);
+       fputs("#if HAVE_SOME_FEATURE\n", run);
+       fputs("\tok1(test_some_feature())\n", run);
+       fputs("#else\n", run);
+       fputs("\tskip(1, \"Don\'t have SOME_FEATURE\")\n", run);
+       fputs("#endif\n", run);
+       fputs("\n", run);
+       fputs("\t/* This exits depending on whether all tests passed */\n", run);
+       fputs("\treturn exit_status();\n", run);
+       fputs("}\n", run);
+
+       fclose(run);
+}      
+
+struct ccanlint has_tests = {
+       .name = "No tests",
+       .check = check_has_tests,
+       .describe = describe_has_tests,
+       .handle = handle_no_tests,
+};
diff --git a/tools/ccanlint/has_info.c b/tools/ccanlint/has_info.c
deleted file mode 100644 (file)
index 563eb2f..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "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>
-
-static void *check_has_info(struct manifest *m)
-{
-       if (m->info_file)
-               return NULL;
-       return m;
-}
-
-static const char *describe_has_info(struct manifest *m, void *check_result)
-{
-       return "You have no _info.c file.\n\n"
-       "The file _info.c 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, void *check_result)
-{
-       FILE *info;
-
-       if (!ask("Should I create a template _info.c file for you?"))
-               return;
-
-       info = fopen("_info.c", "w");
-       if (!info)
-               err(1, "Trying to create a template _info.c");
-
-       if (fprintf(info, template, m->basename) < 0) {
-               unlink_noerr("_info.c");
-               err(1, "Writing template into _info.c");
-       }
-       fclose(info);
-}
-
-struct ccanlint has_info = {
-       .name = "Has _info.c file",
-       .check = check_has_info,
-       .describe = describe_has_info,
-       .handle = create_info_template,
-};
diff --git a/tools/ccanlint/has_info_documentation.c b/tools/ccanlint/has_info_documentation.c
deleted file mode 100644 (file)
index 3142b7b..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-#include "ccanlint.h"
-#include "../doc_extract.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>
-
-struct info_docs
-{
-       bool summary;
-       bool description;
-       bool example;
-};
-
-static void *check_has_info_documentation(struct manifest *m)
-{
-       struct list_head *infodocs = get_ccan_file_docs(m->info_file);
-       struct doc_section *d;
-       struct info_docs id = { false, false, false };
-
-       list_for_each(infodocs, d, list) {
-               if (!streq(d->function, m->basename))
-                       continue;
-               if (streq(d->type, "summary"))
-                       id.summary = true;
-               if (streq(d->type, "description"))
-                       id.description = true;
-               if (streq(d->type, "example"))
-                       id.example = true;
-       }
-
-       if (id.summary && id.description && id.example)
-               return NULL;
-       return talloc_memdup(m, &id, sizeof(id));
-}
-
-/* This is defined below. */
-extern struct ccanlint has_info_documentation;
-
-static void create_info_template_doc(struct manifest *m, void *check_result)
-{
-       int fd = open("_info.c.new", O_WRONLY|O_CREAT|O_EXCL, 0666);
-       FILE *new;
-       char *oldcontents;
-
-       if (fd < 0 || !(new = fdopen(fd, "w")))
-               err(1, "Creating _info.c.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.c.new");
-               err(1, "Writing to _info.c.new to insert documentation");
-       }
-
-       oldcontents = grab_file(m, "_info.c", NULL);
-       if (!oldcontents) {
-               unlink_noerr("_info.c.new");
-               err(1, "Reading _info.c");
-       }
-       if (fprintf(new, "%s", oldcontents) < 0) {
-               unlink_noerr("_info.c.new");
-               err(1, "Appending _info.c to _info.c.new");
-       }
-       if (fclose(new) != 0) {
-               unlink_noerr("_info.c.new");
-               err(1, "Closing _info.c.new");
-       }
-       if (rename("_info.c.new", "_info.c") != 0) {
-               unlink_noerr("_info.c.new");
-               err(1, "Renaming _info.c.new to _info.c");
-       }
-}
-
-static const char *describe_has_info_documentation(struct manifest *m,
-                                                  void *check_result)
-{
-       struct info_docs *id = check_result;
-       char *reason = talloc_strdup(m, "");
-
-       if (!id->summary) {
-               has_info_documentation.handle = create_info_template_doc;
-               reason = talloc_asprintf_append(reason,
-               "Your _info.c has no module documentation.\n\n"
-               "CCAN modules use /**-style comments for documentation: the\n"
-               "overall documentation belongs in the _info.c metafile.\n");
-       }
-       if (!id->description)
-               reason = talloc_asprintf_append(reason,
-               "Your _info.c has no module description.\n\n"
-               "The lines after the first summary line in the _info.c file\n"
-               "documentation should describe the purpose and use of the\n"
-               "overall package\n");
-       if (!id->example)
-               reason = talloc_asprintf_append(reason,
-               "Your _info.c has no module example.\n\n"
-               "There should be an Example: section of the _info.c documentation\n"
-               "which provides a concise toy program which uses your module\n");
-       return reason;
-}
-
-static unsigned int has_info_documentation_score(struct manifest *m,
-                                                void *check_result)
-{
-       struct info_docs *id = check_result;
-       return id->summary + id->description + id->example;
-}
-
-struct ccanlint has_info_documentation = {
-       .name = "Documentation in _info.c",
-       .total_score = 3,
-       .score = has_info_documentation_score,
-       .check = check_has_info_documentation,
-       .describe = describe_has_info_documentation,
-};
diff --git a/tools/ccanlint/has_main_header.c b/tools/ccanlint/has_main_header.c
deleted file mode 100644 (file)
index e1b7119..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "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)
-{
-       struct ccan_file *f;
-
-       list_for_each(&m->h_files, f, list) {
-               if (strstarts(f->name, m->basename)
-                   && strlen(f->name) == strlen(m->basename) + 2)
-                       return NULL;
-       }
-       return m;
-}
-
-static const char *describe_has_main_header(struct manifest *m,
-                                           void *check_result)
-{
-       return talloc_asprintf(m,
-       "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 has_main_header = {
-       .name = "No main header file",
-       .check = check_has_main_header,
-       .describe = describe_has_main_header,
-};
diff --git a/tools/ccanlint/has_tests.c b/tools/ccanlint/has_tests.c
deleted file mode 100644 (file)
index efea3c0..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-#include "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>
-
-static char test_is_not_dir[] = "test is not a directory";
-
-static void *check_has_tests(struct manifest *m)
-{
-       struct stat st;
-
-       if (lstat("test", &st) != 0) {
-               if (errno != ENOENT)
-                       err(1, "statting test/");
-               return "You have no test directory";
-       }
-
-       if (!S_ISDIR(st.st_mode))
-               return test_is_not_dir;
-
-       if (list_empty(&m->api_tests)
-           && list_empty(&m->run_tests)
-           && list_empty(&m->compile_ok_tests)) {
-               if (list_empty(&m->compile_fail_tests)) 
-                       return "You have no tests in the test directory";
-               else
-                       return "You have no positive tests in the test directory";
-       }
-       return NULL;
-}
-
-static const char *describe_has_tests(struct manifest *m, void *check_result)
-{
-       return talloc_asprintf(m, "%s\n\n"
-        "CCAN modules have a directory called test/ which contains tests.\n"
-       "There are four kinds of tests: api, run, compile_ok and compile_fail:\n"
-       "you can tell which type of test a C file is by its name, eg 'run.c'\n"
-       "and 'run-simple.c' are both run tests.\n\n"
-
-       "The simplest kind of test is a run test, which must compile with no\n"
-       "warnings, and then run: it is expected to use libtap to report its\n"
-       "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.\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 the tests are not linked against the files in the\n"
-       "above: you should directly #include those C files you want.  This\n"
-       "allows access to static functions and use special effects inside\n"
-       "test files\n", (char *)check_result);
-}
-
-static void handle_no_tests(struct manifest *m, void *check_result)
-{
-       FILE *run;
-       struct ccan_file *i;
-
-       if (check_result == test_is_not_dir)
-               return;
-
-       if (!ask("Should I create a template test/run.c file for you?"))
-               return;
-
-       if (mkdir("test", 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");
-
-       fputs("/* Include the main header first, to test it works */\n", run);
-       fprintf(run, "#include \"%s/%s.h\"\n", m->basename, m->basename);
-       fputs("/* Include the C files directly. */\n", run);
-       list_for_each(&m->c_files, i, list)
-               fprintf(run, "#include \"%s/%s\"\n", m->basename, i->name);
-       fputs("#include \"tap/tap.h\"\n", run);
-       fputs("\n", run);
-
-       fputs("int main(void)\n", run);
-       fputs("{\n", run);
-       fputs("\t/* This is how many tests you plan to run */\n", run);
-       fputs("\tplan_tests(3);\n", run);
-       fputs("\n", run);
-       fputs("\t/* Simple thing we expect to succeed */\n", run);
-       fputs("\tok1(some_test())\n", run);
-       fputs("\t/* Same, with an explicit description of the test. */\n", run);
-       fputs("\tok(some_test(), \"%s with no args should return 1\", \"some_test\")\n", run);
-       fputs("\t/* How to print out messages for debugging. */\n", run);
-       fputs("\tdiag(\"Address of some_test is %p\", &some_test)\n", run);
-       fputs("\t/* Conditional tests must be explicitly skipped. */\n", run);
-       fputs("#if HAVE_SOME_FEATURE\n", run);
-       fputs("\tok1(test_some_feature())\n", run);
-       fputs("#else\n", run);
-       fputs("\tskip(1, \"Don\'t have SOME_FEATURE\")\n", run);
-       fputs("#endif\n", run);
-       fputs("\n", run);
-       fputs("\t/* This exits depending on whether all tests passed */\n", run);
-       fputs("\treturn exit_status();\n", run);
-       fputs("}\n", run);
-
-       fclose(run);
-}      
-
-struct ccanlint has_tests = {
-       .name = "No tests",
-       .check = check_has_tests,
-       .describe = describe_has_tests,
-       .handle = handle_no_tests,
-};
diff --git a/tools/ccanlint/idempotent.c b/tools/ccanlint/idempotent.c
deleted file mode 100644 (file)
index fa65b05..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-#include "ccanlint.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 "../tools.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 MY_HEADER_H\n"
-  "#define MY_HEADER_H\n"
-  "...\n"
-  "#endif /* MY_HEADER_H */\n";
-
-static char *report_idem(struct ccan_file *f, char *sofar)
-{
-       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 sofar;
-
-       for (i = 0; i < f->num_lines; i++) {
-               if (line_info[i].type == DOC_LINE
-                   || line_info[i].type == COMMENT_LINE)
-                       continue;
-               if (line_info[i].type == CODE_LINE)
-                       return talloc_asprintf_append(sofar,
-                             "%s:%u:expect first non-comment line to be #ifndef.\n", f->name, i+1);
-               else if (line_info[i].type == PREPROC_LINE)
-                       break;
-       }
-
-       /* No code at all?  Don't complain. */
-       if (i == f->num_lines)
-               return sofar;
-
-       first_preproc_line = i;
-       for (i = first_preproc_line+1; i < f->num_lines; i++) {
-               if (line_info[i].type == DOC_LINE
-                   || line_info[i].type == COMMENT_LINE)
-                       continue;
-               if (line_info[i].type == CODE_LINE)
-                       return talloc_asprintf_append(sofar,
-                             "%s:%u:expect second line to be #define.\n", f->name, i+1);
-               else if (line_info[i].type == PREPROC_LINE)
-                       break;
-       }
-
-       /* No code at all?  Weird. */
-       if (i == f->num_lines)
-               return sofar;
-
-       /* We expect a condition on this line. */
-       if (!line_info[i].cond) {
-               return talloc_asprintf_append(sofar,
-                                             "%s:%u:expected #ifndef.\n",
-                                             f->name, first_preproc_line+1);
-       }
-
-       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) {
-               return talloc_asprintf_append(sofar,
-                                             "%s:%u:expected #ifndef.\n",
-                                             f->name, first_preproc_line+1);
-       }
-
-       /* And this to be #define <symbol> */
-       if (!get_token(&line, "#"))
-               abort();
-       if (!get_token(&line, "define")) {
-               return talloc_asprintf_append(sofar,
-                             "%s:%u:expected '#define %s'.\n",
-                             f->name, i+1, line_info[i].cond->symbol);
-       }
-       sym = get_symbol_token(f, &line);
-       if (!sym || !streq(sym, line_info[i].cond->symbol)) {
-               return talloc_asprintf_append(sofar,
-                             "%s:%u:expected '#define %s'.\n",
-                             f->name, i+1, line_info[i].cond->symbol);
-       }
-
-       /* 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)
-                       return talloc_asprintf_append(sofar,
-                             "%s:%u:code outside idempotent region.\n",
-                             f->name, i+1);
-       }
-
-       return sofar;
-}
-
-static void *check_idempotent(struct manifest *m)
-{
-       struct ccan_file *f;
-       char *report = NULL;
-
-       list_for_each(&m->h_files, f, list)
-               report = report_idem(f, report);
-
-       return report;
-}
-
-static const char *describe_idempotent(struct manifest *m, void *check_result)
-{
-       return talloc_asprintf(check_result, 
-                              "Some headers not idempotent:\n"
-                              "%s\n%s", (char *)check_result,
-                              explain);
-}
-
-struct ccanlint idempotent = {
-       .name = "Headers are #ifndef/#define idempotent wrapped",
-       .total_score = 1,
-       .check = check_idempotent,
-       .describe = describe_idempotent,
-};
diff --git a/tools/ccanlint/tests/has_info_documentation.c b/tools/ccanlint/tests/has_info_documentation.c
new file mode 100644 (file)
index 0000000..4202880
--- /dev/null
@@ -0,0 +1,130 @@
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/doc_extract.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>
+
+struct info_docs
+{
+       bool summary;
+       bool description;
+       bool example;
+};
+
+static void *check_has_info_documentation(struct manifest *m)
+{
+       struct list_head *infodocs = get_ccan_file_docs(m->info_file);
+       struct doc_section *d;
+       struct info_docs id = { false, false, false };
+
+       list_for_each(infodocs, d, list) {
+               if (!streq(d->function, m->basename))
+                       continue;
+               if (streq(d->type, "summary"))
+                       id.summary = true;
+               if (streq(d->type, "description"))
+                       id.description = true;
+               if (streq(d->type, "example"))
+                       id.example = true;
+       }
+
+       if (id.summary && id.description && id.example)
+               return NULL;
+       return talloc_memdup(m, &id, sizeof(id));
+}
+
+/* This is defined below. */
+extern struct ccanlint has_info_documentation;
+
+static void create_info_template_doc(struct manifest *m, void *check_result)
+{
+       int fd = open("_info.c.new", O_WRONLY|O_CREAT|O_EXCL, 0666);
+       FILE *new;
+       char *oldcontents;
+
+       if (fd < 0 || !(new = fdopen(fd, "w")))
+               err(1, "Creating _info.c.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.c.new");
+               err(1, "Writing to _info.c.new to insert documentation");
+       }
+
+       oldcontents = grab_file(m, "_info.c", NULL);
+       if (!oldcontents) {
+               unlink_noerr("_info.c.new");
+               err(1, "Reading _info.c");
+       }
+       if (fprintf(new, "%s", oldcontents) < 0) {
+               unlink_noerr("_info.c.new");
+               err(1, "Appending _info.c to _info.c.new");
+       }
+       if (fclose(new) != 0) {
+               unlink_noerr("_info.c.new");
+               err(1, "Closing _info.c.new");
+       }
+       if (rename("_info.c.new", "_info.c") != 0) {
+               unlink_noerr("_info.c.new");
+               err(1, "Renaming _info.c.new to _info.c");
+       }
+}
+
+static const char *describe_has_info_documentation(struct manifest *m,
+                                                  void *check_result)
+{
+       struct info_docs *id = check_result;
+       char *reason = talloc_strdup(m, "");
+
+       if (!id->summary) {
+               has_info_documentation.handle = create_info_template_doc;
+               reason = talloc_asprintf_append(reason,
+               "Your _info.c has no module documentation.\n\n"
+               "CCAN modules use /**-style comments for documentation: the\n"
+               "overall documentation belongs in the _info.c metafile.\n");
+       }
+       if (!id->description)
+               reason = talloc_asprintf_append(reason,
+               "Your _info.c has no module description.\n\n"
+               "The lines after the first summary line in the _info.c file\n"
+               "documentation should describe the purpose and use of the\n"
+               "overall package\n");
+       if (!id->example)
+               reason = talloc_asprintf_append(reason,
+               "Your _info.c has no module example.\n\n"
+               "There should be an Example: section of the _info.c documentation\n"
+               "which provides a concise toy program which uses your module\n");
+       return reason;
+}
+
+static unsigned int has_info_documentation_score(struct manifest *m,
+                                                void *check_result)
+{
+       struct info_docs *id = check_result;
+       return id->summary + id->description + id->example;
+}
+
+struct ccanlint has_info_documentation = {
+       .name = "Documentation in _info.c",
+       .total_score = 3,
+       .score = has_info_documentation_score,
+       .check = check_has_info_documentation,
+       .describe = describe_has_info_documentation,
+};
diff --git a/tools/ccanlint/tests/idempotent.c b/tools/ccanlint/tests/idempotent.c
new file mode 100644 (file)
index 0000000..0aa7d8b
--- /dev/null
@@ -0,0 +1,139 @@
+#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 MY_HEADER_H\n"
+  "#define MY_HEADER_H\n"
+  "...\n"
+  "#endif /* MY_HEADER_H */\n";
+
+static char *report_idem(struct ccan_file *f, char *sofar)
+{
+       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 sofar;
+
+       for (i = 0; i < f->num_lines; i++) {
+               if (line_info[i].type == DOC_LINE
+                   || line_info[i].type == COMMENT_LINE)
+                       continue;
+               if (line_info[i].type == CODE_LINE)
+                       return talloc_asprintf_append(sofar,
+                             "%s:%u:expect first non-comment line to be #ifndef.\n", f->name, i+1);
+               else if (line_info[i].type == PREPROC_LINE)
+                       break;
+       }
+
+       /* No code at all?  Don't complain. */
+       if (i == f->num_lines)
+               return sofar;
+
+       first_preproc_line = i;
+       for (i = first_preproc_line+1; i < f->num_lines; i++) {
+               if (line_info[i].type == DOC_LINE
+                   || line_info[i].type == COMMENT_LINE)
+                       continue;
+               if (line_info[i].type == CODE_LINE)
+                       return talloc_asprintf_append(sofar,
+                             "%s:%u:expect second line to be #define.\n", f->name, i+1);
+               else if (line_info[i].type == PREPROC_LINE)
+                       break;
+       }
+
+       /* No code at all?  Weird. */
+       if (i == f->num_lines)
+               return sofar;
+
+       /* We expect a condition on this line. */
+       if (!line_info[i].cond) {
+               return talloc_asprintf_append(sofar,
+                                             "%s:%u:expected #ifndef.\n",
+                                             f->name, first_preproc_line+1);
+       }
+
+       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) {
+               return talloc_asprintf_append(sofar,
+                                             "%s:%u:expected #ifndef.\n",
+                                             f->name, first_preproc_line+1);
+       }
+
+       /* And this to be #define <symbol> */
+       if (!get_token(&line, "#"))
+               abort();
+       if (!get_token(&line, "define")) {
+               return talloc_asprintf_append(sofar,
+                             "%s:%u:expected '#define %s'.\n",
+                             f->name, i+1, line_info[i].cond->symbol);
+       }
+       sym = get_symbol_token(f, &line);
+       if (!sym || !streq(sym, line_info[i].cond->symbol)) {
+               return talloc_asprintf_append(sofar,
+                             "%s:%u:expected '#define %s'.\n",
+                             f->name, i+1, line_info[i].cond->symbol);
+       }
+
+       /* 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)
+                       return talloc_asprintf_append(sofar,
+                             "%s:%u:code outside idempotent region.\n",
+                             f->name, i+1);
+       }
+
+       return sofar;
+}
+
+static void *check_idempotent(struct manifest *m)
+{
+       struct ccan_file *f;
+       char *report = NULL;
+
+       list_for_each(&m->h_files, f, list)
+               report = report_idem(f, report);
+
+       return report;
+}
+
+static const char *describe_idempotent(struct manifest *m, void *check_result)
+{
+       return talloc_asprintf(check_result, 
+                              "Some headers not idempotent:\n"
+                              "%s\n%s", (char *)check_result,
+                              explain);
+}
+
+struct ccanlint idempotent = {
+       .name = "Headers are #ifndef/#define idempotent wrapped",
+       .total_score = 1,
+       .check = check_idempotent,
+       .describe = describe_idempotent,
+};
diff --git a/tools/ccanlint/tests/trailing_whitespace.c b/tools/ccanlint/tests/trailing_whitespace.c
new file mode 100644 (file)
index 0000000..f522d31
--- /dev/null
@@ -0,0 +1,47 @@
+/* Trailing whitespace test.  Almost embarrassing, but trivial. */
+#include <tools/ccanlint/ccanlint.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+
+static char *report_on_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)
+{
+       char *report;
+
+       report = report_on_lines(&m->c_files, report_on_trailing_whitespace,
+                                NULL);
+       report = report_on_lines(&m->h_files, report_on_trailing_whitespace,
+                                report);
+
+       return report;
+}
+
+static const char *describe_trailing_whitespace(struct manifest *m,
+                                               void *check_result)
+{
+       return talloc_asprintf(check_result, 
+                              "Some source files have trailing whitespace:\n"
+                              "%s", (char *)check_result);
+}
+
+struct ccanlint trailing_whitespace = {
+       .name = "No lines with unnecessary trailing whitespace",
+       .total_score = 1,
+       .check = check_trailing_whitespace,
+       .describe = describe_trailing_whitespace,
+};
diff --git a/tools/ccanlint/trailing_whitespace.c b/tools/ccanlint/trailing_whitespace.c
deleted file mode 100644 (file)
index 96ab0d9..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Trailing whitespace test.  Almost embarrassing, but trivial. */
-#include "ccanlint.h"
-#include <ccan/talloc/talloc.h>
-#include <ccan/str/str.h>
-
-static char *report_on_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)
-{
-       char *report;
-
-       report = report_on_lines(&m->c_files, report_on_trailing_whitespace,
-                                NULL);
-       report = report_on_lines(&m->h_files, report_on_trailing_whitespace,
-                                report);
-
-       return report;
-}
-
-static const char *describe_trailing_whitespace(struct manifest *m,
-                                               void *check_result)
-{
-       return talloc_asprintf(check_result, 
-                              "Some source files have trailing whitespace:\n"
-                              "%s", (char *)check_result);
-}
-
-struct ccanlint trailing_whitespace = {
-       .name = "No lines with unnecessary trailing whitespace",
-       .total_score = 1,
-       .check = check_trailing_whitespace,
-       .describe = describe_trailing_whitespace,
-};