dte test coverage


Directory: ./
File: test/error.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 52 52 100.0%
Functions: 2 2 100.0%
Branches: 4 6 66.7%

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", "invalid substring count"},
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 -np", "mutually exclusive"},
68 {"msg -p 12", "mutually exclusive"},
69 {"open -t filename", "can't be used with filename arguments"},
70 {"open /non/exi/ste/nt/file_Name", "directory does not exist"},
71 {"open /dev/null", "not a regular file"},
72 {"open ''", "empty filename not allowed"},
73 {"option c auto-indent true syntax", "missing option value"},
74 {"option c filetype c", "filetype cannot be set via option command"},
75 {"option c invalid 1", "no such option"},
76 {"option c tab-bar true", "is not local"},
77 {"option c detect-indent 9", "invalid flag"},
78 {"option c indent-width 9", "must be in 1-8 range"},
79 {"option c auto-indent xyz", "invalid value"},
80 {"option , auto-indent true", "invalid filetype"},
81 {"option '' auto-indent true", "first argument cannot be empty"},
82 {"option -r '' auto-indent true", "first argument cannot be empty"},
83 {"quit xyz", "not a valid integer"},
84 {"quit 9000", "exit code should be between"},
85 {"redo 8", "nothing to redo"},
86 {"redo A", "invalid change id"},
87 {"repeat x up", "not a valid repeat count"},
88 {"repeat 2 invalid-cmd", "no such command: invalid-cmd"},
89 {"replace '' x", "must contain at least 1 character"},
90 {"replace e5fwHgHuCFVZd x", "not found"},
91 {"replace -c a b", "-c flag unavailable in headless mode"},
92 {"save ''", "empty filename"},
93 {"save", "no filename"},
94 {"save /dev/", "will not overwrite directory"},
95 {"search -nw", "mutually exclusive"},
96 {"search -p str", "mutually exclusive"},
97 {"search e5fwHgHuCFVZd", "not found"},
98 {"set filetype", "not boolean"},
99 {"set filetype ' '", "invalid filetype name"},
100 {"set tab-width s", "integer value for tab-width expected"},
101 {"set indent-width 9", "must be in 1-8 range"},
102 {"set emulate-tab x", "invalid value"},
103 {"set case-sensitive-search z", "invalid value"},
104 {"set ws-error A", "invalid flag"},
105 {"set non-existent 1", "no such option"},
106 {"set -g filetype c", "not global"},
107 {"set -l statusline-right _", "not local"},
108 {"set 1 2 3", "one or even number of arguments expected"},
109 {"set statusline-left %", "format character expected"},
110 {"set statusline-left %!", "invalid format character"},
111 {"setenv DTE_VERSION 0.1", "cannot be changed"},
112 {"shift x", "invalid number"},
113 {"shift 0", "must be non-zero"},
114 {"show -c alias", "requires 2 arguments"},
115 {"show zzz", "invalid argument"},
116 {"show bind M-M-M---", "invalid key string"},
117 {"show buffer x", "doesn't take extra arguments"},
118 {"show builtin a78sd8623d7k", "no built-in config"},
119 {"show cursor xyz", "no cursor entry"},
120 {"show option 31ldx92kjsd8", "invalid option name"},
121 {"show wsplit m2wz9u263t8a", "invalid window"},
122 {"suspend", "unavailable in headless mode"},
123 {"toggle ws-error", "requires arguments"},
124 {"view z", "invalid view index"},
125 {"view 0", "invalid view index"},
126 {"wrap-paragraph _", "invalid paragraph width"},
127 {"wrap-paragraph 90000", "width must be between"},
128 {"wsplit -t file", "flags -n and -t can't be used with filename"},
129 {"wsplit -t; wresize nonnum; wclose", "invalid resize value"},
130
131 // Error strings produced by command_parse_error_to_string():
132 {"x '", "command syntax error: unclosed '"},
133 {"x \"", "command syntax error: unclosed \""},
134 {"x \"\\", "command syntax error: unexpected EOF"},
135
136 // Error strings produced by arg_parse_error_msg():
137 {"bind -nnnnnnnnnn C-k eol", "too many options given"},
138 {"bind -Z C-k eol", "invalid option -Z"},
139 {"bind -Tsearch C-k eol", "option -T must be given separately"},
140 {"bind -T ", "option -T requires an argument"},
141 {"bind 1 2 3", "too many arguments"},
142 {"bind", "too few arguments"},
143
144 // These are tested last, since some of the commands succeed and
145 // affect the state of the Buffer (i.e. the undo history) and some
146 // are dependant on it (and cannot be reordered)
147 {"insert 1; undo; redo 900", "there is only 1 possible change to redo"},
148 {"insert 2; undo; redo 900", "there are only 2 possible changes to redo"},
149 {"insert x; close; undo", "close without saving"},
150 {"insert x; wclose; undo", "close window without saving"},
151 {"insert x; quit; undo", "quit without saving"},
152 {"insert x; close -p; undo", "-p flag unavailable in headless mode"},
153 {"insert x; wclose -p; undo", "-p flag unavailable in headless mode"},
154 {"insert x; quit -p; undo", "-p flag unavailable in headless mode"},
155 {"insert x; match-bracket; undo", "character under cursor not matchable"},
156 {"insert {{}; match-bracket; undo", "no matching bracket found"},
157 {"repeat 4100 insert x; search -w; undo", "word under cursor too long"},
158 };
159
160 1 EditorState *e = ctx->userdata;
161 1 ErrorBuffer *ebuf = &e->err;
162 1 ASSERT_NONNULL(window_open_empty_buffer(e->window));
163
164
2/2
✓ Branch 0 (21→5) taken 127 times.
✓ Branch 1 (21→22) taken 1 times.
128 FOR_EACH_I(i, tests) {
165 127 const char *cmd = tests[i].cmd_str;
166 127 const char *substr = tests[i].expected_error_substr;
167 127 ASSERT_NONNULL(cmd);
168 127 ASSERT_NONNULL(substr);
169 127 ASSERT_TRUE(substr[0] != '\0');
170 127 ASSERT_TRUE(substr[1] != '\0');
171 127 ASSERT_TRUE(!ascii_isupper(substr[0]));
172
173 127 clear_error(ebuf);
174 127 EXPECT_FALSE(handle_normal_command(e, cmd, false));
175 127 EXPECT_FALSE(ebuf->buf[0] == '\0');
176 127 EXPECT_TRUE(ebuf->is_error);
177
178 // Check for substring in error message (ignoring capitalization)
179 127 const char *found = strstr(ebuf->buf, substr + 1);
180
2/4
✓ Branch 0 (15→16) taken 127 times.
✗ Branch 1 (15→19) not taken.
✓ Branch 2 (16→17) taken 127 times.
✗ Branch 3 (16→19) not taken.
127 if (likely(found && found != ebuf->buf)) {
181 127 test_pass(ctx);
182 127 EXPECT_EQ(ascii_tolower(found[-1]), substr[0]);
183 } else {
184 TEST_FAIL (
185 "Test #%zu: substring \"%s\" not found in message \"%s\"",
186 i + 1, substr, ebuf->buf
187 );
188 }
189 }
190
191 1 window_close_current_view(e->window);
192 1 set_view(e->window->view);
193
194 // Special case errors produced by run_command():
195 // ----------------------------------------------
196
197 1 CommandRunner runner = normal_mode_cmdrunner(e);
198 1 runner.lookup_alias = NULL;
199 1 clear_error(ebuf);
200 1 EXPECT_FALSE(handle_command(&runner, "_xyz"));
201 1 EXPECT_STREQ(ebuf->buf, "No such command: _xyz");
202 1 EXPECT_TRUE(ebuf->is_error);
203
204 1 runner.lookup_alias = dummy_lookup_alias;
205 1 clear_error(ebuf);
206 1 EXPECT_FALSE(handle_command(&runner, "_abc"));
207 1 EXPECT_TRUE(str_has_prefix(ebuf->buf, "Parsing alias _abc:"));
208 1 EXPECT_TRUE(ebuf->is_error);
209
210 1 EXPECT_NULL(ebuf->config_filename);
211 1 ebuf->config_filename = "example";
212 1 ebuf->config_line = 1;
213 1 runner.lookup_alias = NULL;
214 1 clear_error(ebuf);
215 1 EXPECT_FALSE(handle_command(&runner, "quit"));
216 1 EXPECT_STREQ(ebuf->buf, "example:1: Command quit not allowed in config file");
217 1 EXPECT_TRUE(ebuf->is_error);
218 1 ebuf->config_filename = NULL;
219
220 // Special case errors produced by exec_config():
221 // ----------------------------------------------
222
223 // This is a special case because handle_command() currently returns true,
224 // since cmd_include() only indicates failure for I/O errors and not for
225 // failing commands in the loaded file
226 1 clear_error(ebuf);
227 1 EXPECT_TRUE(handle_command(&runner, "include test/data/recursive.dterc"));
228 1 EXPECT_STREQ(ebuf->buf, "test/data/recursive.dterc:1: config recursion limit reached");
229 1 EXPECT_TRUE(ebuf->is_error);
230 1 }
231
232 static const TestEntry tests[] = {
233 TEST(test_normal_command_errors),
234 };
235
236 const TestGroup error_tests = TEST_GROUP(tests);
237