]> git.ozlabs.org Git - ccan/blobdiff - tools/ccanlint/ccanlint.c
ccanlint: added --test-dep-graph option
[ccan] / tools / ccanlint / ccanlint.c
index d09f68da19dc94932f4ba05d09a44d550e121608..5b45f4d2f2b2ca7294f7e2859d76cd7409e63b19 100644 (file)
@@ -318,7 +318,7 @@ static char *keep_test(const char *testname, void *unused)
 
 static char *skip_test(const char *testname, void *unused)
 {
-       btree_insert(cmdline_exclude, optarg);
+       btree_insert(cmdline_exclude, testname);
        return NULL;
 }
 
@@ -344,24 +344,102 @@ static char *list_tests(void *arg)
        exit(0);
 }
 
-static char *strip(const void *ctx, const char *line)
+static void test_dgraph_vertices(struct list_head *tests, const char *style)
 {
-       line += strcspn(line, IDENT_CHARS "-");
-       return talloc_strndup(ctx, line, strspn(line, IDENT_CHARS "-"));
+       const struct ccanlint *i;
+
+       list_for_each(tests, i, list) {
+               /*
+                * todo: escape labels in case ccanlint test keys have
+                *       characters interpreted as GraphViz syntax.
+                */
+               printf("\t\"%p\" [label=\"%s\"%s]\n", i, i->key, style);
+       }
+}
+
+static void test_dgraph_edges(struct list_head *tests)
+{
+       const struct ccanlint *i;
+       const struct dependent *d;
+
+       list_for_each(tests, i, list)
+               list_for_each(&i->dependencies, d, node)
+                       printf("\t\"%p\" -> \"%p\"\n", d->dependent, i);
+}
+
+static char *test_dependency_graph(void *arg)
+{
+       puts("digraph G {");
+
+       test_dgraph_vertices(&compulsory_tests, ", style=filled, fillcolor=yellow");
+       test_dgraph_vertices(&normal_tests,     "");
+
+       test_dgraph_edges(&compulsory_tests);
+       test_dgraph_edges(&normal_tests);
+
+       puts("}");
+
+       exit(0);
+}
+
+/* Remove empty lines. */
+static char **collapse(char **lines, unsigned int *nump)
+{
+       unsigned int i, j;
+       for (i = j = 0; lines[i]; i++) {
+               if (lines[i][0])
+                       lines[j++] = lines[i];
+       }
+       if (nump)
+               *nump = j;
+       return lines;
 }
 
-static void add_info_fails(struct ccan_file *info)
+static void add_info_options(struct ccan_file *info, bool mark_fails)
 {
        struct doc_section *d;
        unsigned int i;
+       struct ccanlint *test;
 
        list_for_each(get_ccan_file_docs(info), d, list) {
-               if (!streq(d->type, "fails"))
+               if (!streq(d->type, "ccanlint"))
                        continue;
 
-               for (i = 0; i < d->num_lines; i++)
-                       btree_insert(info_exclude, strip(info, d->lines[i]));
-               break;
+               for (i = 0; i < d->num_lines; i++) {
+                       char **words = collapse(strsplit(d, d->lines[i], " \t",
+                                                        NULL), NULL);
+                       if (!words[0])
+                               continue;
+
+                       if (strncmp(words[0], "//", 2) == 0)
+                               continue;
+
+                       test = find_test(words[0]);
+                       if (!test) {
+                               warnx("%s: unknown ccanlint test '%s'",
+                                     info->fullname, words[0]);
+                               continue;
+                       }
+
+                       if (!words[1]) {
+                               warnx("%s: no argument to test '%s'",
+                                     info->fullname, words[0]);
+                               continue;
+                       }
+
+                       /* Known failure? */
+                       if (strcasecmp(words[1], "FAIL") == 0) {
+                               if (mark_fails)
+                                       btree_insert(info_exclude, words[0]);
+                       } else {
+                               if (!test->takes_options)
+                                       warnx("%s: %s doesn't take options",
+                                             info->fullname, words[0]);
+                               /* Copy line exactly into options. */
+                               test->options = strstr(d->lines[i], words[0])
+                                       + strlen(words[0]);
+                       }
+               }
        }
 }
 
@@ -411,6 +489,8 @@ int main(int argc, char *argv[])
                         "do not compile anything");
        opt_register_noarg("-l|--list-tests", list_tests, NULL,
                         "list tests ccanlint performs (and exit)");
+       opt_register_noarg("--test-dep-graph", test_dependency_graph, NULL,
+                        "print dependency graph of tests in Graphviz .dot format");
        opt_register_arg("-k|--keep <testname>", keep_test, NULL, NULL,
                           "keep results of <testname> (can be used multiple times)");
        opt_register_noarg("--summary|-s", opt_set_bool, &summary,
@@ -457,7 +537,7 @@ int main(int argc, char *argv[])
 
                test = find_test(target);
                if (!test)
-                       err(1, "Unknown test to run '%s'", target);
+                       errx(1, "Unknown test to run '%s'", target);
                skip_unrelated_tests(test);
        }
 
@@ -469,9 +549,8 @@ int main(int argc, char *argv[])
                }
        }
 
-       /* --target overrides _info excludes */
-       if (!target)
-               add_info_fails(m->info_file);
+       /* --target overrides known FAIL from _info */
+       add_info_options(m->info_file, !target);
 
        while ((i = get_next_test(&normal_tests)) != NULL)
                run_test(i, summary, &score, &total_score, m);