dte test coverage


Directory: ./
File: test/command.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 389 389 100.0%
Functions: 13 13 100.0%
Branches: 6 6 100.0%

Line Branch Exec Source
1 #include <limits.h>
2 #include "test.h"
3 #include "command/alias.h"
4 #include "command/args.h"
5 #include "command/cache.h"
6 #include "command/parse.h"
7 #include "command/run.h"
8 #include "command/serialize.h"
9 #include "commands.h"
10 #include "editor.h"
11 #include "replace.h"
12 #include "search.h"
13 #include "util/ascii.h"
14 #include "util/debug.h"
15 #include "util/path.h"
16 #include "util/str-util.h"
17 #include "version.h"
18
19 1 static void test_parse_command_arg(TestContext *ctx)
20 {
21 1 EditorState *e = ctx->userdata;
22 1 CommandRunner runner = normal_mode_cmdrunner(e);
23 1 runner.expand_tilde_slash = false;
24 1 ASSERT_PTREQ(runner.e, e);
25 1 ASSERT_PTREQ(runner.cmds, &normal_commands);
26 1 EXPECT_PTREQ(runner.lookup_alias, find_normal_alias);
27 1 EXPECT_PTREQ(runner.home_dir, &e->home_dir);
28 1 EXPECT_FALSE(runner.allow_recording);
29 1 EXPECT_EQ(runner.recursion_count, 0);
30
31 // Single, unquoted argument
32 1 char *arg = parse_command_arg(&runner, STRN("arg"));
33 1 EXPECT_STREQ(arg, "arg");
34 1 free(arg);
35
36 // Two unquoted, space-separated arguments
37 1 arg = parse_command_arg(&runner, STRN("hello world"));
38 1 EXPECT_STREQ(arg, "hello");
39 1 free(arg);
40
41 // Two unquoted, tab-separated arguments
42 1 arg = parse_command_arg(&runner, STRN("hello\tworld"));
43 1 EXPECT_STREQ(arg, "hello");
44 1 free(arg);
45
46 // Unquoted argument preceded by whitespace
47 1 arg = parse_command_arg(&runner, STRN(" x"));
48 1 EXPECT_STREQ(arg, "");
49 1 free(arg);
50
51 // Single-quoted argument, including whitespace
52 1 arg = parse_command_arg(&runner, STRN("' foo ' .."));
53 1 EXPECT_STREQ(arg, " foo ");
54 1 free(arg);
55
56 // Several adjacent, quoted strings forming a single argument
57 1 arg = parse_command_arg(&runner, STRN("\"foo\"'bar'' baz '\"etc\"."));
58 1 EXPECT_STREQ(arg, "foobar baz etc.");
59 1 free(arg);
60
61 // Control character escapes in a double-quoted string
62 1 arg = parse_command_arg(&runner, STRN("\"\\a\\b\\t\\n\\v\\f\\r\""));
63 1 EXPECT_STREQ(arg, "\a\b\t\n\v\f\r");
64 1 free(arg);
65
66 // Backslash escape sequence in a double-quoted string
67 1 arg = parse_command_arg(&runner, STRN("\"\\\\\""));
68 1 EXPECT_STREQ(arg, "\\");
69 1 free(arg);
70
71 // Double-quote escape sequence in a double-quoted string
72 1 arg = parse_command_arg(&runner, STRN("\"\\\"\""));
73 1 EXPECT_STREQ(arg, "\"");
74 1 free(arg);
75
76 // Escape character escape sequence in a double-quoted string
77 1 arg = parse_command_arg(&runner, STRN("\"\\e[0m\""));
78 1 EXPECT_STREQ(arg, "\033[0m");
79 1 free(arg);
80
81 // Unrecognized escape sequence in a double-quoted string
82 1 arg = parse_command_arg(&runner, STRN("\"\\z\""));
83 1 EXPECT_STREQ(arg, "\\z");
84 1 free(arg);
85
86 // Hexadecimal escape sequences in a double-quoted string
87 1 arg = parse_command_arg(&runner, STRN("\"\\x1B[31m\\x7E\\x2f\\x1b[0m\""));
88 1 EXPECT_STREQ(arg, "\x1B[31m~/\x1B[0m");
89 1 free(arg);
90
91 // Invalid hexadecimal escape sequences
92 1 arg = parse_command_arg(&runner, STRN("\"\\x\\x1\\xFG\\xz1\""));
93 1 EXPECT_STREQ(arg, "Gz1");
94 1 free(arg);
95
96 // Incomplete hexadecimal escape sequence
97 1 arg = parse_command_arg(&runner, STRN("\"\\x"));
98 1 EXPECT_STREQ(arg, "");
99 1 free(arg);
100
101 // 4-digit Unicode escape sequence
102 1 arg = parse_command_arg(&runner, STRN("\"\\u148A\""));
103 1 EXPECT_STREQ(arg, "\xE1\x92\x8A");
104 1 free(arg);
105
106 // 8-digit Unicode escape sequence
107 1 arg = parse_command_arg(&runner, STRN("\"\\U0001F4A4\""));
108 1 EXPECT_STREQ(arg, "\xF0\x9F\x92\xA4");
109 1 free(arg);
110
111 // "\U" escape sequence terminated by non-hexadecimal character
112 1 arg = parse_command_arg(&runner, STRN("\"\\U1F4A4...\""));
113 1 EXPECT_STREQ(arg, "\xF0\x9F\x92\xA4...");
114 1 free(arg);
115
116 // Incomplete Unicode escape sequence
117 1 arg = parse_command_arg(&runner, STRN("\"\\u"));
118 1 EXPECT_STREQ(arg, "");
119 1 free(arg);
120
121 // Invalid Unicode escape sequence
122 1 arg = parse_command_arg(&runner, STRN("\"\\ugef\""));
123 1 EXPECT_STREQ(arg, "gef");
124 1 free(arg);
125
126 // Unsupported, escape-like sequences in a single-quoted string
127 1 arg = parse_command_arg(&runner, STRN("'\\t\\n'"));
128 1 EXPECT_STREQ(arg, "\\t\\n");
129 1 free(arg);
130
131 // Trailing backslash
132 // Note: `s` is unterminated, to allow ASan to catch OOB reads
133 1 static const NONSTRING char s[4] = "123\\";
134 1 arg = parse_command_arg(&runner, s, sizeof s);
135 1 EXPECT_STREQ(arg, "123");
136 1 free(arg);
137
138 // Single-quoted, empty string
139 1 arg = parse_command_arg(&runner, STRN("''"));
140 1 EXPECT_STREQ(arg, "");
141 1 free(arg);
142
143 // Double-quoted, empty string
144 1 arg = parse_command_arg(&runner, STRN("\"\""));
145 1 EXPECT_STREQ(arg, "");
146 1 free(arg);
147
148 // NULL input with zero length
149 1 arg = parse_command_arg(&runner, NULL, 0);
150 1 EXPECT_STREQ(arg, "");
151 1 free(arg);
152
153 // Empty input
154 1 arg = parse_command_arg(&runner, "", 1);
155 1 EXPECT_STREQ(arg, "");
156 1 free(arg);
157
158 // Built-in vars (expand to nothing; buffer isn't initialized yet)
159 1 static const char vars[] =
160 "' '$FILE"
161 "' '$FILEDIR"
162 "' '$RFILE"
163 "' '$RFILEDIR"
164 "' '$FILETYPE"
165 "' '$LINENO"
166 "' '$COLNO"
167 "' '$WORD"
168 ;
169 1 arg = parse_command_arg(&runner, vars, sizeof(vars) - 1);
170 1 EXPECT_STREQ(arg, " ");
171 1 free(arg);
172
173 // Built-in $DTE_HOME var (expands to user config dir)
174 1 arg = parse_command_arg(&runner, STRN("$DTE_HOME"));
175 1 EXPECT_TRUE(path_is_absolute(arg));
176 1 EXPECT_TRUE(str_has_suffix(arg, "/test/DTE_HOME"));
177 1 free(arg);
178
179 // Built-in $MSGPOS var (expands to "1" by default)
180 1 arg = parse_command_arg(&runner, STRN("$MSGPOS"));
181 1 EXPECT_STREQ(arg, "1");
182 1 free(arg);
183
184 // Environment var (via getenv(3))
185 1 arg = parse_command_arg(&runner, STRN("$DTE_VERSION"));
186 1 EXPECT_STREQ(arg, VERSION);
187 1 free(arg);
188
189 // Tilde expansion
190 1 runner.expand_tilde_slash = true;
191 1 arg = parse_command_arg(&runner, STRN("~/filename"));
192 1 EXPECT_TRUE(str_has_suffix(arg, "/test/HOME/filename"));
193 1 free(arg);
194
195 1 runner.expand_tilde_slash = false;
196 1 arg = parse_command_arg(&runner, STRN("'xyz"));
197 1 EXPECT_STREQ(arg, "xyz");
198 1 free(arg);
199
200 1 arg = parse_command_arg(&runner, STRN("\"\\u148A\"xyz'foo'\"\\x5A\"\\;\t."));
201 1 EXPECT_STREQ(arg, "\xE1\x92\x8AxyzfooZ;");
202 1 free(arg);
203 1 }
204
205 3 static CommandRunner mock_cmdrunner(const CommandSet *cmds)
206 {
207 3 static ErrorBuffer ebuf = {.print_to_stderr = false};
208 3 return (CommandRunner){.cmds = cmds, .ebuf = &ebuf};
209 }
210
211 1 static void test_parse_commands(TestContext *ctx)
212 {
213 1 CommandRunner runner = mock_cmdrunner(&normal_commands);
214 1 PointerArray array = PTR_ARRAY_INIT;
215 1 EXPECT_EQ(parse_commands(&runner, &array, " left -c;;"), CMDERR_NONE);
216 1 ASSERT_EQ(array.count, 5);
217 1 EXPECT_STREQ(array.ptrs[0], "left");
218 1 EXPECT_STREQ(array.ptrs[1], "-c");
219 1 EXPECT_NULL(array.ptrs[2]);
220 1 EXPECT_NULL(array.ptrs[3]);
221 1 EXPECT_NULL(array.ptrs[4]);
222 1 ptr_array_free(&array);
223
224 1 EXPECT_EQ(parse_commands(&runner, &array, "save -e UTF-8 file.c; close -q"), CMDERR_NONE);
225 1 ASSERT_EQ(array.count, 8);
226 1 EXPECT_STREQ(array.ptrs[0], "save");
227 1 EXPECT_STREQ(array.ptrs[1], "-e");
228 1 EXPECT_STREQ(array.ptrs[2], "UTF-8");
229 1 EXPECT_STREQ(array.ptrs[3], "file.c");
230 1 EXPECT_NULL(array.ptrs[4]);
231 1 EXPECT_STREQ(array.ptrs[5], "close");
232 1 EXPECT_STREQ(array.ptrs[6], "-q");
233 1 EXPECT_NULL(array.ptrs[7]);
234 1 ptr_array_free(&array);
235
236 1 EXPECT_EQ(parse_commands(&runner, &array, "\n ; ; \t\n "), CMDERR_NONE);
237 1 ASSERT_EQ(array.count, 3);
238 1 EXPECT_NULL(array.ptrs[0]);
239 1 EXPECT_NULL(array.ptrs[1]);
240 1 EXPECT_NULL(array.ptrs[2]);
241 1 ptr_array_free(&array);
242
243 1 EXPECT_EQ(parse_commands(&runner, &array, ""), CMDERR_NONE);
244 1 ASSERT_EQ(array.count, 1);
245 1 EXPECT_NULL(array.ptrs[0]);
246 1 ptr_array_free(&array);
247
248 1 EXPECT_EQ(parse_commands(&runner, &array, "insert '... "), CMDERR_UNCLOSED_SQUOTE);
249 1 ptr_array_free(&array);
250
251 1 EXPECT_EQ(parse_commands(&runner, &array, "insert \" "), CMDERR_UNCLOSED_DQUOTE);
252 1 ptr_array_free(&array);
253
254 1 EXPECT_EQ(parse_commands(&runner, &array, "insert \"\\\" "), CMDERR_UNCLOSED_DQUOTE);
255 1 ptr_array_free(&array);
256
257 1 EXPECT_EQ(parse_commands(&runner, &array, "insert \\"), CMDERR_UNEXPECTED_EOF);
258 1 ptr_array_free(&array);
259
260 1 EXPECT_EQ(parse_commands(&runner, &array, "insert \"\\"), CMDERR_UNEXPECTED_EOF);
261 1 ptr_array_free(&array);
262 1 }
263
264 1 static void test_find_normal_command(TestContext *ctx)
265 {
266 1 const Command *cmd = find_normal_command("alias");
267 1 ASSERT_NONNULL(cmd);
268 1 EXPECT_STREQ(cmd->name, "alias");
269
270 1 cmd = find_normal_command("bind");
271 1 ASSERT_NONNULL(cmd);
272 1 EXPECT_STREQ(cmd->name, "bind");
273
274 1 cmd = find_normal_command("wswap");
275 1 ASSERT_NONNULL(cmd);
276 1 EXPECT_STREQ(cmd->name, "wswap");
277
278 1 EXPECT_NULL(find_normal_command("alia"));
279 1 EXPECT_NULL(find_normal_command("aliass"));
280 1 EXPECT_NULL(find_normal_command("Alias"));
281 1 EXPECT_NULL(find_normal_command("bin "));
282 1 EXPECT_NULL(find_normal_command("bind "));
283 1 EXPECT_NULL(find_normal_command(" bind"));
284 1 EXPECT_NULL(find_normal_command("bind!"));
285 1 EXPECT_NULL(find_normal_command("bind\n"));
286 1 }
287
288 1 static void test_parse_args(TestContext *ctx)
289 {
290 1 const CommandSet *cmds = &normal_commands;
291 1 const CommandRunner runner = mock_cmdrunner(cmds);
292 1 const char *cmd_str = "open -g file.c file.h *.mk -e UTF-8";
293 1 PointerArray array = PTR_ARRAY_INIT;
294 1 ASSERT_NONNULL(cmds);
295 1 ASSERT_EQ(parse_commands(&runner, &array, cmd_str), CMDERR_NONE);
296 1 ASSERT_EQ(array.count, 8);
297
298 1 const Command *cmd = cmds->lookup(array.ptrs[0]);
299 1 ASSERT_NONNULL(cmd);
300 1 EXPECT_STREQ(cmd->name, "open");
301
302 1 CommandArgs a = cmdargs_new((char**)array.ptrs + 1);
303 1 ASSERT_EQ(do_parse_args(cmd, &a), ARGERR_NONE);
304 1 EXPECT_EQ(a.nr_flags, 2);
305 1 EXPECT_EQ(a.flags[0], 'g');
306 1 EXPECT_EQ(a.flags[1], 'e');
307 1 EXPECT_EQ(a.flags[2], '\0');
308 1 EXPECT_TRUE(cmdargs_has_flag(&a, 'g'));
309 1 EXPECT_TRUE(cmdargs_has_flag(&a, 'e'));
310 1 EXPECT_FALSE(cmdargs_has_flag(&a, 'f'));
311 1 EXPECT_FALSE(cmdargs_has_flag(&a, 'E'));
312 1 EXPECT_FALSE(cmdargs_has_flag(&a, '0'));
313 1 EXPECT_EQ(a.nr_flag_args, 1);
314 1 EXPECT_STREQ(a.args[0], "UTF-8");
315 1 EXPECT_EQ(a.nr_args, 3);
316 1 EXPECT_STREQ(a.args[1], "file.c");
317 1 EXPECT_STREQ(a.args[2], "file.h");
318 1 EXPECT_STREQ(a.args[3], "*.mk");
319 1 EXPECT_NULL(a.args[4]);
320
321 1 ptr_array_free(&array);
322 1 EXPECT_NULL(array.ptrs);
323 1 EXPECT_EQ(array.alloc, 0);
324 1 EXPECT_EQ(array.count, 0);
325
326 1 cmd_str = "bind 1 2 3 4 5 -6";
327 1 ASSERT_EQ(parse_commands(&runner, &array, cmd_str), CMDERR_NONE);
328 1 ASSERT_EQ(array.count, 8);
329 1 EXPECT_STREQ(array.ptrs[0], "bind");
330 1 EXPECT_STREQ(array.ptrs[1], "1");
331 1 EXPECT_STREQ(array.ptrs[6], "-6");
332 1 EXPECT_NULL(array.ptrs[7]);
333
334 1 cmd = cmds->lookup(array.ptrs[0]);
335 1 ASSERT_NONNULL(cmd);
336 1 EXPECT_STREQ(cmd->name, "bind");
337 1 EXPECT_EQ(cmd->max_args, 2);
338 1 EXPECT_EQ(cmd->flags[0], 'c');
339
340 1 a = cmdargs_new((char**)array.ptrs + 1);
341 1 a.flags[0] = 'X';
342 1 ASSERT_EQ(do_parse_args(cmd, &a), ARGERR_TOO_MANY_ARGUMENTS);
343 1 EXPECT_EQ(a.nr_args, 6);
344 1 EXPECT_EQ(a.nr_flags, 0);
345 1 EXPECT_EQ(a.nr_flag_args, 0);
346 1 EXPECT_EQ(a.flags[0], '\0');
347 1 EXPECT_EQ(a.flag_set, 0);
348 1 ptr_array_free(&array);
349
350 1 cmd_str = "open \"-\\xff\"";
351 1 ASSERT_EQ(parse_commands(&runner, &array, cmd_str), CMDERR_NONE);
352 1 ASSERT_EQ(array.count, 3);
353 1 EXPECT_STREQ(array.ptrs[0], "open");
354 1 EXPECT_STREQ(array.ptrs[1], "-\xff");
355 1 EXPECT_NULL(array.ptrs[2]);
356 1 cmd = cmds->lookup(array.ptrs[0]);
357 1 ASSERT_NONNULL(cmd);
358 1 a = cmdargs_new((char**)array.ptrs + 1);
359 1 EXPECT_EQ(do_parse_args(cmd, &a), ARGERR_INVALID_OPTION);
360 1 EXPECT_EQ(a.flags[0], '\xff');
361 1 ptr_array_free(&array);
362
363 1 ASSERT_EQ(parse_commands(&runner, &array, "save -e"), CMDERR_NONE);
364 1 ASSERT_EQ(array.count, 3);
365 1 cmd = cmds->lookup(array.ptrs[0]);
366 1 ASSERT_NONNULL(cmd);
367 1 a = cmdargs_new((char**)array.ptrs + 1);
368 1 EXPECT_EQ(do_parse_args(cmd, &a), ARGERR_OPTION_ARGUMENT_MISSING);
369 1 EXPECT_EQ(a.flags[0], 'e');
370 1 ptr_array_free(&array);
371
372 1 ASSERT_EQ(parse_commands(&runner, &array, "save -eUTF-8"), CMDERR_NONE);
373 1 ASSERT_EQ(array.count, 3);
374 1 cmd = cmds->lookup(array.ptrs[0]);
375 1 ASSERT_NONNULL(cmd);
376 1 a = cmdargs_new((char**)array.ptrs + 1);
377 1 EXPECT_EQ(do_parse_args(cmd, &a), ARGERR_OPTION_ARGUMENT_NOT_SEPARATE);
378 1 EXPECT_EQ(a.flags[0], 'e');
379 1 ptr_array_free(&array);
380 1 }
381
382 1 static void test_cached_command_new(TestContext *ctx)
383 {
384 1 const CommandRunner runner = mock_cmdrunner(&normal_commands);
385 1 const char cmd_str[] = "open -t -e UTF-8 file.c inc.h";
386 1 CachedCommand *cc = cached_command_new(&runner, cmd_str);
387 1 ASSERT_NONNULL(cc);
388 1 ASSERT_NONNULL(cc->cmd);
389 1 EXPECT_PTREQ(cc->cmd, runner.cmds->lookup("open"));
390 1 EXPECT_STREQ(cc->cmd_str, cmd_str);
391 1 ASSERT_EQ(cc->a.nr_args, 2);
392 1 ASSERT_EQ(cc->a.nr_flag_args, 1);
393 1 ASSERT_EQ(cc->a.nr_flags, 2);
394 1 EXPECT_STREQ(cc->a.args[0], "UTF-8");
395 1 EXPECT_STREQ(cc->a.args[1], "file.c");
396 1 EXPECT_STREQ(cc->a.args[2], "inc.h");
397 1 EXPECT_EQ(cc->a.flags[0], 't');
398 1 EXPECT_EQ(cc->a.flags[1], 'e');
399 1 EXPECT_TRUE(cmdargs_has_flag(&cc->a, 't'));
400 1 EXPECT_TRUE(cmdargs_has_flag(&cc->a, 'e'));
401 1 cached_command_free(cc);
402 1 cached_command_free(NULL);
403
404 1 static const char *const uncacheable[] = {
405 "", // No command
406 "zxcvbnm", // Invalid command
407 "left; right", // Multiple commands
408 "insert $DTE_HOME", // Variable expansion
409 "insert -xyz321", // Invalid flags
410 "alias 1 2 3", // Too many arguments
411 "alias", // Too few arguments
412 "insert 'xyz.", // CMDERR_UNCLOSED_SQUOTE
413 "insert \"xyz", // CMDERR_UNCLOSED_DQUOTE
414 "insert xyz\\", // CMDERR_UNEXPECTED_EOF
415 };
416
417
2/2
✓ Branch 0 (28→23) taken 10 times.
✓ Branch 1 (28→29) taken 1 times.
11 for (size_t i = 0; i < ARRAYLEN(uncacheable); i++) {
418 10 cc = cached_command_new(&runner, uncacheable[i]);
419 10 ASSERT_NONNULL(cc);
420 10 EXPECT_NULL(cc->cmd);
421 10 cached_command_free(cc);
422 }
423 1 }
424
425 19 static const char *escape_command_arg(String *buf, const char *arg, bool escape_tilde)
426 {
427 19 string_clear(buf);
428 19 string_append_escaped_arg(buf, arg, escape_tilde);
429 19 return string_borrow_cstring(buf);
430 }
431
432 1 static void test_string_append_escaped_arg(TestContext *ctx)
433 {
434 1 String buf = string_new(64);
435 1 const char *str = escape_command_arg(&buf, "arg", false);
436 1 EXPECT_STREQ(str, "arg");
437
438 1 str = escape_command_arg(&buf, "arg-x.y:z", false);
439 1 EXPECT_STREQ(str, "arg-x.y:z");
440
441 1 str = escape_command_arg(&buf, "", false);
442 1 EXPECT_STREQ(str, "''");
443
444 1 str = escape_command_arg(&buf, " ", false);
445 1 EXPECT_STREQ(str, "' '");
446
447 1 str = escape_command_arg(&buf, "hello world", false);
448 1 EXPECT_STREQ(str, "'hello world'");
449
450 1 str = escape_command_arg(&buf, "line1\nline2\n", false);
451 1 EXPECT_STREQ(str, "\"line1\\nline2\\n\"");
452
453 1 str = escape_command_arg(&buf, " \t\r\n\x1F\x7F", false);
454 1 EXPECT_STREQ(str, "\" \\t\\r\\n\\x1F\\x7F\"");
455
456 1 str = escape_command_arg(&buf, "x ' y", false);
457 1 EXPECT_STREQ(str, "\"x ' y\"");
458
459 1 str = escape_command_arg(&buf, "\033[P", false);
460 1 EXPECT_STREQ(str, "\"\\e[P\"");
461
462 1 str = escape_command_arg(&buf, "\"''\"", false);
463 1 EXPECT_STREQ(str, "\"\\\"''\\\"\"");
464
465 1 str = escape_command_arg(&buf, "\t\\", false);
466 1 EXPECT_STREQ(str, "\"\\t\\\\\"");
467
468 1 str = escape_command_arg(&buf, "~/file with spaces", false);
469 1 EXPECT_STREQ(str, "~/'file with spaces'");
470 1 str = escape_command_arg(&buf, "~/file with spaces", true);
471 1 EXPECT_STREQ(str, "'~/file with spaces'");
472
473 1 str = escape_command_arg(&buf, "~/need \t\ndquotes", false);
474 1 EXPECT_STREQ(str, "~/\"need \\t\\ndquotes\"");
475 1 str = escape_command_arg(&buf, "~/need \t\ndquotes", true);
476 1 EXPECT_STREQ(str, "\"~/need \\t\\ndquotes\"");
477
478 1 str = escape_command_arg(&buf, "~/file-with-no-spaces", false);
479 1 EXPECT_STREQ(str, "~/file-with-no-spaces");
480 1 str = escape_command_arg(&buf, "~/file-with-no-spaces", true);
481 1 EXPECT_STREQ(str, "\\~/file-with-no-spaces");
482
483 1 str = escape_command_arg(&buf, "~/", false);
484 1 EXPECT_STREQ(str, "~/");
485 1 str = escape_command_arg(&buf, "~/", true);
486 1 EXPECT_STREQ(str, "\\~/");
487
488 1 string_free(&buf);
489 1 }
490
491 1 static void test_command_struct_layout(TestContext *ctx)
492 {
493 1 const Command *cmd = find_normal_command("open");
494 1 EXPECT_STREQ(cmd->name, "open");
495 1 EXPECT_STREQ(cmd->flags, "e=gt");
496 1 EXPECT_FALSE(cmd->cmdopts & CMDOPT_ALLOW_IN_RC);
497 1 EXPECT_UINT_EQ(cmd->min_args, 0);
498 1 EXPECT_UINT_EQ(cmd->max_args, 0xFF);
499
500 IGNORE_WARNING("-Wpedantic")
501 1 EXPECT_NONNULL(cmd->cmd);
502 UNIGNORE_WARNINGS
503 1 }
504
505 1 static void test_cmdargs_flagset_idx(TestContext *ctx)
506 {
507 1 EXPECT_EQ(cmdargs_flagset_idx('A'), 1);
508 1 EXPECT_EQ(cmdargs_flagset_idx('Z'), 26);
509 1 EXPECT_EQ(cmdargs_flagset_idx('a'), 27);
510 1 EXPECT_EQ(cmdargs_flagset_idx('z'), 52);
511 1 EXPECT_EQ(cmdargs_flagset_idx('0'), 53);
512 1 EXPECT_EQ(cmdargs_flagset_idx('1'), 54);
513 1 EXPECT_EQ(cmdargs_flagset_idx('9'), 62);
514
515
2/2
✓ Branch 0 (22→17) taken 75 times.
✓ Branch 1 (22→23) taken 1 times.
76 for (unsigned char c = '0', z = 'z'; c <= z; c++) {
516
2/2
✓ Branch 0 (17→18) taken 62 times.
✓ Branch 1 (17→21) taken 13 times.
75 if (ascii_isalnum(c)) {
517 62 const unsigned int idx = cmdargs_flagset_idx(c);
518 62 EXPECT_TRUE(idx < 63);
519 62 EXPECT_EQ(idx, base64_decode(c) + 1);
520 }
521 }
522 1 }
523
524 1 static void test_cmdargs_convert_flags_1(TestContext *ctx)
525 {
526 1 static const FlagMapping map[] = {
527 {'b', REPLACE_BASIC},
528 {'c', REPLACE_CONFIRM},
529 {'g', REPLACE_GLOBAL},
530 {'i', REPLACE_IGNORE_CASE},
531 };
532
533 2 const CommandArgs a = {
534 1 .flag_set = cmdargs_flagset_value('c') | cmdargs_flagset_value('g')
535 };
536
537 1 ReplaceFlags flags = cmdargs_convert_flags(&a, map, ARRAYLEN(map));
538 1 EXPECT_EQ(flags, REPLACE_CONFIRM | REPLACE_GLOBAL);
539 1 }
540
541 1 static void test_cmdargs_convert_flags_2(TestContext *ctx)
542 {
543 1 static const FlagMapping map[] = {
544 {'C', EFLAG_CMD_HIST},
545 {'S', EFLAG_SEARCH_HIST},
546 {'F', EFLAG_FILE_HIST},
547 {'H', EFLAG_ALL_HIST},
548 };
549
550 1 CommandArgs a = {.flag_set = cmdargs_flagset_value('C')};
551 1 EditorFlags flags = cmdargs_convert_flags(&a, map, ARRAYLEN(map));
552 1 EXPECT_EQ(flags, EFLAG_CMD_HIST);
553
554 1 a.flag_set |= cmdargs_flagset_value('F');
555 1 flags = cmdargs_convert_flags(&a, map, ARRAYLEN(map));
556 1 EXPECT_EQ(flags, EFLAG_CMD_HIST | EFLAG_FILE_HIST);
557
558 1 a.flag_set |= cmdargs_flagset_value('S');
559 1 flags = cmdargs_convert_flags(&a, map, ARRAYLEN(map));
560 1 EXPECT_EQ(flags, EFLAG_ALL_HIST);
561
562 1 a.flag_set = cmdargs_flagset_value('H');
563 1 EXPECT_EQ(flags, cmdargs_convert_flags(&a, map, ARRAYLEN(map)));
564 1 }
565
566 1 static void test_add_alias(TestContext *ctx)
567 {
568 1 const char name[] = "insert-foo";
569 1 const char cmd[] = "insert -m foo";
570 1 HashMap m = HASHMAP_INIT(HMAP_NO_FLAGS);
571 1 add_alias(&m, name, cmd);
572 1 EXPECT_STREQ(find_alias(&m, name), cmd);
573 1 EXPECT_EQ(m.count, 1);
574
575 1 remove_alias(&m, "insert-foo");
576 1 EXPECT_NULL(find_alias(&m, name));
577 1 EXPECT_EQ(m.count, 0);
578 1 hashmap_free(&m, free);
579 1 }
580
581 static const TestEntry tests[] = {
582 TEST(test_parse_command_arg),
583 TEST(test_parse_commands),
584 TEST(test_find_normal_command),
585 TEST(test_parse_args),
586 TEST(test_cached_command_new),
587 TEST(test_string_append_escaped_arg),
588 TEST(test_command_struct_layout),
589 TEST(test_cmdargs_flagset_idx),
590 TEST(test_cmdargs_convert_flags_1),
591 TEST(test_cmdargs_convert_flags_2),
592 TEST(test_add_alias),
593 };
594
595 const TestGroup command_tests = TEST_GROUP(tests);
596