#include <stic.h> #include <sys/stat.h> /* chmod() */ #include <unistd.h> /* F_OK access() chdir() rmdir() unlink() */ #include <locale.h> /* LC_ALL setlocale() */ #include <stdio.h> /* FILE fclose() fopen() fprintf() remove() */ #include <string.h> /* strcpy() */ #include <test-utils.h> #include "../../src/compat/fs_limits.h" #include "../../src/cfg/config.h" #include "../../src/engine/cmds.h" #include "../../src/engine/keys.h" #include "../../src/engine/mode.h" #include "../../src/modes/modes.h" #include "../../src/ui/ui.h" #include "../../src/utils/dynarray.h" #include "../../src/utils/env.h" #include "../../src/utils/fs.h" #include "../../src/utils/path.h" #include "../../src/utils/str.h" #include "../../src/cmd_core.h" #include "../../src/filelist.h" #include "../../src/ops.h" #include "../../src/registers.h" #include "../../src/status.h" #include "../../src/undo.h" static int builtin_cmd(const cmd_info_t* cmd_info); static const cmd_add_t commands[] = { { .name = "builtin", .abbr = NULL, .id = -1, .descr = "descr", .flags = HAS_EMARK | HAS_BG_FLAG, .handler = &builtin_cmd, .min_args = 0, .max_args = 0, }, { .name = "onearg", .abbr = NULL, .id = -1, .descr = "descr", .flags = 0, .handler = &builtin_cmd, .min_args = 1, .max_args = 1, }, }; static int called; static int bg; static char *arg; static char cwd[PATH_MAX + 1]; static char sandbox[PATH_MAX + 1]; static char test_data[PATH_MAX + 1]; SETUP_ONCE() { assert_non_null(get_cwd(cwd, sizeof(cwd))); make_abs_path(sandbox, sizeof(sandbox), SANDBOX_PATH, "", cwd); make_abs_path(test_data, sizeof(test_data), TEST_DATA_PATH, "", cwd); } SETUP() { view_setup(&lwin); view_setup(&rwin); curr_view = &lwin; other_view = &rwin; conf_setup(); cfg.use_system_calls = 1; cmds_init(); vle_cmds_add(commands, ARRAY_LEN(commands)); called = 0; undo_setup(); } TEARDOWN() { conf_teardown(); view_teardown(&lwin); view_teardown(&rwin); vle_cmds_reset(); undo_teardown(); } static int builtin_cmd(const cmd_info_t* cmd_info) { called = 1; bg = cmd_info->bg; if(cmd_info->argc != 0) { replace_string(&arg, cmd_info->argv[0]); } return 0; } TEST(space_amp) { assert_success(cmds_dispatch("builtin &", &lwin, CIT_COMMAND)); assert_true(called); assert_true(bg); } TEST(space_amp_spaces) { assert_success(cmds_dispatch("builtin & ", &lwin, CIT_COMMAND)); assert_true(called); assert_true(bg); } TEST(space_bg_bar) { assert_success(cmds_dispatch("builtin &|", &lwin, CIT_COMMAND)); assert_true(called); assert_true(bg); } TEST(bg_space_bar) { assert_success(cmds_dispatch("builtin& |", &lwin, CIT_COMMAND)); assert_true(called); assert_true(bg); } TEST(space_bg_space_bar) { assert_success(cmds_dispatch("builtin & |", &lwin, CIT_COMMAND)); assert_true(called); assert_true(bg); } TEST(non_printable_arg) { /* \x0C is Ctrl-L. */ assert_success(cmds_dispatch("onearg \x0C", &lwin, CIT_COMMAND)); assert_true(called); assert_string_equal("\x0C", arg); } TEST(non_printable_arg_in_udf) { /* \x0C is Ctrl-L. */ assert_success(cmds_dispatch("command udf :onearg \x0C", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("udf", &lwin, CIT_COMMAND)); assert_true(called); assert_string_equal("\x0C", arg); } TEST(space_last_arg_in_udf) { assert_success(cmds_dispatch("command udf :onearg \\ ", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("udf", &lwin, CIT_COMMAND)); assert_true(called); assert_string_equal(" ", arg); } TEST(bg_mark_with_space_in_udf) { assert_success(cmds_dispatch("command udf :builtin &", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("udf", &lwin, CIT_COMMAND)); assert_true(called); assert_true(bg); } TEST(bg_mark_without_space_in_udf) { assert_success(cmds_dispatch("command udf :builtin&", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("udf", &lwin, CIT_COMMAND)); assert_true(called); assert_true(bg); } TEST(shell_invocation_works_in_udf) { const char *const cmd = "command! udf echo a > out"; #ifndef _WIN32 replace_string(&cfg.shell, "/bin/sh"); replace_string(&cfg.shell_cmd_flag, "-c"); #else replace_string(&cfg.shell, "cmd"); replace_string(&cfg.shell_cmd_flag, "/C"); #endif assert_success(chdir(SANDBOX_PATH)); assert_success(cmds_dispatch(cmd, &lwin, CIT_COMMAND)); curr_view = &lwin; assert_failure(access("out", F_OK)); assert_success(cmds_dispatch("udf", &lwin, CIT_COMMAND)); assert_success(access("out", F_OK)); assert_success(unlink("out")); } TEST(envvars_of_commands_come_from_variables_unit) { assert_success(chdir(test_data)); strcpy(lwin.curr_dir, test_data); assert_false(is_root_dir(lwin.curr_dir)); assert_success(cmds_dispatch("let $ABCDE = '/'", &lwin, CIT_COMMAND)); env_set("ABCDE", SANDBOX_PATH); assert_success(cmds_dispatch("cd $ABCDE", &lwin, CIT_COMMAND)); assert_true(is_root_dir(lwin.curr_dir)); } TEST(or_operator_is_attributed_to_echo) { (void)cmds_dispatch("echo 1 || builtin", &lwin, CIT_COMMAND); assert_false(called); } TEST(bar_is_not_attributed_to_echo) { (void)cmds_dispatch("echo 1 | builtin", &lwin, CIT_COMMAND); assert_true(called); } TEST(mixed_or_operator_and_bar) { (void)cmds_dispatch("echo 1 || 0 | builtin", &lwin, CIT_COMMAND); assert_true(called); } TEST(or_operator_is_attributed_to_if) { (void)cmds_dispatch("if 0 || 0 | builtin | endif", &lwin, CIT_COMMAND); assert_false(called); } TEST(or_operator_is_attributed_to_let) { (void)cmds_dispatch("let $a = 'x'", &lwin, CIT_COMMAND); assert_string_equal("x", env_get("a")); (void)cmds_dispatch("let $a = 0 || 1", &lwin, CIT_COMMAND); assert_string_equal("1", env_get("a")); } TEST(user_command_is_executed_in_separated_scope) { assert_success(cmds_dispatch("command cmd :if 1 > 2", &lwin, CIT_COMMAND)); assert_failure(cmds_dispatch("cmd", &lwin, CIT_COMMAND)); } TEST(usercmd_can_provide_input_to_fg_process, IF(have_cat)) { assert_success(chdir(SANDBOX_PATH)); setup_grid(&lwin, 20, 2, /*init=*/1); replace_string(&lwin.dir_entry[0].name, "a"); replace_string(&lwin.dir_entry[1].name, "b"); assert_int_equal(0, cmds_dispatch("command list cat > file %Pl", &lwin, CIT_COMMAND)); lwin.dir_entry[0].marked = 1; lwin.dir_entry[1].marked = 1; lwin.pending_marking = 1; assert_int_equal(0, cmds_dispatch("list", &lwin, CIT_COMMAND)); const char *lines[] = { "/path/a", "/path/b" }; file_is("file", lines, ARRAY_LEN(lines)); remove_file("file"); } TEST(usercmd_can_provide_input_to_bg_process, IF(have_cat)) { assert_success(chdir(SANDBOX_PATH)); setup_grid(&lwin, 20, 2, /*init=*/1); replace_string(&lwin.dir_entry[0].name, "a"); replace_string(&lwin.dir_entry[1].name, "b"); assert_int_equal(0, cmds_dispatch("command list cat > file %Pl &", &lwin, CIT_COMMAND)); lwin.dir_entry[0].marked = 1; lwin.dir_entry[1].marked = 1; lwin.pending_marking = 1; assert_int_equal(0, cmds_dispatch("list", &lwin, CIT_COMMAND)); wait_for_all_bg(); const char *lines[] = { "/path/a", "/path/b" }; file_is("file", lines, ARRAY_LEN(lines)); remove_file("file"); } TEST(cv_is_built_by_emark) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), test_data, "", cwd); flist_custom_start(&lwin, "test"); assert_non_null(flist_custom_add(&lwin, "existing-files/a")); assert_success(flist_custom_finish(&lwin, CV_REGULAR, 0)); assert_success(cmds_dispatch("!echo %c %u", &lwin, CIT_COMMAND)); assert_true(flist_custom_active(&lwin)); assert_string_equal("!echo %c %u", lwin.custom.title); } TEST(title_of_cv_is_limited, IF(not_windows)) { const char *long_cmd = "!echo " " " " %c%u"; const char *title = "!echo " " ..."; assert_success(stats_init(&cfg)); make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), test_data, "", cwd); flist_custom_start(&lwin, "test"); assert_non_null(flist_custom_add(&lwin, "existing-files/a")); assert_success(flist_custom_finish(&lwin, CV_REGULAR, 0)); assert_success(cmds_dispatch(long_cmd, &lwin, CIT_COMMAND)); assert_true(flist_custom_active(&lwin)); assert_string_equal(title, lwin.custom.title); } TEST(cv_is_built_by_usercmd) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), test_data, "", cwd); flist_custom_start(&lwin, "test"); assert_non_null(flist_custom_add(&lwin, "existing-files/a")); assert_success(flist_custom_finish(&lwin, CV_REGULAR, 0)); assert_success(cmds_dispatch("command cmd echo %c %u", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("cmd", &lwin, CIT_COMMAND)); assert_true(flist_custom_active(&lwin)); assert_string_equal(":cmd", lwin.custom.title); } TEST(tree_cv_keeps_title) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), test_data, "", cwd); flist_custom_start(&lwin, "test"); assert_non_null(flist_custom_add(&lwin, "existing-files/a")); assert_success(flist_custom_finish(&lwin, CV_REGULAR, 0)); assert_success(cmds_dispatch("!echo %c %u", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("tree", &lwin, CIT_COMMAND)); assert_true(flist_custom_active(&lwin)); assert_string_equal("!echo %c %u", lwin.custom.title); } TEST(put_bg_cmd_is_parsed_correctly) { /* Simulate custom view to force failure of the command. */ lwin.curr_dir[0] = '\0'; assert_success(cmds_dispatch("put \" &", &lwin, CIT_COMMAND)); } TEST(conversion_failure_is_handled) { assert_non_null(setlocale(LC_ALL, "C")); modes_init(); /* Execution of the following commands just shouldn't crash. */ (void)cmds_dispatch("nnoremap \xee\x85\x8b", &lwin, CIT_COMMAND); (void)cmds_dispatch("nnoremap \xee\x85\x8b tj", &lwin, CIT_COMMAND); (void)cmds_dispatch("nnoremap tj \xee\x85\x8b", &lwin, CIT_COMMAND); (void)cmds_dispatch("nunmap \xee\x85\x8b", &lwin, CIT_COMMAND); (void)cmds_dispatch("unmap \xee\x85\x8b", &lwin, CIT_COMMAND); (void)cmds_dispatch("cabbrev \xee\x85\x8b tj", &lwin, CIT_COMMAND); /* The next command is needed so that there will be something to list. */ (void)cmds_dispatch("cabbrev a b", &lwin, CIT_COMMAND); (void)cmds_dispatch("cabbrev \xee\x85\x8b", &lwin, CIT_COMMAND); (void)cmds_dispatch("cunabbrev \xee\x85\x8b", &lwin, CIT_COMMAND); (void)cmds_dispatch("normal \xee\x85\x8b", &lwin, CIT_COMMAND); (void)cmds_dispatch("wincmd \xee", &lwin, CIT_COMMAND); vle_keys_reset(); } TEST(selection_is_not_reset_in_visual_mode) { modes_init(); init_view_list(&lwin); update_string(&lwin.dir_entry[0].name, "name"); (void)cmds_dispatch("if 1 == 1 | execute 'norm! ggvG' | endif", &lwin, CIT_COMMAND); assert_true(lwin.dir_entry[0].selected); assert_int_equal(1, lwin.selected_files); assert_true(vle_mode_is(VISUAL_MODE)); vle_keys_reset(); } TEST(usercmd_range_is_as_good_as_selection) { stats_init(&cfg); modes_init(); regs_init(); make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), test_data, "", cwd); populate_dir_list(&lwin, 0); /* For gA. */ assert_success(cmds_dispatch("command! size :normal gA", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("%size", &lwin, CIT_COMMAND)); wait_for_bg(); assert_string_equal("color-schemes", lwin.dir_entry[0].name); assert_ulong_equal(107, fentry_get_size(&lwin, &lwin.dir_entry[0])); assert_string_equal("various-sizes", lwin.dir_entry[lwin.list_rows - 1].name); assert_ulong_equal(73728, fentry_get_size(&lwin, &lwin.dir_entry[lwin.list_rows - 1])); /* For zf. */ assert_success(cmds_dispatch("command! afilter :normal zf", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("%afilter", &lwin, CIT_COMMAND)); populate_dir_list(&lwin, 1); assert_int_equal(1, lwin.list_rows); assert_string_equal("..", lwin.dir_entry[0].name); /* For zd. */ flist_custom_start(&lwin, "test"); assert_non_null(flist_custom_add(&lwin, "existing-files/a")); assert_non_null(flist_custom_add(&lwin, "existing-files/b")); assert_success(flist_custom_finish(&lwin, CV_REGULAR, 0)); assert_success(cmds_dispatch("command! exclude :normal zd", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("%exclude", &lwin, CIT_COMMAND)); assert_int_equal(1, lwin.list_rows); assert_string_equal("..", lwin.dir_entry[0].name); /* For :command in :usercmd. */ flist_custom_start(&lwin, "test"); assert_non_null(flist_custom_add(&lwin, "existing-files/a")); assert_non_null(flist_custom_add(&lwin, "existing-files/b")); assert_success(flist_custom_finish(&lwin, CV_REGULAR, 0)); const reg_t *reg = regs_find('"'); assert_success(cmds_dispatch("command! myyank :yank", &lwin, CIT_COMMAND)); assert_failure(cmds_dispatch("%myyank", &lwin, CIT_COMMAND)); assert_int_equal(2, reg->nfiles); assert_success(cmds_dispatch("command! myyank :yank %a", &lwin, CIT_COMMAND)); assert_failure(cmds_dispatch("%myyank", &lwin, CIT_COMMAND)); assert_int_equal(2, reg->nfiles); #ifndef _WIN32 update_string(&cfg.shell, "/bin/sh"); char script_path[PATH_MAX + 1]; make_abs_path(script_path, sizeof(script_path), SANDBOX_PATH, "script", cwd); FILE *fp = fopen(SANDBOX_PATH "/script", "w"); fprintf(fp, "#!/bin/sh\n"); fprintf(fp, "for arg; do echo \"$arg\" >> %s/vi-list; done\n", SANDBOX_PATH); fclose(fp); assert_success(chmod(SANDBOX_PATH "/script", 0777)); /* For l. */ update_string(&cfg.vi_command, script_path); flist_custom_start(&lwin, "test"); assert_non_null(flist_custom_add(&lwin, "existing-files/a")); assert_non_null(flist_custom_add(&lwin, "existing-files/b")); assert_success(flist_custom_finish(&lwin, CV_REGULAR, 0)); assert_success(cmds_dispatch("command! run :normal l", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("%run", &lwin, CIT_COMMAND)); const char *lines[] = { "existing-files/a", "existing-files/b" }; file_is(SANDBOX_PATH "/vi-list", lines, ARRAY_LEN(lines)); assert_success(remove(SANDBOX_PATH "/vi-list")); /* For cp. */ update_string(&cfg.shell, "/bin/sh"); assert_success(chdir(sandbox)); create_file("file1"); create_file("file2"); make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), sandbox, "", cwd); populate_dir_list(&lwin, 0); assert_success(cmds_dispatch("command! ex :normal 777cp", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("%ex", &lwin, CIT_COMMAND)); populate_dir_list(&lwin, 1); assert_int_equal(FT_EXEC, lwin.dir_entry[0].type); assert_int_equal(FT_EXEC, lwin.dir_entry[1].type); assert_success(remove("file1")); assert_success(remove("file2")); /* For some :menus. */ put_string(&cfg.find_prg, format_str("%s %%s %%A", script_path)); assert_success(chdir(cwd)); strcpy(lwin.curr_dir, test_data); flist_custom_start(&lwin, "test"); assert_non_null(flist_custom_add(&lwin, "existing-files/a")); assert_non_null(flist_custom_add(&lwin, "existing-files/b")); assert_success(flist_custom_finish(&lwin, CV_REGULAR, 0)); assert_failure(cmds_dispatch(".find a", &lwin, CIT_COMMAND)); const char *find_lines[] = { "existing-files/a", "a" }; file_is(SANDBOX_PATH "/vi-list", find_lines, ARRAY_LEN(find_lines)); assert_success(remove(SANDBOX_PATH "/vi-list")); assert_success(remove(script_path)); #endif regs_reset(); vle_keys_reset(); stats_reset(&cfg); } TEST(search_usercmd_perform_search) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), test_data, "", cwd); populate_dir_list(&lwin, /*reload=*/0); assert_int_equal(0, lwin.list_pos); assert_string_equal("", lwin.last_search); assert_success(cmds_dispatch("command cmd /var", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("cmd", &lwin, CIT_COMMAND)); assert_int_equal(9, lwin.list_pos); assert_string_equal("var", lwin.last_search); } TEST(search_usercmd_sets_search_direction_on_editing) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), test_data, "", cwd); populate_dir_list(&lwin, /*reload=*/0); assert_int_equal(0, lwin.list_pos); assert_string_equal("", lwin.last_search); curr_stats.last_search_backward = 1; assert_success(cmds_dispatch("command cmd /var", &lwin, CIT_COMMAND)); assert_success(cmds_dispatch("cmd", &lwin, CIT_COMMAND)); assert_int_equal(9, lwin.list_pos); assert_string_equal("var", lwin.last_search); assert_false(curr_stats.last_search_backward); } /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set cinoptions+=t0 filetype=c : */