dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 100.0% 52 / 1 / 53
Functions: 100.0% 2 / 0 / 2
Branches: 66.7% 4 / 4 / 10

test/error.c
Line Branch Exec Source
1 #include "test.h"
2 #include "command/error.h"
3 #include "commands.h"
4 #include "editor.h"
5 #include "util/ascii.h"
6 #include "util/debug.h"
7 #include "window.h"
8
9 1 static const char *dummy_lookup_alias(const EditorState *e, const char *name)
10 {
11 1 BUG_ON(!e);
12 1 BUG_ON(!name);
13 1 return "insert \"...";
14 }
15
16 1 static void test_normal_command_errors(TestContext *ctx)
17 {
18 1 static const struct {
19 const char *cmd_str;
20 const char *expected_error_substr;
21 } tests[] = {
22 // Note: do not include error substrings produced by strerror(3)
23 {"non__existent__command", "no such command or alias"},
24 {"alias '' up", "empty alias name"},
25 {"alias -- -name up", "alias name cannot begin with '-'"},
26 {"alias 'x y' up", "invalid byte in alias name"},
27 {"alias open close", "can't replace existing command"},
28 {"alias a b; alias b a; a; alias a; alias b", "alias recursion limit reached"},
29 {"bind C-C-C", "invalid key string"},
30 {"bind -T invalid-mode C-k eol", "can't bind key in unknown mode"},
31 {"cd", "too few arguments"},
32 {"cd ''", "directory argument cannot be empty"},
33 {"cd /non-existent/dir/_x_29_", "changing directory failed"},
34 {"compile dfa34dazzdf2 echo", "no such error parser"},
35 {"cursor xyz bar #f40", "invalid mode argument"},
36 {"cursor insert _ #f40", "invalid cursor type"},
37 {"cursor insert bar blue", "invalid cursor color"},
38 {"def-mode normal", "mode 'normal' already exists"},
39 {"def-mode command", "mode 'command' already exists"},
40 {"def-mode search", "mode 'search' already exists"},
41 {"def-mode -- -", "mode name can't be empty or start with '-'"},
42 {"def-mode ''", "mode name can't be empty or start with '-'"},
43 {"def-mode xz qrst9", "unknown fallthrough mode 'qrst9'"},
44 {"def-mode xz search", "unable to use 'search' as fall-through mode"},
45 {"def-mode _m; def-mode _m", "mode '_m' already exists"},
46 {"errorfmt x (re) zz", "unknown substring name"},
47 {"errorfmt x (re) file line", "expected 2 subexpressions in regex, but found 1: '(re)'"},
48 {"exec -s sh -c 'kill -s USR1 $$'", "child received signal"},
49 {"exec -s sh -c 'exit 44'", "child returned 44"},
50 {"exec -s _z9kjdf_2dmm_92:j-a3d_xzkw::", "unable to exec"},
51 {"exec -s -e errmsg sh -c 'echo X >&2; exit 9'", "child returned 9: \"X\""},
52 {"exec -s -i invalid true", "invalid action for -i: 'invalid'"},
53 {"exec -s -o invalid true", "invalid action for -o: 'invalid'"},
54 {"exec -s -e invalid true", "invalid action for -e: 'invalid'"},
55 {"ft -- -name ext", "invalid filetype name"},
56 {"hi xyz red green blue", "too many colors"},
57 {"hi xyz _invalid_", "invalid color or attribute"},
58 {"include -b nonexistent", "no built-in config with name 'nonexistent'"},
59 {"include /.n-o-n-e-x-i-s-t-e-n-t/_/az_..3", "error reading"},
60 {"line 0", "invalid line number"},
61 {"line _", "invalid line number"},
62 {"line 1:x", "invalid line number"},
63 {"macro xyz", "unknown action"},
64 {"match-bracket", "no character under cursor"},
65 {"mode inValiD", "unknown mode 'inValiD'"},
66 {"move-tab 0", "invalid tab position"},
67 {"msg -p 12", "flags [-n|-p] can't be used with [number] argument"},
68 {"open -t filename", "can't be used with filename arguments"},
69 {"open /non/exi/ste/nt/file_Name", "directory does not exist"},
70 {"open /dev/null", "not a regular file"},
71 {"open ''", "empty filename not allowed"},
72 {"option c auto-indent true syntax", "missing option value"},
73 {"option c filetype c", "filetype cannot be set via option command"},
74 {"option c invalid 1", "no such option"},
75 {"option c tab-bar true", "is not local"},
76 {"option c detect-indent 1,0", "invalid flag"},
77 {"option c detect-indent 9", "invalid flag"},
78 {"option c indent-width 0", "must be in 1-8 range"},
79 {"option c indent-width 9", "must be in 1-8 range"},
80 {"option c auto-indent xyz", "invalid value"},
81 {"option , auto-indent true", "invalid filetype"},
82 {"option '' auto-indent true", "first argument cannot be empty"},
83 {"option -r '' auto-indent true", "first argument cannot be empty"},
84 {"quit xyz", "exit code should be an integer between 0 and 125; got 'xyz'"},
85 {"quit 126", "exit code should be an integer between 0 and 125; got '126'"},
86 {"redo 8", "nothing to redo"},
87 {"redo A", "invalid change id"},
88 {"repeat x up", "not a valid repeat count"},
89 {"repeat 2 invalid-cmd", "no such command: invalid-cmd"},
90 {"replace '' x", "must contain at least 1 character"},
91 {"replace e5fwHgHuCFVZd x", "not found"},
92 {"replace -c a b", "-c flag unavailable in headless mode"},
93 {"save ''", "empty filename"},
94 {"save", "no filename"},
95 {"save /dev/", "will not overwrite directory"},
96 {"search -nw", "mutually exclusive"},
97 {"search -p str", "mutually exclusive"},
98 {"search e5fwHgHuCFVZd", "not found"},
99 {"set filetype", "not boolean"},
100 {"set filetype ' '", "invalid filetype name"},
101 {"set tab-width s", "integer value for tab-width expected"},
102 {"set indent-width 9", "must be in 1-8 range"},
103 {"set emulate-tab x", "invalid value"},
104 {"set msg-compile xyz", "invalid value for msg-compile; expected: A, B, C"},
105 {"set msg-tag xyz", "invalid value for msg-tag; expected: A, B, C"},
106 {"set newline xyz", "invalid value for newline; expected: unix, dos"},
107 {
108 "set case-sensitive-search z",
109 "invalid value for case-sensitive-search; expected: false, true, auto"
110 },
111 {
112 "set save-unmodified xyz",
113 "invalid value for save-unmodified; expected: none, touch, full"
114 },
115 {
116 "set window-separator xyz",
117 "invalid value for window-separator; expected: blank, bar"
118 },
119 {
120 "set ws-error A",
121 "invalid flag 'A' for ws-error; expected: space-indent,"
122 " space-align, tab-indent, tab-after-indent, special,"
123 " auto-indent, trailing, all-trailing"
124 },
125 {"set non-existent 1", "no such option"},
126 {"set -g filetype c", "not global"},
127 {"set -l statusline-right _", "not local"},
128 {"set 1 2 3", "one or even number of arguments expected"},
129 {"set statusline-left %", "format character expected"},
130 {"set statusline-left %!", "invalid format character"},
131 {"set filesize-limit 1M", "invalid filesize"},
132 {"set filesize-limit 1Mi", "invalid filesize"},
133 {"set filesize-limit 1MB", "invalid filesize"},
134 {"set filesize-limit 1MIB", "invalid filesize"},
135 {"set filesize-limit 1Mib", "invalid filesize"},
136 {"setenv DTE_VERSION 0.1", "cannot be changed"},
137 {"shift x", "invalid number"},
138 {"show -c alias", "requires 2 arguments"},
139 {"show zzz", "invalid argument"},
140 {"show bind M-M-M---", "invalid key string"},
141 {"show buffer x", "doesn't take extra arguments"},
142 {"show builtin a78sd8623d7k", "no built-in config"},
143 {"show cursor xyz", "no cursor entry"},
144 {"show option 31ldx92kjsd8", "invalid option name"},
145 {"show wsplit m2wz9u263t8a", "invalid window"},
146 {"suspend", "unavailable in headless mode"},
147 {"toggle ws-error", "requires arguments"},
148 {"view z", "invalid view index"},
149 {"view 0", "invalid view index"},
150 {"wrap-paragraph _", "invalid paragraph width"},
151 {"wrap-paragraph 90000", "width must be between"},
152 {"wsplit -t file", "flags [-n|-t] can't be used with [filename] arguments"},
153 {"wsplit -t; wresize nonnum; wclose", "invalid resize value"},
154
155 // Error strings produced by command_parse_error_to_string():
156 {"x '", "command syntax error: unclosed '"},
157 {"x \"", "command syntax error: unclosed \""},
158 {"x \"\\", "command syntax error: unexpected EOF"},
159
160 // Error strings produced by arg_parse_error_msg():
161 {"bind -nnnnnnnnnnnnnn C-k eol", "too many options given"},
162 {"bind -Z C-k eol", "invalid option -Z"},
163 {"bind -Tsearch C-k eol", "option -T must be given separately"},
164 {"bind -T ", "option -T requires an argument"},
165 {"bind 1 2 3", "too many arguments"},
166 {"bind", "too few arguments"},
167
168 // These are tested last, since some of the commands succeed and
169 // affect the state of the Buffer (i.e. the undo history) and some
170 // are dependent on it (and cannot be reordered)
171 {"insert 1; undo; redo 900", "there is only 1 possible change to redo"},
172 {"insert 2; undo; redo 900", "there are only 2 possible changes to redo"},
173 {"insert x; close; undo", "close without saving"},
174 {"insert x; wclose; undo", "close window without saving"},
175 {"insert x; quit; undo", "quit without saving"},
176 {"insert x; close -p; undo", "-p flag unavailable in headless mode"},
177 {"insert x; wclose -p; undo", "-p flag unavailable in headless mode"},
178 {"insert x; quit -p; undo", "-p flag unavailable in headless mode"},
179 {"insert x; match-bracket; undo", "character under cursor not matchable"},
180 {"insert {{}; match-bracket; undo", "no matching bracket found"},
181 {"repeat 4100 insert x; search -w; undo", "word under cursor too long"},
182 };
183
184 1 EditorState *e = ctx->userdata;
185 1 ErrorBuffer *ebuf = &e->err;
186 1 ASSERT_NONNULL(window_open_empty_buffer(e->window));
187
188
2/2
✓ Branch 21 → 5 taken 137 times.
✓ Branch 21 → 22 taken 1 time.
138 FOR_EACH_I(i, tests) {
189 137 const char *cmd = tests[i].cmd_str;
190 137 const char *substr = tests[i].expected_error_substr;
191 137 ASSERT_NONNULL(cmd);
192 137 ASSERT_NONNULL(substr);
193 137 ASSERT_NE(substr[0], '\0');
194 137 ASSERT_NE(substr[1], '\0');
195 137 ASSERT_TRUE(!ascii_isupper(substr[0]));
196
197 137 clear_error(ebuf);
198 137 EXPECT_FALSE(handle_normal_command(e, cmd, false));
199 137 EXPECT_NE(ebuf->buf[0], '\0');
200 137 EXPECT_TRUE(ebuf->is_error);
201
202 // Check for substring in error message (ignoring capitalization)
203 137 const char *found = strstr(ebuf->buf, substr + 1);
204
2/4
✓ Branch 15 → 16 taken 137 times.
✗ Branch 15 → 19 not taken.
✓ Branch 16 → 17 taken 137 times.
✗ Branch 16 → 19 not taken.
137 if (likely(found && found != ebuf->buf)) {
205 137 test_pass(ctx);
206 137 EXPECT_EQ(ascii_tolower(found[-1]), substr[0]);
207 } else {
208 TEST_FAIL (
209 "Test #%zu: substring \"%s\" not found in message \"%s\"",
210 i + 1, substr, ebuf->buf
211 );
212 }
213 }
214
215 1 window_close_current_view(e->window);
216 1 set_view(e->window->view);
217
218 // Special case errors produced by run_command():
219 // ----------------------------------------------
220
221 1 CommandRunner runner = normal_mode_cmdrunner(e);
222 1 runner.lookup_alias = NULL;
223 1 clear_error(ebuf);
224 1 EXPECT_FALSE(handle_command(&runner, "_xyz"));
225 1 EXPECT_STREQ(ebuf->buf, "No such command: _xyz");
226 1 EXPECT_TRUE(ebuf->is_error);
227
228 1 runner.lookup_alias = dummy_lookup_alias;
229 1 clear_error(ebuf);
230 1 EXPECT_FALSE(handle_command(&runner, "_abc"));
231 1 EXPECT_TRUE(str_has_prefix(ebuf->buf, "Parsing alias _abc:"));
232 1 EXPECT_TRUE(ebuf->is_error);
233
234 1 EXPECT_NULL(ebuf->config_filename);
235 1 ebuf->config_filename = "example";
236 1 ebuf->config_line = 1;
237 1 runner.lookup_alias = NULL;
238 1 clear_error(ebuf);
239 1 EXPECT_FALSE(handle_command(&runner, "quit"));
240 1 EXPECT_STREQ(ebuf->buf, "example:1: Command quit not allowed in config file");
241 1 EXPECT_TRUE(ebuf->is_error);
242 1 ebuf->config_filename = NULL;
243
244 // Special case errors produced by exec_config():
245 // ----------------------------------------------
246
247 // This is a special case because handle_command() currently returns true,
248 // since cmd_include() only indicates failure for I/O errors and not for
249 // failing commands in the loaded file
250 1 clear_error(ebuf);
251 1 EXPECT_TRUE(handle_command(&runner, "include test/data/recursive.dterc"));
252 1 EXPECT_STREQ(ebuf->buf, "test/data/recursive.dterc:1: config recursion limit reached");
253 1 EXPECT_TRUE(ebuf->is_error);
254 1 }
255
256 static const TestEntry tests[] = {
257 TEST(test_normal_command_errors),
258 };
259
260 const TestGroup error_tests = TEST_GROUP(tests);
261