dte test coverage


Directory: ./
File: test/syntax.c
Date: 2025-07-10 06:26:10
Exec Total Coverage
Lines: 231 234 98.7%
Functions: 3 3 100.0%
Branches: 10 12 83.3%

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