]> git.ozlabs.org Git - ccan/blob - tools/ccanlint/test/run-file_analysis.c
build_assert: relicense to public domain.
[ccan] / tools / ccanlint / test / run-file_analysis.c
1 #include "tools/ccanlint/ccanlint.h"
2 #include "ccan/tap/tap.h"
3 #include "tools/ccanlint/file_analysis.c"
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <assert.h>
9
10 /* This is our test file. */
11 struct test {
12         enum line_info_type type;
13         bool continued;
14         const char *line;
15 };
16
17 static struct test testfile[] = {
18         { PREPROC_LINE, false, "#ifndef TEST_H" },
19         { PREPROC_LINE, false, "#define TEST_H" },
20         { DOC_LINE,     false, "/**" },
21         { DOC_LINE,     false, " * Comment here." },
22         { DOC_LINE,     false, " * Comment here too." },
23         { DOC_LINE,     false, " */" },
24         { COMMENT_LINE, false, "// Normal one-line comment" },
25         { COMMENT_LINE, false, "  // Spaced one-line comment" },
26         { COMMENT_LINE, false, "/* Normal one-line comment */" },
27         { COMMENT_LINE, false, "  /* Spaced one-line comment */" },
28         { COMMENT_LINE, false, "  /* Spaced two-line comment" },
29         { COMMENT_LINE, false, "  continued comment */" },
30         { CODE_LINE,    false, "extern int x;"},
31         { CODE_LINE,    false, "extern int y; // With new-style comment"},
32         { CODE_LINE,    false, "extern int z; /* With old-style comment */"},
33         { CODE_LINE,    false, "extern int v; /* With two-line comment"},
34         { COMMENT_LINE, false, "                 Second line of comment"},
35         { COMMENT_LINE, false, "/* comment1  */ // comment 2"},
36         { COMMENT_LINE, false, "/* comment1  */ /* comment 2 */ "},
37         { CODE_LINE,    false, "/* comment1  */ code; /* comment 2 */ "},
38         { CODE_LINE,    false, "/* comment1  */ code; // comment 2"},
39         { COMMENT_LINE, false, "/* comment start  \\"},
40         { COMMENT_LINE, true,  "   comment finish */"},
41         { PREPROC_LINE, false, "#define foo \\"},
42         { PREPROC_LINE, true,  "        (bar + \\"},
43         { PREPROC_LINE, true,  "         baz)"},
44         { CODE_LINE,    false, "extern int \\"},
45         { CODE_LINE,    true,  "#x;"},
46
47         /* Variants of the same thing. */
48         { PREPROC_LINE, false, "#ifdef BAR"},
49         { CODE_LINE,    false, "BAR"},
50         { PREPROC_LINE, false, "#else"},
51         { CODE_LINE,    false, "!BAR"},
52         { PREPROC_LINE, false, "#endif"},
53
54         { PREPROC_LINE, false, "#if defined BAR"},
55         { CODE_LINE,    false, "BAR"},
56         { PREPROC_LINE, false, "#else"},
57         { CODE_LINE,    false, "!BAR"},
58         { PREPROC_LINE, false, "#endif"},
59
60         { PREPROC_LINE, false, "#if defined(BAR)"},
61         { CODE_LINE,    false, "BAR"},
62         { PREPROC_LINE, false, "#else"},
63         { CODE_LINE,    false, "!BAR"},
64         { PREPROC_LINE, false, "#endif"},
65
66         { PREPROC_LINE, false, "#if !defined(BAR)"},
67         { CODE_LINE,    false, "!BAR"},
68         { PREPROC_LINE, false, "#else"},
69         { CODE_LINE,    false, "BAR"},
70         { PREPROC_LINE, false, "#endif"},
71
72         { PREPROC_LINE, false, "#if HAVE_FOO"},
73         { CODE_LINE,    false, "HAVE_FOO"},
74         { PREPROC_LINE, false, "#elif HAVE_BAR"},
75         { CODE_LINE,    false, "HAVE_BAR"},
76         { PREPROC_LINE, false, "#else"},
77         { CODE_LINE,    false, "neither"},
78         { PREPROC_LINE, false, "#endif /* With a comment. */"},
79
80         { PREPROC_LINE, false, "#endif /* TEST_H */" },
81 };
82
83 #define NUM_LINES (sizeof(testfile)/sizeof(testfile[0]))
84
85 static const char *line_type_name(enum line_info_type type)
86 {
87         switch (type) {
88         case PREPROC_LINE: return "PREPROC_LINE";
89         case CODE_LINE: return "CODE_LINE";
90         case DOC_LINE: return "DOC_LINE";
91         case COMMENT_LINE: return "COMMENT_LINE";
92         default: return "**INVALID**";
93         }
94 }
95
96 /* This just tests parser for the moment. */
97 int main(int argc, char *argv[])
98 {
99         unsigned int i;
100         struct line_info *line_info;
101         struct ccan_file *f = talloc(NULL, struct ccan_file);
102
103         plan_tests(NUM_LINES * 2 + 2 + 86);
104
105         f->num_lines = NUM_LINES;
106         f->line_info = NULL;
107         f->lines = talloc_array(f, char *, f->num_lines);
108         for (i = 0; i < f->num_lines; i++)
109                 f->lines[i] = talloc_strdup(f->lines, testfile[i].line);
110         
111         line_info = get_ccan_line_info(f);
112         ok1(line_info == f->line_info);
113         for (i = 0; i < f->num_lines; i++) {
114                 ok(f->line_info[i].type == testfile[i].type,
115                    "Line %u:'%s' type %s should be %s",
116                    i, testfile[i].line,
117                    line_type_name(f->line_info[i].type),
118                    line_type_name(testfile[i].type));
119                 ok(f->line_info[i].continued == testfile[i].continued,
120                    "Line %u:'%s' continued should be %s",
121                    i, testfile[i].line,
122                    testfile[i].continued ? "TRUE" : "FALSE");
123         }
124
125         /* Should cache. */
126         ok1(get_ccan_line_info(f) == line_info);
127
128         /* Expect line 1 condition to be NULL. */
129         ok1(line_info[0].cond == NULL);
130         /* Line 2, should depend on TEST_H being undefined. */
131         ok1(line_info[1].cond != NULL);
132         ok1(line_info[1].cond->type == PP_COND_IFDEF);
133         ok1(line_info[1].cond->inverse);
134         ok1(line_info[1].cond->parent == NULL);
135         ok1(streq(line_info[1].cond->symbol, "TEST_H"));
136
137         /* Every line BAR should depend on BAR being defined. */
138         for (i = 0; i < f->num_lines; i++) {
139                 if (!streq(testfile[i].line, "BAR"))
140                         continue;
141                 ok1(line_info[i].cond->type == PP_COND_IFDEF);
142                 ok1(!line_info[i].cond->inverse);
143                 ok1(streq(line_info[i].cond->symbol, "BAR"));
144                 ok1(line_info[i].cond->parent == line_info[1].cond);
145         }
146
147         /* Every line !BAR should depend on BAR being undefined. */
148         for (i = 0; i < f->num_lines; i++) {
149                 if (!streq(testfile[i].line, "!BAR"))
150                         continue;
151                 ok1(line_info[i].cond->type == PP_COND_IFDEF);
152                 ok1(line_info[i].cond->inverse);
153                 ok1(streq(line_info[i].cond->symbol, "BAR"));
154                 ok1(line_info[i].cond->parent == line_info[1].cond);
155         }
156         
157         /* Every line HAVE_BAR should depend on HAVE_BAR being set. */
158         for (i = 0; i < f->num_lines; i++) {
159                 if (!streq(testfile[i].line, "HAVE_BAR"))
160                         continue;
161                 ok1(line_info[i].cond->type == PP_COND_IF);
162                 ok1(!line_info[i].cond->inverse);
163                 ok1(streq(line_info[i].cond->symbol, "HAVE_BAR"));
164                 ok1(line_info[i].cond->parent == line_info[1].cond);
165         }
166         
167         /* Every line HAVE_FOO should depend on HAVE_FOO being set. */
168         for (i = 0; i < f->num_lines; i++) {
169                 if (!streq(testfile[i].line, "HAVE_FOO"))
170                         continue;
171                 ok1(line_info[i].cond->type == PP_COND_IF);
172                 ok1(!line_info[i].cond->inverse);
173                 ok1(streq(line_info[i].cond->symbol, "HAVE_FOO"));
174                 ok1(line_info[i].cond->parent == line_info[1].cond);
175         }
176
177         /* Now check using interface. */
178         for (i = 0; i < f->num_lines; i++) {
179                 unsigned int val = 1;
180                 if (streq(testfile[i].line, "BAR")) {
181                         /* If we don't know if the TEST_H was undefined,
182                          * best we get is a MAYBE. */
183                         ok1(get_ccan_line_pp(line_info[i].cond, "BAR", &val,
184                                              NULL) == MAYBE_COMPILED);
185                         ok1(get_ccan_line_pp(line_info[i].cond, "BAR", NULL,
186                                              NULL) == NOT_COMPILED);
187                         ok1(get_ccan_line_pp(line_info[i].cond, "BAR", &val,
188                                              "TEST_H", NULL,
189                                              NULL) == COMPILED);
190                         ok1(get_ccan_line_pp(line_info[i].cond, "BAR", NULL,
191                                              "TEST_H", NULL,
192                                              NULL) == NOT_COMPILED);
193                 } else if (streq(testfile[i].line, "!BAR")) {
194                         ok1(get_ccan_line_pp(line_info[i].cond, "BAR", &val,
195                                              NULL) == NOT_COMPILED);
196                         ok1(get_ccan_line_pp(line_info[i].cond, "BAR", NULL,
197                                              NULL) == MAYBE_COMPILED);
198                         ok1(get_ccan_line_pp(line_info[i].cond, "BAR", &val,
199                                              "TEST_H", NULL,
200                                              NULL) == NOT_COMPILED);
201                         ok1(get_ccan_line_pp(line_info[i].cond, "BAR", NULL,
202                                              "TEST_H", NULL,
203                                              NULL) == COMPILED);
204                 } else if (streq(testfile[i].line, "HAVE_BAR")) {
205                         ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_BAR",
206                                              &val, NULL) == MAYBE_COMPILED);
207                         ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_BAR",
208                                              &val, "TEST_H", NULL,
209                                              NULL) == COMPILED);
210                         val = 0;
211                         ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_BAR",
212                                              &val, NULL) == NOT_COMPILED);
213                         ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_BAR",
214                                              &val, "TEST_H", NULL,
215                                              NULL) == NOT_COMPILED);
216                 } else if (streq(testfile[i].line, "HAVE_FOO")) {
217                         ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_FOO",
218                                              &val, NULL) == MAYBE_COMPILED);
219                         ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_FOO",
220                                              &val, "TEST_H", NULL,
221                                              NULL) == COMPILED);
222                         val = 0;
223                         ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_FOO",
224                                              &val, NULL) == NOT_COMPILED);
225                         ok1(get_ccan_line_pp(line_info[i].cond, "HAVE_FOO",
226                                              &val, "TEST_H", NULL,
227                                              NULL) == NOT_COMPILED);
228                 }
229         }
230
231         return exit_status();
232 }