dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 98.3% 171 / 3 / 177
Functions: 100.0% 8 / 0 / 8
Branches: 70.0% 21 / 0 / 30

test/config.c
Line Branch Exec Source
1 #include "build-defs.h"
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include "test.h"
5 #include "config.h"
6 #include "command/macro.h"
7 #include "commands.h"
8 #include "convert.h"
9 #include "editor.h"
10 #include "frame.h"
11 #include "insert.h"
12 #include "mode.h"
13 #include "syntax/state.h"
14 #include "syntax/syntax.h"
15 #include "util/log.h"
16 #include "util/path.h"
17 #include "util/readfile.h"
18 #include "util/str-util.h"
19 #include "util/string-view.h"
20 #include "util/xsnprintf.h"
21 #include "window.h"
22
23 #if HAVE_EMBED
24 #include "test-data-embed.h"
25 #else
26 #include "test-data.h"
27 #endif
28
29 1 static void test_builtin_configs(TestContext *ctx)
30 {
31 1 EditorState *e = ctx->userdata;
32 1 HashMap *syntaxes = &e->syntaxes;
33 1 size_t n;
34 1 const BuiltinConfig *editor_builtin_configs = get_builtin_configs_array(&n);
35 1 e->err.print_to_stderr = true;
36
37
2/2
✓ Branch 20 → 4 taken 79 times.
✓ Branch 20 → 21 taken 1 time.
80 for (size_t i = 0; i < n; i++) {
38 79 const BuiltinConfig cfg = editor_builtin_configs[i];
39
2/2
✓ Branch 4 → 5 taken 61 times.
✓ Branch 4 → 14 taken 18 times.
79 if (str_has_prefix(cfg.name, "syntax/")) {
40
2/2
✓ Branch 5 → 6 taken 4 times.
✓ Branch 5 → 7 taken 57 times.
61 if (str_has_prefix(cfg.name, "syntax/inc/")) {
41 4 continue;
42 }
43 // Check that built-in syntax files load without errors
44 57 EXPECT_NULL(find_syntax(syntaxes, path_basename(cfg.name)));
45 57 unsigned int saved_nr_errs = e->err.nr_errors;
46 57 EXPECT_NONNULL(load_syntax(e, cfg.text, cfg.name, 0));
47 57 EXPECT_EQ(e->err.nr_errors, saved_nr_errs);
48 57 EXPECT_NONNULL(find_syntax(syntaxes, path_basename(cfg.name)));
49 } else {
50 // Check that built-in configs and scripts are identical to
51 // their source files
52 18 char path[4096];
53 18 xsnprintf(path, sizeof path, "config/%s", cfg.name);
54 18 char *src;
55 18 ssize_t size = read_file(path, &src, 8u << 20);
56 18 EXPECT_MEMEQ(src, size, cfg.text.data, cfg.text.length);
57 18 free(src);
58 }
59 }
60
61 1 update_all_syntax_styles(&e->syntaxes, &e->styles);
62 1 e->err.print_to_stderr = false;
63 1 }
64
65 21 static void expect_files_equal(TestContext *ctx, const char *path1, const char *path2)
66 {
67 21 size_t filesize_limit = 1u << 20; // 1MiB
68 21 char *buf1;
69 21 ssize_t size1 = read_file(path1, &buf1, filesize_limit);
70
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 21 times.
21 if (size1 < 0) {
71 TEST_FAIL("Error reading '%s': %s", path1, strerror(errno));
72 return;
73 }
74 21 test_pass(ctx);
75
76 21 char *buf2;
77 21 ssize_t size2 = read_file(path2, &buf2, filesize_limit);
78
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 12 taken 21 times.
21 if (size2 < 0) {
79 free(buf1);
80 TEST_FAIL("Error reading '%s': %s", path2, strerror(errno));
81 return;
82 }
83 21 test_pass(ctx);
84
85
2/4
✓ Branch 13 → 14 taken 21 times.
✗ Branch 13 → 16 not taken.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 21 times.
21 if (size1 != size2 || !mem_equal(buf1, buf2, size1)) {
86 TEST_FAIL("Files differ: '%s', '%s'", path1, path2);
87 } else {
88 21 test_pass(ctx);
89 }
90
91 21 free(buf1);
92 21 free(buf2);
93 }
94
95 1 static void test_exec_config(TestContext *ctx)
96 {
97 1 static const char *const outfiles[] = {
98 "env.txt",
99 "crlf.txt",
100 "insert.txt",
101 "join.txt",
102 "change.txt",
103 "thai-utf8.txt",
104 "pipe-from.txt",
105 "pipe-to.txt",
106 "redo1.txt",
107 "redo2.txt",
108 "replace.txt",
109 "shift.txt",
110 "repeat.txt",
111 "wrap.txt",
112 "exec.txt",
113 "move.txt",
114 "delete.txt",
115 "new-line.txt",
116 "tag.txt",
117 };
118
119 // Delete output files left over from previous runs
120 1 int r = unlink("build/test/thai-tis620.txt");
121
2/4
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 12 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 12 taken 1 time.
1 LOG_ERRNO_ON(r && errno != ENOENT, "unlink")
122
2/2
✓ Branch 13 → 6 taken 19 times.
✓ Branch 13 → 14 taken 1 time.
20 FOR_EACH_I(i, outfiles) {
123 19 char out[64];
124 19 xsnprintf(out, sizeof out, "build/test/%s", outfiles[i]);
125 19 r = unlink(out);
126
2/4
✓ Branch 8 → 9 taken 19 times.
✗ Branch 8 → 11 not taken.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 19 times.
19 LOG_ERRNO_ON(r && errno != ENOENT, "unlink")
127 }
128
129 // Execute *.dterc files
130 1 EditorState *e = ctx->userdata;
131
2/2
✓ Branch 17 → 15 taken 19 times.
✓ Branch 17 → 22 taken 1 time.
20 FOR_EACH_I(i, builtin_configs) {
132 19 const BuiltinConfig config = builtin_configs[i];
133 19 exec_normal_config(e, config.text);
134 }
135
136 // Check that output files have expected contents
137
2/2
✓ Branch 22 → 18 taken 19 times.
✓ Branch 22 → 23 taken 1 time.
20 FOR_EACH_I(i, outfiles) {
138 19 char out[64], ref[64];
139 19 xsnprintf(out, sizeof out, "build/test/%s", outfiles[i]);
140 19 xsnprintf(ref, sizeof ref, "test/data/%s", outfiles[i]);
141 19 expect_files_equal(ctx, out, ref);
142 }
143
144
1/2
✓ Branch 24 → 25 taken 1 time.
✗ Branch 24 → 26 not taken.
1 if (conversion_supported_by_iconv("UTF-8", "TIS-620")) {
145 1 expect_files_equal(ctx, "build/test/thai-tis620.txt", "test/data/thai-tis620.txt");
146 }
147
148 1 const StringView sv = STRING_VIEW("toggle utf8-bom \\");
149 1 EXPECT_FALSE(e->options.utf8_bom);
150 1 exec_normal_config(e, sv);
151 1 EXPECT_TRUE(e->options.utf8_bom);
152 1 exec_normal_config(e, sv);
153 1 EXPECT_FALSE(e->options.utf8_bom);
154 1 }
155
156 1 static void test_detect_indent(TestContext *ctx)
157 {
158 1 EditorState *e = ctx->userdata;
159 1 EXPECT_FALSE(e->options.detect_indent);
160 1 EXPECT_FALSE(e->options.expand_tab);
161 1 EXPECT_EQ(e->options.indent_width, 8);
162
163 1 static const char cmds[] =
164 "option -r '/test/data/detect-indent\\.ini$' detect-indent 2,4,8;"
165 "open test/data/detect-indent.ini;";
166
167 1 EXPECT_TRUE(handle_normal_command(e, cmds, false));
168 1 EXPECT_EQ(e->buffer->options.detect_indent, 1 << 1 | 1 << 3 | 1 << 7);
169 1 EXPECT_TRUE(e->buffer->options.expand_tab);
170 1 EXPECT_EQ(e->buffer->options.indent_width, 2);
171
172 1 EXPECT_TRUE(handle_normal_command(e, "close", false));
173 1 }
174
175 1 static void test_editor_state(TestContext *ctx)
176 {
177 1 const EditorState *e = ctx->userdata;
178 1 const Buffer *buffer = e->buffer;
179 1 const View *view = e->view;
180 1 const Window *window = e->window;
181 1 const Frame *root_frame = e->root_frame;
182 1 ASSERT_NONNULL(window);
183 1 ASSERT_NONNULL(root_frame);
184 1 ASSERT_NONNULL(buffer);
185 1 ASSERT_NONNULL(view);
186 1 ASSERT_PTREQ(window->view, view);
187 1 ASSERT_PTREQ(window->frame, root_frame);
188 1 ASSERT_PTREQ(window->editor, e);
189 1 ASSERT_PTREQ(view->buffer, buffer);
190 1 ASSERT_PTREQ(view->window, window);
191 1 ASSERT_PTREQ(root_frame->window, window);
192 1 ASSERT_PTREQ(root_frame->parent, NULL);
193
194 1 ASSERT_EQ(window->views.count, 1);
195 1 ASSERT_EQ(buffer->views.count, 1);
196 1 ASSERT_EQ(e->buffers.count, 1);
197 1 ASSERT_PTREQ(window->views.ptrs[0], view);
198 1 ASSERT_PTREQ(buffer->views.ptrs[0], view);
199 1 ASSERT_PTREQ(window_get_first_view(window), view);
200 1 ASSERT_PTREQ(buffer_get_first_view(buffer), view);
201 1 ASSERT_PTREQ(e->buffers.ptrs[0], buffer);
202
203 1 ASSERT_NONNULL(buffer->encoding);
204 1 ASSERT_NONNULL(buffer->blocks.next);
205 1 ASSERT_PTREQ(&buffer->blocks, view->cursor.head);
206 1 ASSERT_PTREQ(buffer->blocks.next, view->cursor.blk);
207 1 ASSERT_PTREQ(buffer->cur_change, &buffer->change_head);
208 1 ASSERT_PTREQ(buffer->saved_change, buffer->cur_change);
209 1 EXPECT_NULL(buffer->display_filename);
210
211 // Note: this isn't necessarily equal to 1 because some UNITTEST
212 // blocks may have already called window_open_empty_buffer()
213 // before init_headless_mode() was entered
214 1 EXPECT_TRUE(buffer->id > 0);
215 1 }
216
217 1 static void test_handle_normal_command(TestContext *ctx)
218 {
219 1 EditorState *e = ctx->userdata;
220 1 EXPECT_TRUE(handle_normal_command(e, "right; left", false));
221 1 EXPECT_TRUE(handle_normal_command(e, ";left;right;;left;right;;;", false));
222 1 EXPECT_FALSE(handle_normal_command(e, "alias 'err", false));
223 1 EXPECT_FALSE(handle_normal_command(e, "refresh; alias 'x", false));
224 1 }
225
226 1 static void test_macro_record(TestContext *ctx)
227 {
228 1 EditorState *e = ctx->userdata;
229 1 MacroRecorder *m = &e->macro;
230 1 EXPECT_PTREQ(e->mode->cmds, &normal_commands);
231
232 1 EXPECT_EQ(m->macro.count, 0);
233 1 EXPECT_EQ(m->prev_macro.count, 0);
234 1 EXPECT_FALSE(macro_is_recording(m));
235 1 EXPECT_TRUE(macro_record(m));
236 1 EXPECT_TRUE(macro_is_recording(m));
237
238 1 EXPECT_TRUE(handle_normal_command(e, "open", false));
239 1 EXPECT_TRUE(handle_input(e, 'x'));
240 1 EXPECT_TRUE(handle_input(e, 'y'));
241 1 EXPECT_TRUE(handle_normal_command(e, "bol", true));
242 1 EXPECT_TRUE(handle_input(e, '-'));
243 1 EXPECT_TRUE(handle_input(e, 'z'));
244 1 EXPECT_TRUE(handle_normal_command(e, "eol; right; insert -m .; new-line", true));
245
246 1 const StringView t1 = STRING_VIEW("test 1\n");
247 1 insert_text(e->view, t1.data, t1.length, true);
248 1 macro_insert_text_hook(m, t1.data, t1.length);
249
250 1 const StringView t2 = STRING_VIEW("-- test 2");
251 1 insert_text(e->view, t2.data, t2.length, true);
252 1 macro_insert_text_hook(m, t2.data, t2.length);
253
254 1 macro_search_hook(m, "[12]", true, false);
255
256 1 EXPECT_TRUE(macro_is_recording(m));
257 1 EXPECT_TRUE(macro_stop(m));
258 1 EXPECT_FALSE(macro_is_recording(m));
259 1 EXPECT_EQ(m->macro.count, 10);
260 1 EXPECT_EQ(m->prev_macro.count, 0);
261
262 1 static const char expected_macro[] =
263 "insert -k xy\n"
264 "bol\n"
265 "insert -k -- -z\n"
266 "eol\n"
267 "right\n"
268 "insert -m .\n"
269 "new-line\n"
270 "insert -m \"test 1\\n\"\n"
271 "insert -m -- '-- test 2'\n"
272 "search -r -H [12]\n"
273 ;
274
275 1 String s = dump_macro(m);
276 1 EXPECT_MEMEQ(s.buffer, s.len, expected_macro, sizeof(expected_macro) - 1);
277 1 string_free(&s);
278
279 // Check that the replayed macro produces the same buffer as the commands above
280 1 static const char cmds[] =
281 "save -f build/test/macro-rec.txt;"
282 "close -f;"
283 "open;"
284 "macro play;"
285 "save -f build/test/macro-out.txt;"
286 "close -f;"
287 "show macro;"
288 "close -f;";
289
290 1 EXPECT_TRUE(handle_normal_command(e, cmds, false));
291 1 expect_files_equal(ctx, "build/test/macro-rec.txt", "build/test/macro-out.txt");
292
293 // Ensure macro_cancel() keeps the previously recorded macro
294 1 EXPECT_FALSE(macro_is_recording(m));
295 1 EXPECT_TRUE(macro_record(m));
296 1 EXPECT_TRUE(macro_is_recording(m));
297 1 EXPECT_TRUE(handle_input(e, 'x'));
298 1 EXPECT_TRUE(macro_cancel(m));
299 1 EXPECT_FALSE(macro_is_recording(m));
300 1 EXPECT_EQ(m->macro.count, 10);
301 1 EXPECT_EQ(m->prev_macro.count, 0);
302 1 }
303
304 static const TestEntry tests[] = {
305 TEST(test_editor_state),
306 TEST(test_handle_normal_command),
307 TEST(test_builtin_configs),
308 TEST(test_exec_config),
309 TEST(test_detect_indent),
310 TEST(test_macro_record),
311 };
312
313 const TestGroup config_tests = TEST_GROUP(tests);
314
315 DISABLE_WARNING("-Wmissing-prototypes")
316
317 1 void init_headless_mode(TestContext *ctx)
318 {
319 1 EditorState *e = ctx->userdata;
320 1 ASSERT_NONNULL(e);
321 1 exec_rc_files(e, NULL, false);
322 1 e->window = new_window(e);
323 1 e->root_frame = new_root_frame(e->window);
324 1 set_view(window_open_empty_buffer(e->window));
325 1 }
326