|
|
@@ -0,0 +1,203 @@
|
|
|
+#include "tools/ccanlint/ccanlint.h"
|
|
|
+#include "ccan/tap/tap.h"
|
|
|
+#include "tools/ccanlint/file_analysis.c"
|
|
|
+#include <sys/types.h>
|
|
|
+#include <sys/stat.h>
|
|
|
+#include <fcntl.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <assert.h>
|
|
|
+
|
|
|
+/* This is our test file. */
|
|
|
+struct test {
|
|
|
+ enum line_info_type type;
|
|
|
+ bool continued;
|
|
|
+ const char *line;
|
|
|
+};
|
|
|
+
|
|
|
+static struct test testfile[] = {
|
|
|
+ { PREPROC_LINE, false, "#ifndef TEST_H" },
|
|
|
+ { PREPROC_LINE, false, "#define TEST_H" },
|
|
|
+ { DOC_LINE, false, "/**" },
|
|
|
+ { DOC_LINE, false, " * Comment here." },
|
|
|
+ { DOC_LINE, false, " * Comment here too." },
|
|
|
+ { DOC_LINE, false, " */" },
|
|
|
+ { COMMENT_LINE, false, "// Normal one-line comment" },
|
|
|
+ { COMMENT_LINE, false, " // Spaced one-line comment" },
|
|
|
+ { COMMENT_LINE, false, "/* Normal one-line comment */" },
|
|
|
+ { COMMENT_LINE, false, " /* Spaced one-line comment */" },
|
|
|
+ { COMMENT_LINE, false, " /* Spaced two-line comment" },
|
|
|
+ { COMMENT_LINE, false, " continued comment */" },
|
|
|
+ { CODE_LINE, false, "extern int x;"},
|
|
|
+ { CODE_LINE, false, "extern int y; // With new-style comment"},
|
|
|
+ { CODE_LINE, false, "extern int z; /* With old-style comment */"},
|
|
|
+ { CODE_LINE, false, "extern int v; /* With two-line comment"},
|
|
|
+ { COMMENT_LINE, false, " Second line of comment"},
|
|
|
+ { COMMENT_LINE, false, "/* comment1 */ // comment 2"},
|
|
|
+ { COMMENT_LINE, false, "/* comment1 */ /* comment 2 */ "},
|
|
|
+ { CODE_LINE, false, "/* comment1 */ code; /* comment 2 */ "},
|
|
|
+ { CODE_LINE, false, "/* comment1 */ code; // comment 2"},
|
|
|
+ { COMMENT_LINE, false, "/* comment start \\"},
|
|
|
+ { COMMENT_LINE, true, " comment finish */"},
|
|
|
+ { PREPROC_LINE, false, "#define foo \\"},
|
|
|
+ { PREPROC_LINE, true, " (bar + \\"},
|
|
|
+ { PREPROC_LINE, true, " baz)"},
|
|
|
+ { CODE_LINE, false, "extern int \\"},
|
|
|
+ { CODE_LINE, true, "#x;"},
|
|
|
+
|
|
|
+ /* Variants of the same thing. */
|
|
|
+ { PREPROC_LINE, false, "#ifdef BAR"},
|
|
|
+ { CODE_LINE, false, "BAR"},
|
|
|
+ { PREPROC_LINE, false, "#else"},
|
|
|
+ { CODE_LINE, false, "!BAR"},
|
|
|
+ { PREPROC_LINE, false, "#endif"},
|
|
|
+
|
|
|
+ { PREPROC_LINE, false, "#if defined BAR"},
|
|
|
+ { CODE_LINE, false, "BAR"},
|
|
|
+ { PREPROC_LINE, false, "#else"},
|
|
|
+ { CODE_LINE, false, "!BAR"},
|
|
|
+ { PREPROC_LINE, false, "#endif"},
|
|
|
+
|
|
|
+ { PREPROC_LINE, false, "#if defined(BAR)"},
|
|
|
+ { CODE_LINE, false, "BAR"},
|
|
|
+ { PREPROC_LINE, false, "#else"},
|
|
|
+ { CODE_LINE, false, "!BAR"},
|
|
|
+ { PREPROC_LINE, false, "#endif"},
|
|
|
+
|
|
|
+ { PREPROC_LINE, false, "#if !defined(BAR)"},
|
|
|
+ { CODE_LINE, false, "!BAR"},
|
|
|
+ { PREPROC_LINE, false, "#else"},
|
|
|
+ { CODE_LINE, false, "BAR"},
|
|
|
+ { PREPROC_LINE, false, "#endif"},
|
|
|
+
|
|
|
+ { PREPROC_LINE, false, "#if HAVE_FOO"},
|
|
|
+ { CODE_LINE, false, "HAVE_FOO"},
|
|
|
+ { PREPROC_LINE, false, "#elif HAVE_BAR"},
|
|
|
+ { CODE_LINE, false, "HAVE_BAR"},
|
|
|
+ { PREPROC_LINE, false, "#else"},
|
|
|
+ { CODE_LINE, false, "neither"},
|
|
|
+ { PREPROC_LINE, false, "#endif /* With a comment. */"},
|
|
|
+
|
|
|
+ { PREPROC_LINE, false, "#endif /* TEST_H */" },
|
|
|
+};
|
|
|
+
|
|
|
+#define NUM_LINES (sizeof(testfile)/sizeof(testfile[0]))
|
|
|
+
|
|
|
+static const char *line_type_name(enum line_info_type type)
|
|
|
+{
|
|
|
+ switch (type) {
|
|
|
+ case PREPROC_LINE: return "PREPROC_LINE";
|
|
|
+ case CODE_LINE: return "CODE_LINE";
|
|
|
+ case DOC_LINE: return "DOC_LINE";
|
|
|
+ case COMMENT_LINE: return "COMMENT_LINE";
|
|
|
+ default: return "**INVALID**";
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* This just tests parser for the moment. */
|
|
|
+int main(int argc, char *argv[])
|
|
|
+{
|
|
|
+ unsigned int i;
|
|
|
+ struct line_info *line_info;
|
|
|
+ struct ccan_file *f = talloc(NULL, struct ccan_file);
|
|
|
+
|
|
|
+ plan_tests(NUM_LINES * 2 + 2 + 66);
|
|
|
+
|
|
|
+ f->num_lines = NUM_LINES;
|
|
|
+ f->line_info = NULL;
|
|
|
+ f->lines = talloc_array(f, char *, f->num_lines);
|
|
|
+ for (i = 0; i < f->num_lines; i++)
|
|
|
+ f->lines[i] = talloc_strdup(f->lines, testfile[i].line);
|
|
|
+
|
|
|
+ line_info = get_ccan_line_info(f);
|
|
|
+ ok1(line_info == f->line_info);
|
|
|
+ for (i = 0; i < f->num_lines; i++) {
|
|
|
+ ok(f->line_info[i].type == testfile[i].type,
|
|
|
+ "Line %u:'%s' type %s should be %s",
|
|
|
+ i, testfile[i].line,
|
|
|
+ line_type_name(f->line_info[i].type),
|
|
|
+ line_type_name(testfile[i].type));
|
|
|
+ ok(f->line_info[i].continued == testfile[i].continued,
|
|
|
+ "Line %u:'%s' continued should be %s",
|
|
|
+ i, testfile[i].line,
|
|
|
+ testfile[i].continued ? "TRUE" : "FALSE");
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Should cache. */
|
|
|
+ ok1(get_ccan_line_info(f) == line_info);
|
|
|
+
|
|
|
+ /* Expect line 1 condition to be NULL. */
|
|
|
+ ok1(line_info[0].cond == NULL);
|
|
|
+ /* Line 2, should depend on TEST_H being undefined. */
|
|
|
+ ok1(line_info[1].cond != NULL);
|
|
|
+ ok1(line_info[1].cond->type == PP_COND_IFDEF);
|
|
|
+ ok1(line_info[1].cond->inverse);
|
|
|
+ ok1(line_info[1].cond->parent == NULL);
|
|
|
+ ok1(streq(line_info[1].cond->symbol, "TEST_H"));
|
|
|
+
|
|
|
+ /* Every line BAR should depend on BAR being defined. */
|
|
|
+ for (i = 0; i < f->num_lines; i++) {
|
|
|
+ if (!streq(testfile[i].line, "BAR"))
|
|
|
+ continue;
|
|
|
+ ok1(line_info[i].cond->type == PP_COND_IFDEF);
|
|
|
+ ok1(!line_info[i].cond->inverse);
|
|
|
+ ok1(streq(line_info[i].cond->symbol, "BAR"));
|
|
|
+ ok1(line_info[i].cond->parent == line_info[1].cond);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Every line !BAR should depend on BAR being undefined. */
|
|
|
+ for (i = 0; i < f->num_lines; i++) {
|
|
|
+ if (!streq(testfile[i].line, "!BAR"))
|
|
|
+ continue;
|
|
|
+ ok1(line_info[i].cond->type == PP_COND_IFDEF);
|
|
|
+ ok1(line_info[i].cond->inverse);
|
|
|
+ ok1(streq(line_info[i].cond->symbol, "BAR"));
|
|
|
+ ok1(line_info[i].cond->parent == line_info[1].cond);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Every line HAVE_BAR should depend on HAVE_BAR being set. */
|
|
|
+ for (i = 0; i < f->num_lines; i++) {
|
|
|
+ if (!streq(testfile[i].line, "HAVE_BAR"))
|
|
|
+ continue;
|
|
|
+ ok1(line_info[i].cond->type == PP_COND_IF);
|
|
|
+ ok1(!line_info[i].cond->inverse);
|
|
|
+ ok1(streq(line_info[i].cond->symbol, "HAVE_BAR"));
|
|
|
+ ok1(line_info[i].cond->parent == line_info[1].cond);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Every line HAVE_FOO should depend on HAVE_FOO being set. */
|
|
|
+ for (i = 0; i < f->num_lines; i++) {
|
|
|
+ if (!streq(testfile[i].line, "HAVE_FOO"))
|
|
|
+ continue;
|
|
|
+ ok1(line_info[i].cond->type == PP_COND_IF);
|
|
|
+ ok1(!line_info[i].cond->inverse);
|
|
|
+ ok1(streq(line_info[i].cond->symbol, "HAVE_FOO"));
|
|
|
+ ok1(line_info[i].cond->parent == line_info[1].cond);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Now check using interface. */
|
|
|
+ for (i = 0; i < f->num_lines; i++) {
|
|
|
+ if (streq(testfile[i].line, "BAR")) {
|
|
|
+ ok1(get_ccan_line_pp(line_info[i].cond, "BAR", 1)
|
|
|
+ == COMPILED);
|
|
|
+ ok1(get_ccan_line_pp(line_info[i].cond, "FOO", 1)
|
|
|
+ == NOT_COMPILED);
|
|
|
+ } else if (streq(testfile[i].line, "!BAR")) {
|
|
|
+ ok1(get_ccan_line_pp(line_info[i].cond, "BAR", 1)
|
|
|
+ == NOT_COMPILED);
|
|
|
+ ok1(get_ccan_line_pp(line_info[i].cond, "FOO", 1)
|
|
|
+ == COMPILED);
|
|
|
+ } else if (streq(testfile[i].line, "HAVE_BAR")) {
|
|
|
+ ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_BAR", 1)
|
|
|
+ == COMPILED);
|
|
|
+ ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_BAR", 0)
|
|
|
+ == NOT_COMPILED);
|
|
|
+ } else if (streq(testfile[i].line, "HAVE_FOO")) {
|
|
|
+ ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_FOO", 1)
|
|
|
+ == COMPILED);
|
|
|
+ ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_FOO", 0)
|
|
|
+ == NOT_COMPILED);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return exit_status();
|
|
|
+}
|