Line | Branch | Exec | Source |
---|---|---|---|
1 | #include "test.h" | ||
2 | #include "block-iter.h" | ||
3 | #include "config.h" | ||
4 | #include "editor.h" | ||
5 | #include "syntax/bitset.h" | ||
6 | #include "syntax/highlight.h" | ||
7 | #include "util/log.h" | ||
8 | #include "util/utf8.h" | ||
9 | #include "window.h" | ||
10 | |||
11 | 1 | static void test_bitset(TestContext *ctx) | |
12 | { | ||
13 | 1 | BitSetWord set[BITSET_NR_WORDS(256)]; | |
14 | 1 | ASSERT_TRUE(sizeof(set) >= 32); | |
15 | |||
16 | 1 | memset(set, 0, sizeof(set)); | |
17 | 1 | EXPECT_FALSE(bitset_contains(set, '0')); | |
18 | 1 | EXPECT_FALSE(bitset_contains(set, 'a')); | |
19 | 1 | EXPECT_FALSE(bitset_contains(set, 'z')); | |
20 | 1 | EXPECT_FALSE(bitset_contains(set, '!')); | |
21 | 1 | EXPECT_FALSE(bitset_contains(set, '\0')); | |
22 | |||
23 | 1 | bitset_add_char_range(set, "0-9a-fxy"); | |
24 | 1 | EXPECT_TRUE(bitset_contains(set, '0')); | |
25 | 1 | EXPECT_TRUE(bitset_contains(set, '8')); | |
26 | 1 | EXPECT_TRUE(bitset_contains(set, '9')); | |
27 | 1 | EXPECT_TRUE(bitset_contains(set, 'a')); | |
28 | 1 | EXPECT_TRUE(bitset_contains(set, 'b')); | |
29 | 1 | EXPECT_TRUE(bitset_contains(set, 'f')); | |
30 | 1 | EXPECT_TRUE(bitset_contains(set, 'x')); | |
31 | 1 | EXPECT_TRUE(bitset_contains(set, 'y')); | |
32 | 1 | EXPECT_FALSE(bitset_contains(set, 'g')); | |
33 | 1 | EXPECT_FALSE(bitset_contains(set, 'z')); | |
34 | 1 | EXPECT_FALSE(bitset_contains(set, 'A')); | |
35 | 1 | EXPECT_FALSE(bitset_contains(set, 'F')); | |
36 | 1 | EXPECT_FALSE(bitset_contains(set, 'X')); | |
37 | 1 | EXPECT_FALSE(bitset_contains(set, 'Z')); | |
38 | 1 | EXPECT_FALSE(bitset_contains(set, '{')); | |
39 | 1 | EXPECT_FALSE(bitset_contains(set, '`')); | |
40 | 1 | EXPECT_FALSE(bitset_contains(set, '/')); | |
41 | 1 | EXPECT_FALSE(bitset_contains(set, ':')); | |
42 | 1 | EXPECT_FALSE(bitset_contains(set, '\0')); | |
43 | |||
44 | 1 | BITSET_INVERT(set); | |
45 | 1 | EXPECT_FALSE(bitset_contains(set, '0')); | |
46 | 1 | EXPECT_FALSE(bitset_contains(set, '8')); | |
47 | 1 | EXPECT_FALSE(bitset_contains(set, '9')); | |
48 | 1 | EXPECT_FALSE(bitset_contains(set, 'a')); | |
49 | 1 | EXPECT_FALSE(bitset_contains(set, 'b')); | |
50 | 1 | EXPECT_FALSE(bitset_contains(set, 'f')); | |
51 | 1 | EXPECT_FALSE(bitset_contains(set, 'x')); | |
52 | 1 | EXPECT_FALSE(bitset_contains(set, 'y')); | |
53 | 1 | EXPECT_TRUE(bitset_contains(set, 'g')); | |
54 | 1 | EXPECT_TRUE(bitset_contains(set, 'z')); | |
55 | 1 | EXPECT_TRUE(bitset_contains(set, 'A')); | |
56 | 1 | EXPECT_TRUE(bitset_contains(set, 'F')); | |
57 | 1 | EXPECT_TRUE(bitset_contains(set, 'X')); | |
58 | 1 | EXPECT_TRUE(bitset_contains(set, 'Z')); | |
59 | 1 | EXPECT_TRUE(bitset_contains(set, '{')); | |
60 | 1 | EXPECT_TRUE(bitset_contains(set, '`')); | |
61 | 1 | EXPECT_TRUE(bitset_contains(set, '/')); | |
62 | 1 | EXPECT_TRUE(bitset_contains(set, ':')); | |
63 | 1 | EXPECT_TRUE(bitset_contains(set, '\0')); | |
64 | |||
65 | 1 | memset(set, 0, sizeof(set)); | |
66 | 1 | bitset_add_char_range(set, "\1-?r-r^-b"); | |
67 | 1 | EXPECT_TRUE(bitset_contains(set, '\1')); | |
68 | 1 | EXPECT_TRUE(bitset_contains(set, '\2')); | |
69 | 1 | EXPECT_TRUE(bitset_contains(set, ' ')); | |
70 | 1 | EXPECT_TRUE(bitset_contains(set, '!')); | |
71 | 1 | EXPECT_TRUE(bitset_contains(set, '>')); | |
72 | 1 | EXPECT_TRUE(bitset_contains(set, '?')); | |
73 | 1 | EXPECT_TRUE(bitset_contains(set, 'r')); | |
74 | 1 | EXPECT_TRUE(bitset_contains(set, '^')); | |
75 | 1 | EXPECT_TRUE(bitset_contains(set, '_')); | |
76 | 1 | EXPECT_TRUE(bitset_contains(set, '`')); | |
77 | 1 | EXPECT_TRUE(bitset_contains(set, 'a')); | |
78 | 1 | EXPECT_TRUE(bitset_contains(set, 'b')); | |
79 | 1 | EXPECT_FALSE(bitset_contains(set, '\0')); | |
80 | 1 | EXPECT_FALSE(bitset_contains(set, '@')); | |
81 | 1 | EXPECT_FALSE(bitset_contains(set, 'c')); | |
82 | 1 | EXPECT_FALSE(bitset_contains(set, 'q')); | |
83 | 1 | EXPECT_FALSE(bitset_contains(set, 's')); | |
84 | 1 | EXPECT_FALSE(bitset_contains(set, 'A')); | |
85 | |||
86 | 1 | memset(set, 0, sizeof(set)); | |
87 | 1 | bitset_add_char_range(set, "\x03-\xFC"); | |
88 | 1 | EXPECT_TRUE(bitset_contains(set, '\x03')); | |
89 | 1 | EXPECT_TRUE(bitset_contains(set, '\x40')); | |
90 | 1 | EXPECT_TRUE(bitset_contains(set, '\x7F')); | |
91 | 1 | EXPECT_TRUE(bitset_contains(set, '\x80')); | |
92 | 1 | EXPECT_TRUE(bitset_contains(set, '\xFC')); | |
93 | 1 | EXPECT_FALSE(bitset_contains(set, '\x00')); | |
94 | 1 | EXPECT_FALSE(bitset_contains(set, '\x01')); | |
95 | 1 | EXPECT_FALSE(bitset_contains(set, '\x02')); | |
96 | 1 | EXPECT_FALSE(bitset_contains(set, '\xFE')); | |
97 | 1 | EXPECT_FALSE(bitset_contains(set, '\xFF')); | |
98 |
2/2✓ Branch 0 (81→79) taken 250 times.
✓ Branch 1 (81→82) taken 1 times.
|
251 | for (unsigned int i = 3; i <= 0xFC; i++) { |
99 | 250 | IEXPECT_TRUE(bitset_contains(set, i)); | |
100 | } | ||
101 | |||
102 | 1 | memset(set, 0, sizeof(set)); | |
103 | 1 | bitset_add_char_range(set, "?-@"); | |
104 | 1 | EXPECT_TRUE(bitset_contains(set, '?')); | |
105 | 1 | EXPECT_TRUE(bitset_contains(set, '@')); | |
106 | 1 | EXPECT_FALSE(bitset_contains(set, '>')); | |
107 | 1 | EXPECT_FALSE(bitset_contains(set, 'A')); | |
108 | |||
109 | 1 | memset(set, 0, sizeof(set)); | |
110 | 1 | bitset_add_char_range(set, "z-a"); | |
111 |
2/2✓ Branch 0 (91→89) taken 4 times.
✓ Branch 1 (91→92) taken 1 times.
|
5 | FOR_EACH_I(i, set) { |
112 | 4 | EXPECT_UINT_EQ(set[i], 0); | |
113 | } | ||
114 | |||
115 | 1 | BITSET_INVERT(set); | |
116 |
2/2✓ Branch 0 (96→94) taken 4 times.
✓ Branch 1 (96→97) taken 1 times.
|
5 | FOR_EACH_I(i, set) { |
117 | 4 | EXPECT_UINT_EQ(set[i], bitset_word_max()); | |
118 | } | ||
119 | 1 | } | |
120 | |||
121 | 1 | static void test_load_syntax_errors(TestContext *ctx) | |
122 | { | ||
123 | 1 | EditorState *e = ctx->userdata; | |
124 | 1 | ErrorBuffer *ebuf = &e->err; | |
125 | 1 | SyntaxLoadFlags flags = SYN_LINT; | |
126 | |||
127 | 1 | clear_error(ebuf); | |
128 | 1 | StringView text = strview("syntax dup; state a; eat this; syntax dup; state b; eat this"); | |
129 | 1 | EXPECT_NULL(load_syntax(e, text, "dup", flags)); | |
130 | 1 | EXPECT_STREQ(ebuf->buf, "dup:2: Syntax 'dup' already exists"); | |
131 | |||
132 | 1 | clear_error(ebuf); | |
133 | 1 | text = strview("syntax empty"); | |
134 | 1 | EXPECT_NULL(load_syntax(e, text, "empty", flags)); | |
135 | 1 | EXPECT_STREQ(ebuf->buf, "empty:2: Empty syntax"); | |
136 | |||
137 | 1 | clear_error(ebuf); | |
138 | 1 | text = strview("syntax hde; state a; heredocend b; eat this; state b; eat this"); | |
139 | 1 | EXPECT_NULL(load_syntax(e, text, "hde", flags)); | |
140 | 1 | EXPECT_STREQ(ebuf->buf, "hde:2: heredocend can be used only in subsyntaxes"); | |
141 | |||
142 | 1 | clear_error(ebuf); | |
143 | 1 | text = strview("syntax loop; state ident; noeat this"); | |
144 | 1 | EXPECT_NULL(load_syntax(e, text, "loop", flags)); | |
145 | 1 | EXPECT_STREQ(ebuf->buf, "loop:1: noeat: using noeat to jump to same state causes infinite loop"); | |
146 | |||
147 | 1 | clear_error(ebuf); | |
148 | 1 | text = strview("syntax ml; state a; inlist X this; eat this"); | |
149 | 1 | EXPECT_NULL(load_syntax(e, text, "ml", flags)); | |
150 | 1 | EXPECT_STREQ(ebuf->buf, "ml:2: No such list 'X'"); | |
151 | |||
152 | 1 | clear_error(ebuf); | |
153 | 1 | text = strview("syntax mst; state a; noeat X"); | |
154 | 1 | EXPECT_NULL(load_syntax(e, text, "mst", flags)); | |
155 | 1 | EXPECT_STREQ(ebuf->buf, "mst:2: No such state 'X'"); | |
156 | |||
157 | 1 | clear_error(ebuf); | |
158 | 1 | text = strview("syntax nda; state a; char a this"); | |
159 | 1 | EXPECT_NULL(load_syntax(e, text, "nda", flags)); | |
160 | 1 | EXPECT_STREQ(ebuf->buf, "nda:2: No default action in state 'a'"); | |
161 | |||
162 | 1 | clear_error(ebuf); | |
163 | 1 | text = strview("syntax not-known; state a; eat this"); | |
164 | 1 | EXPECT_NULL(load_syntax(e, text, "known", flags)); | |
165 | 1 | EXPECT_STREQ(ebuf->buf, "known: no main syntax found (i.e. with name 'known')"); | |
166 | |||
167 | // Non-fatal errors: | ||
168 | |||
169 | // Unreachable state | ||
170 | 1 | clear_error(ebuf); | |
171 | 1 | text = strview("syntax ust; state a; eat this; state U; eat a"); | |
172 | 1 | const Syntax *syntax = load_syntax(e, text, "ust", flags); | |
173 | 1 | ASSERT_NONNULL(syntax); | |
174 | 1 | EXPECT_STREQ(syntax->name, "ust"); | |
175 | 1 | EXPECT_EQ(syntax->states.count, 2); | |
176 | 1 | EXPECT_STREQ(syntax->start_state->name, "a"); | |
177 | 1 | EXPECT_STREQ(ebuf->buf, "ust:2: State 'U' is unreachable"); | |
178 | |||
179 | // Unused list | ||
180 | 1 | clear_error(ebuf); | |
181 | 1 | text = strview("syntax ul; state a; eat this; list unused a"); | |
182 | 1 | syntax = load_syntax(e, text, "ul", flags); | |
183 | 1 | ASSERT_NONNULL(syntax); | |
184 | 1 | EXPECT_STREQ(syntax->name, "ul"); | |
185 | 1 | EXPECT_EQ(syntax->states.count, 1); | |
186 | 1 | EXPECT_STREQ(syntax->start_state->name, "a"); | |
187 | 1 | EXPECT_STREQ(ebuf->buf, "ul:2: List 'unused' never used"); | |
188 | |||
189 | // Unused sub-syntax | ||
190 | 1 | clear_error(ebuf); | |
191 | 1 | text = strview("syntax .uss-x; state a; eat this; syntax uss; state a; eat this"); | |
192 | 1 | syntax = load_syntax(e, text, "uss", flags); | |
193 | 1 | ASSERT_NONNULL(syntax); | |
194 | 1 | EXPECT_STREQ(syntax->name, "uss"); | |
195 | 1 | EXPECT_EQ(syntax->states.count, 1); | |
196 | 1 | EXPECT_STREQ(syntax->start_state->name, "a"); | |
197 | 1 | EXPECT_STREQ(ebuf->buf, "uss:2: Subsyntax '.uss-x' is unused"); | |
198 | |||
199 | // Use of named destination, instead of "this" | ||
200 | 1 | clear_error(ebuf); | |
201 | 1 | text = strview("syntax td; state a; eat a"); | |
202 | 1 | syntax = load_syntax(e, text, "td", flags); | |
203 | 1 | ASSERT_NONNULL(syntax); | |
204 | 1 | EXPECT_STREQ(syntax->name, "td"); | |
205 | 1 | EXPECT_EQ(syntax->states.count, 1); | |
206 | 1 | EXPECT_STREQ(syntax->start_state->name, "a"); | |
207 | 1 | EXPECT_STREQ(ebuf->buf, "td:1: eat: destination 'a' can be optimized to 'this' in 'td' syntax"); | |
208 | |||
209 | // Redundant emit-name | ||
210 | 1 | clear_error(ebuf); | |
211 | 1 | text = strview("syntax ren; state a; eat this a"); | |
212 | 1 | syntax = load_syntax(e, text, "ren", flags); | |
213 | 1 | ASSERT_NONNULL(syntax); | |
214 | 1 | EXPECT_STREQ(syntax->name, "ren"); | |
215 | 1 | EXPECT_EQ(syntax->states.count, 1); | |
216 | 1 | EXPECT_STREQ(syntax->start_state->name, "a"); | |
217 | 1 | EXPECT_STREQ(ebuf->buf, "ren:1: eat: emit-name 'a' not needed (destination state uses same emit-name)"); | |
218 | 1 | } | |
219 | |||
220 | 1 | static void test_hl_line(TestContext *ctx) | |
221 | { | ||
222 |
1/2✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→5) taken 1 times.
|
1 | if (!get_builtin_config("syntax/c")) { |
223 | ✗ | LOG_INFO("syntax/c not available; skipping %s()", __func__); | |
224 | ✗ | return; | |
225 | } | ||
226 | |||
227 | 1 | EditorState *e = ctx->userdata; | |
228 | 1 | Window *window = e->window; | |
229 | 1 | ASSERT_NONNULL(window); | |
230 | 1 | View *view = window_open_file(window, "test/data/test.c", NULL); | |
231 | 1 | ASSERT_NONNULL(view); | |
232 | 1 | Buffer *buffer = view->buffer; | |
233 | 1 | ASSERT_NONNULL(buffer); | |
234 | 1 | const size_t line_nr = 5; | |
235 | 1 | ASSERT_TRUE(buffer->nl >= line_nr); | |
236 | |||
237 | 1 | Syntax *syn = buffer->syntax; | |
238 | 1 | ASSERT_NONNULL(syn); | |
239 | 1 | ASSERT_NONNULL(syn->start_state); | |
240 | 1 | EXPECT_STREQ(syn->name, "c"); | |
241 | 1 | EXPECT_FALSE(syn->heredoc); | |
242 | |||
243 | 1 | const StyleMap *styles = &e->styles; | |
244 | 1 | PointerArray *lss = &buffer->line_start_states; | |
245 | 1 | BlockIter tmp = block_iter(buffer); | |
246 | 1 | hl_fill_start_states(syn, lss, styles, &tmp, buffer->nl); | |
247 | 1 | block_iter_goto_line(&view->cursor, line_nr - 1); | |
248 | 1 | view_update(view); | |
249 | 1 | ASSERT_EQ(view->cx, 0); | |
250 | 1 | ASSERT_EQ(view->cy, line_nr - 1); | |
251 | |||
252 | 1 | StringView line = get_current_line(&view->cursor); | |
253 | 1 | ASSERT_EQ(line.length, 65); | |
254 | |||
255 | 1 | bool next_changed; | |
256 | 1 | const TermStyle **hl = hl_line(syn, lss, styles, line, line_nr, &next_changed); | |
257 | 1 | ASSERT_NONNULL(hl); | |
258 | 1 | EXPECT_TRUE(next_changed); | |
259 | |||
260 | 1 | const TermStyle *t = find_style(styles, "text"); | |
261 | 1 | const TermStyle *c = find_style(styles, "constant"); | |
262 | 1 | const TermStyle *s = find_style(styles, "string"); | |
263 | 1 | const TermStyle *x = find_style(styles, "special"); | |
264 | 1 | const TermStyle *n = find_style(styles, "numeric"); | |
265 | 1 | const TermStyle *y = find_style(styles, "type"); | |
266 | 1 | ASSERT_NONNULL(t); | |
267 | 1 | ASSERT_NONNULL(c); | |
268 | 1 | ASSERT_NONNULL(s); | |
269 | 1 | ASSERT_NONNULL(x); | |
270 | 1 | ASSERT_NONNULL(n); | |
271 | 1 | ASSERT_NONNULL(y); | |
272 | 1 | EXPECT_EQ(t->fg, COLOR_DEFAULT); | |
273 | 1 | EXPECT_EQ(c->fg, COLOR_CYAN); | |
274 | 1 | EXPECT_EQ(s->fg, COLOR_YELLOW); | |
275 | 1 | EXPECT_EQ(x->fg, COLOR_MAGENTA); | |
276 | 1 | EXPECT_EQ(n->fg, COLOR_BLUE); | |
277 | 1 | EXPECT_EQ(y->fg, COLOR_GREEN); | |
278 | |||
279 | 1 | const TermStyle *const expected_styles[] = { | |
280 | t, t, t, t, t, t, t, t, t, t, t, t, c, c, c, c, | ||
281 | c, c, t, t, s, s, s, s, s, s, s, s, s, s, s, s, | ||
282 | s, s, s, s, x, x, s, t, t, s, s, s, s, s, t, t, | ||
283 | x, x, x, t, t, t, y, y, y, y, y, y, t, n, n, t, | ||
284 | t | ||
285 | }; | ||
286 | |||
287 | 1 | size_t i = 0; | |
288 |
2/2✓ Branch 0 (49→43) taken 65 times.
✓ Branch 1 (49→50) taken 1 times.
|
66 | for (size_t pos = 0; pos < line.length; i++) { |
289 | 65 | CodePoint u = u_get_char(line.data, line.length, &pos); | |
290 | 65 | IEXPECT_EQ(u, line.data[i]); | |
291 |
1/2✗ Branch 0 (45→46) not taken.
✓ Branch 1 (45→47) taken 65 times.
|
65 | if (i >= ARRAYLEN(expected_styles)) { |
292 | ✗ | continue; | |
293 | } | ||
294 | 65 | IEXPECT_TRUE(same_style(hl[i], expected_styles[i])); | |
295 | } | ||
296 | |||
297 | 1 | EXPECT_EQ(i, ARRAYLEN(expected_styles)); | |
298 | 1 | window_close(window); | |
299 | } | ||
300 | |||
301 | static const TestEntry tests[] = { | ||
302 | TEST(test_bitset), | ||
303 | TEST(test_load_syntax_errors), | ||
304 | TEST(test_hl_line), | ||
305 | }; | ||
306 | |||
307 | const TestGroup syntax_tests = TEST_GROUP(tests); | ||
308 |