Line | Branch | Exec | Source |
---|---|---|---|
1 | #include "test.h" | ||
2 | #include "commands.h" | ||
3 | #include "editor.h" | ||
4 | #include "error.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 | {"bind C-C-C", "invalid key string"}, | ||
29 | {"bind -T invalid-mode C-k eol", "can't bind key in unknown mode"}, | ||
30 | {"cd", "too few arguments"}, | ||
31 | {"cd ''", "directory argument cannot be empty"}, | ||
32 | {"cd /non-existent/dir/_x_29_", "changing directory failed"}, | ||
33 | {"compile dfa34dazzdf2 echo", "no such error parser"}, | ||
34 | {"cursor xyz bar #f40", "invalid mode argument"}, | ||
35 | {"cursor insert _ #f40", "invalid cursor type"}, | ||
36 | {"cursor insert bar blue", "invalid cursor color"}, | ||
37 | {"def-mode normal", "mode 'normal' already exists"}, | ||
38 | {"def-mode command", "mode 'command' already exists"}, | ||
39 | {"def-mode search", "mode 'search' already exists"}, | ||
40 | {"def-mode _m; def-mode _m", "mode '_m' already exists"}, | ||
41 | {"errorfmt x (re) zz", "unknown substring name"}, | ||
42 | {"errorfmt x (re) file line", "invalid substring count"}, | ||
43 | {"exec -s sh -c 'kill -s USR1 $$'", "child received signal"}, | ||
44 | {"exec -s sh -c 'exit 44'", "child returned 44"}, | ||
45 | {"exec -s _z9kjdf_2dmm_92:j-a3d_xzkw::", "unable to exec"}, | ||
46 | {"exec -s -e errmsg sh -c 'echo X >&2; exit 9'", "child returned 9: \"X\""}, | ||
47 | {"ft -- -name ext", "invalid filetype name"}, | ||
48 | {"hi xyz red green blue", "too many colors"}, | ||
49 | {"hi xyz _invalid_", "invalid color or attribute"}, | ||
50 | {"line 0", "invalid line number"}, | ||
51 | {"line _", "invalid line number"}, | ||
52 | {"line 1:x", "invalid line number"}, | ||
53 | {"macro xyz", "unknown action"}, | ||
54 | {"match-bracket", "no character under cursor"}, | ||
55 | {"move-tab 0", "invalid tab position"}, | ||
56 | {"msg -np", "mutually exclusive"}, | ||
57 | {"msg -p 12", "mutually exclusive"}, | ||
58 | {"open -t filename", "can't be used with filename arguments"}, | ||
59 | {"open /non/exi/ste/nt/file_Name", "error opening"}, | ||
60 | {"open /dev/null", "not a regular file"}, | ||
61 | {"option c auto-indent true syntax", "missing option value"}, | ||
62 | {"option c filetype c", "filetype cannot be set via option command"}, | ||
63 | {"option , auto-indent true", "invalid filetype"}, | ||
64 | {"option -r '' auto-indent true", "empty pattern"}, | ||
65 | {"quit xyz", "not a valid integer"}, | ||
66 | {"quit 9000", "exit code should be between"}, | ||
67 | {"redo 8", "nothing to redo"}, | ||
68 | {"repeat x up", "not a valid repeat count"}, | ||
69 | {"repeat 2 invalid-cmd", "no such command: invalid-cmd"}, | ||
70 | {"replace '' x", "must contain at least 1 character"}, | ||
71 | {"replace e5fwHgHuCFVZd x", "not found"}, | ||
72 | {"replace -c a b", "-c flag unavailable in headless mode"}, | ||
73 | {"save ''", "empty filename"}, | ||
74 | {"save", "no filename"}, | ||
75 | {"search -nw", "mutually exclusive"}, | ||
76 | {"search -p str", "mutually exclusive"}, | ||
77 | {"search e5fwHgHuCFVZd", "not found"}, | ||
78 | {"set filetype", "not boolean"}, | ||
79 | {"set filetype ' '", "invalid filetype name"}, | ||
80 | {"set tab-width s", "integer value for tab-width expected"}, | ||
81 | {"set indent-width 9", "must be in 1-8 range"}, | ||
82 | {"set emulate-tab x", "invalid value"}, | ||
83 | {"set case-sensitive-search z", "invalid value"}, | ||
84 | {"set ws-error A", "invalid flag"}, | ||
85 | {"set non-existent 1", "no such option"}, | ||
86 | {"set -g filetype c", "not global"}, | ||
87 | {"set -l statusline-right _", "not local"}, | ||
88 | {"set 1 2 3", "one or even number of arguments expected"}, | ||
89 | {"set statusline-left %", "format character expected"}, | ||
90 | {"set statusline-left %!", "invalid format character"}, | ||
91 | {"setenv DTE_VERSION 0.1", "cannot be changed"}, | ||
92 | {"shift x", "invalid number"}, | ||
93 | {"shift 0", "must be non-zero"}, | ||
94 | {"show -c alias", "requires 2 arguments"}, | ||
95 | {"show zzz", "invalid argument"}, | ||
96 | {"show bind M-M-M---", "invalid key string"}, | ||
97 | {"show buffer x", "doesn't take extra arguments"}, | ||
98 | {"show builtin a78sd8623d7k", "no built-in config"}, | ||
99 | {"show cursor xyz", "no cursor entry"}, | ||
100 | {"show option 31ldx92kjsd8", "invalid option name"}, | ||
101 | {"show wsplit m2wz9u263t8a", "invalid window"}, | ||
102 | {"suspend", "unavailable in headless mode"}, | ||
103 | {"toggle ws-error", "requires arguments"}, | ||
104 | {"view z", "invalid view index"}, | ||
105 | {"view 0", "invalid view index"}, | ||
106 | {"wrap-paragraph _", "invalid paragraph width"}, | ||
107 | {"wrap-paragraph 90000", "width must be between"}, | ||
108 | {"wsplit -t file", "flags -n and -t can't be used with filename"}, | ||
109 | |||
110 | // Error strings produced by arg_parse_error_msg(): | ||
111 | {"bind -nnnnnnnnnn C-k eol", "too many options given"}, | ||
112 | {"bind -Z C-k eol", "invalid option -Z"}, | ||
113 | {"bind -Tsearch C-k eol", "option -T must be given separately"}, | ||
114 | {"bind -T ", "option -T requires an argument"}, | ||
115 | {"bind 1 2 3", "too many arguments"}, | ||
116 | {"bind", "too few arguments"}, | ||
117 | }; | ||
118 | |||
119 | 1 | EditorState *e = ctx->userdata; | |
120 | 1 | ASSERT_NONNULL(window_open_empty_buffer(e->window)); | |
121 | |||
122 |
2/2✓ Branch 0 taken 92 times.
✓ Branch 1 taken 1 times.
|
93 | FOR_EACH_I(i, tests) { |
123 | 92 | const char *cmd = tests[i].cmd_str; | |
124 | 92 | const char *substr = tests[i].expected_error_substr; | |
125 | 92 | ASSERT_NONNULL(cmd); | |
126 | 92 | ASSERT_NONNULL(substr); | |
127 | 92 | ASSERT_TRUE(substr[0] != '\0'); | |
128 | 92 | ASSERT_TRUE(substr[1] != '\0'); | |
129 | 92 | ASSERT_TRUE(!ascii_isupper(substr[0])); | |
130 | |||
131 | 92 | clear_error(); | |
132 | 92 | EXPECT_FALSE(handle_normal_command(e, cmd, false)); | |
133 | 92 | bool is_error = false; | |
134 | 92 | const char *msg = get_msg(&is_error); | |
135 | 92 | ASSERT_NONNULL(msg); | |
136 | 92 | EXPECT_TRUE(is_error); | |
137 | |||
138 | // Check for substring in error message (ignoring capitalization) | ||
139 | 92 | const char *found = strstr(msg, substr + 1); | |
140 |
1/2✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
|
92 | if (likely(found && found != msg)) { |
141 | 92 | test_pass(ctx); | |
142 | 92 | EXPECT_EQ(ascii_tolower(found[-1]), substr[0]); | |
143 | } else { | ||
144 | − | TEST_FAIL ( | |
145 | "Test #%zu: substring \"%s\" not found in message \"%s\"", | ||
146 | i + 1, substr, msg | ||
147 | ); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | 1 | window_close_current_view(e->window); | |
152 | 1 | set_view(e->window->view); | |
153 | |||
154 | // Special case errors produced by run_command(): | ||
155 | // ---------------------------------------------- | ||
156 | |||
157 | 1 | CommandRunner runner = normal_mode_cmdrunner(e); | |
158 | 1 | runner.lookup_alias = NULL; | |
159 | 1 | clear_error(); | |
160 | 1 | EXPECT_FALSE(handle_command(&runner, "_xyz")); | |
161 | 1 | bool is_error = false; | |
162 | 1 | const char *msg = get_msg(&is_error); | |
163 | 1 | EXPECT_STREQ(msg, "No such command: _xyz"); | |
164 | 1 | EXPECT_TRUE(is_error); | |
165 | |||
166 | 1 | runner.lookup_alias = dummy_lookup_alias; | |
167 | 1 | clear_error(); | |
168 | 1 | EXPECT_FALSE(handle_command(&runner, "_abc")); | |
169 | 1 | msg = get_msg(&is_error); | |
170 | 1 | EXPECT_TRUE(msg && str_has_prefix(msg, "Parsing alias _abc:")); | |
171 | 1 | EXPECT_TRUE(is_error); | |
172 | |||
173 | 1 | EXPECT_NULL(current_config.file); | |
174 | 1 | current_config.file = "example"; | |
175 | 1 | current_config.line = 1; | |
176 | 1 | runner.lookup_alias = NULL; | |
177 | 1 | clear_error(); | |
178 | 1 | EXPECT_FALSE(handle_command(&runner, "quit")); | |
179 | 1 | msg = get_msg(&is_error); | |
180 | 1 | EXPECT_STREQ(msg, "example:1: Command quit not allowed in config file"); | |
181 | 1 | EXPECT_TRUE(is_error); | |
182 | 1 | current_config.file = NULL; | |
183 | |||
184 | // TODO: Cover alias recursion overflow check in run_commands() | ||
185 | 1 | } | |
186 | |||
187 | static const TestEntry tests[] = { | ||
188 | TEST(test_normal_command_errors), | ||
189 | }; | ||
190 | |||
191 | const TestGroup error_tests = TEST_GROUP(tests); | ||
192 |