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