dte test coverage


Directory: ./
File: test/config.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 171 174 98.3%
Functions: 8 8 100.0%
Branches: 17 22 77.3%

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