#include <stic.h> #include <sys/stat.h> /* chmod() */ #include <limits.h> /* INT_MAX */ #include <string.h> /* memset() strcpy() */ #include <time.h> /* time() */ #include <unistd.h> /* usleep() */ #include <test-utils.h> #include "../../src/cfg/config.h" #include "../../src/compat/fs_limits.h" #include "../../src/compat/os.h" #include "../../src/ui/column_view.h" #include "../../src/ui/fileview.h" #include "../../src/ui/ui.h" #include "../../src/utils/cancellation.h" #include "../../src/utils/dynarray.h" #include "../../src/utils/fs.h" #include "../../src/utils/str.h" #include "../../src/compare.h" #include "../../src/filelist.h" #include "../../src/flist_pos.h" #include "../../src/fops_misc.h" #include "../../src/status.h" #define WAIT_FOR(expr, duration_ms) \ do \ { \ int duration; \ for(duration = 0; duration < duration_ms; ++duration) \ { \ int predicate = (expr);\ if(predicate) \ { \ break; \ } \ usleep(1000/*us*/); \ } \ if(duration == duration_ms) \ { \ assert_fail("Timed out waiting for: " # expr); \ } \ } \ while(0) static char cwd[PATH_MAX + 1]; SETUP_ONCE() { assert_non_null(get_cwd(cwd, sizeof(cwd))); } SETUP() { curr_view = &lwin; other_view = &rwin; view_setup(&lwin); opt_handlers_setup(); } TEARDOWN() { view_teardown(&lwin); opt_handlers_teardown(); cfg.ignore_case = 0; } TEST(compare_view_defines_id_grouping) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "compare/a", cwd); compare_one_pane(&lwin, CT_CONTENTS, LT_ALL, CF_NONE); assert_int_equal(3, lwin.list_rows); assert_int_equal(0, lwin.list_pos); assert_int_equal(1, lwin.list_pos = fpos_find_group(&lwin, 1)); assert_int_equal(2, lwin.list_pos = fpos_find_group(&lwin, 1)); assert_int_equal(1, lwin.list_pos = fpos_find_group(&lwin, 0)); } TEST(goto_file_nagivates_to_files) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "existing-files", cwd); load_dir_list(&lwin, 1); cfg.ignore_case = 1; assert_int_equal(-1, fpos_find_by_ch(&lwin, 'a', 0, 0)); assert_int_equal(-1, fpos_find_by_ch(&lwin, 'A', 1, 0)); assert_int_equal(0, fpos_find_by_ch(&lwin, 'A', 0, 1)); assert_int_equal(0, fpos_find_by_ch(&lwin, 'a', 1, 1)); assert_int_equal(1, fpos_find_by_ch(&lwin, 'b', 0, 0)); assert_int_equal(-1, fpos_find_by_ch(&lwin, 'B', 1, 0)); assert_int_equal(1, fpos_find_by_ch(&lwin, 'B', 0, 1)); assert_int_equal(1, fpos_find_by_ch(&lwin, 'b', 1, 1)); } TEST(goto_file_nagivates_to_files_with_case_override) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "existing-files", cwd); load_dir_list(&lwin, 1); cfg.ignore_case = 1; cfg.case_override = CO_GOTO_FILE; cfg.case_ignore = 0; assert_int_equal(-1, fpos_find_by_ch(&lwin, 'a', 0, 0)); assert_int_equal(-1, fpos_find_by_ch(&lwin, 'A', 1, 0)); assert_int_equal(0, fpos_find_by_ch(&lwin, 'A', 0, 1)); assert_int_equal(0, fpos_find_by_ch(&lwin, 'a', 1, 1)); assert_int_equal(1, fpos_find_by_ch(&lwin, 'b', 0, 0)); assert_int_equal(-1, fpos_find_by_ch(&lwin, 'B', 1, 0)); assert_int_equal(0, fpos_find_by_ch(&lwin, 'B', 0, 1)); assert_int_equal(1, fpos_find_by_ch(&lwin, 'b', 1, 1)); cfg.case_override = 0; } TEST(find_directory) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "tree/dir1", cwd); load_dir_list(&lwin, 1); assert_int_equal(2, lwin.list_rows); assert_int_equal(0, fpos_next_dir(&lwin)); assert_int_equal(0, fpos_prev_dir(&lwin)); lwin.list_pos = 1; assert_int_equal(1, fpos_next_dir(&lwin)); assert_int_equal(0, fpos_prev_dir(&lwin)); } TEST(find_selected) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "existing-files", cwd); load_dir_list(&lwin, 1); assert_int_equal(3, lwin.list_rows); lwin.dir_entry[0].selected = 1; lwin.dir_entry[2].selected = 1; lwin.selected_files = 2; assert_int_equal(2, fpos_next_selected(&lwin)); assert_int_equal(0, fpos_prev_selected(&lwin)); lwin.list_pos = 1; assert_int_equal(2, fpos_next_selected(&lwin)); assert_int_equal(0, fpos_prev_selected(&lwin)); } TEST(find_first_and_last_siblings) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "tree", cwd); assert_success(flist_load_tree(&lwin, lwin.curr_dir, INT_MAX)); assert_int_equal(12, lwin.list_rows); assert_int_equal(0, fpos_first_sibling(&lwin)); assert_int_equal(11, fpos_last_sibling(&lwin)); lwin.list_pos = 8; assert_int_equal(0, fpos_first_sibling(&lwin)); assert_int_equal(11, fpos_last_sibling(&lwin)); lwin.list_pos = 11; assert_int_equal(0, fpos_first_sibling(&lwin)); assert_int_equal(11, fpos_last_sibling(&lwin)); lwin.list_pos = 1; assert_int_equal(1, fpos_first_sibling(&lwin)); assert_int_equal(7, fpos_last_sibling(&lwin)); } TEST(find_next_and_prev_dir_sibling) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "tree", cwd); assert_success(flist_load_tree(&lwin, lwin.curr_dir, INT_MAX)); assert_int_equal(12, lwin.list_rows); assert_int_equal(0, fpos_prev_dir_sibling(&lwin)); assert_int_equal(8, fpos_next_dir_sibling(&lwin)); lwin.list_pos = 8; assert_int_equal(0, fpos_prev_dir_sibling(&lwin)); assert_int_equal(8, fpos_next_dir_sibling(&lwin)); lwin.list_pos = 11; assert_int_equal(8, fpos_prev_dir_sibling(&lwin)); assert_int_equal(11, fpos_next_dir_sibling(&lwin)); /* When last sibling is an empty dir. */ cfg.dot_dirs &= ~DD_TREE_LEAFS_PARENT; lwin.dir_entry[6].selected = 1; lwin.selected_files = 1; flist_custom_exclude(&lwin, 1); assert_int_equal(11, lwin.list_rows); lwin.list_pos = 5; assert_int_equal(2, fpos_prev_dir_sibling(&lwin)); lwin.list_pos = 2; assert_int_equal(5, fpos_next_dir_sibling(&lwin)); } TEST(find_next_and_prev_mismatches) { curr_view = &lwin; other_view = &rwin; view_setup(&rwin); strcpy(lwin.curr_dir, TEST_DATA_PATH "/compare/a"); strcpy(rwin.curr_dir, TEST_DATA_PATH "/compare/b"); (void)compare_two_panes(CT_CONTENTS, LT_ALL, CF_GROUP_PATHS | CF_SHOW); assert_int_equal(4, lwin.list_rows); assert_int_equal(4, rwin.list_rows); lwin.list_pos = 0; assert_int_equal(0, fpos_prev_mismatch(&lwin)); assert_int_equal(1, fpos_next_mismatch(&lwin)); lwin.list_pos = 1; assert_int_equal(1, fpos_prev_mismatch(&lwin)); assert_int_equal(2, fpos_next_mismatch(&lwin)); lwin.list_pos = 2; assert_int_equal(1, fpos_prev_mismatch(&lwin)); assert_int_equal(2, fpos_next_mismatch(&lwin)); lwin.list_pos = 3; assert_int_equal(2, fpos_prev_mismatch(&lwin)); assert_int_equal(3, fpos_next_mismatch(&lwin)); view_teardown(&rwin); } TEST(current_unselected_file_is_marked) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "existing-files", cwd); load_dir_list(&lwin, 1); assert_int_equal(0, lwin.list_pos); assert_int_equal(3, lwin.list_rows); assert_int_equal(1, mark_selection_or_current(&lwin)); assert_true(lwin.dir_entry[0].marked); assert_false(lwin.dir_entry[1].marked); assert_false(lwin.dir_entry[2].marked); assert_false(lwin.dir_entry[0].selected); assert_false(lwin.dir_entry[1].selected); assert_false(lwin.dir_entry[2].selected); } TEST(selection_is_marked) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "existing-files", cwd); load_dir_list(&lwin, 1); assert_int_equal(0, lwin.list_pos); assert_int_equal(3, lwin.list_rows); lwin.selected_files = 1; lwin.dir_entry[1].selected = 1; assert_int_equal(1, mark_selection_or_current(&lwin)); assert_false(lwin.dir_entry[0].marked); assert_true(lwin.dir_entry[1].marked); assert_false(lwin.dir_entry[2].marked); assert_false(lwin.dir_entry[0].selected); assert_true(lwin.dir_entry[1].selected); assert_false(lwin.dir_entry[2].selected); } TEST(filelist_reloading_corrects_current_position) { make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "existing-files", cwd); load_dir_list(&lwin, 1); assert_int_equal(3, lwin.list_rows); lwin.list_pos = 2; assert_success(replace_matcher(&lwin.manual_filter, "/.*/")); load_dir_list(&lwin, 1); assert_int_equal(0, lwin.list_pos); assert_int_equal(1, lwin.list_rows); } TEST(fentry_get_size_returns_file_size_for_files) { char origin[] = "/"; const dir_entry_t entry = { .name = "f", .origin = origin, .size = 123U, .type = FT_REG }; update_string(&cfg.shell, ""); assert_success(stats_init(&cfg)); assert_ulong_equal(entry.size, fentry_get_size(&lwin, &entry)); update_string(&cfg.shell, NULL); } TEST(fentry_get_size_returns_file_size_if_nothing_cached) { char origin[] = "/"; const dir_entry_t entry = { .name = "f", .origin = origin, .size = 123U, .type = FT_DIR }; update_string(&cfg.shell, ""); assert_success(stats_init(&cfg)); assert_ulong_equal(entry.size, fentry_get_size(&lwin, &entry)); update_string(&cfg.shell, NULL); } TEST(fentry_get_recalculates_size) { char dir_origin[] = SANDBOX_PATH; dir_entry_t dir = { .name = "dir", .origin = dir_origin, .size = 123U, .type = FT_DIR, .mtime = time(NULL) }; char subdir_origin[] = SANDBOX_PATH "/dir"; dir_entry_t subdir = { .name = "subdir", .origin = subdir_origin, .size = 123U, .type = FT_DIR, .mtime = time(NULL) }; update_string(&cfg.shell, ""); assert_success(stats_init(&cfg)); assert_success(os_mkdir(SANDBOX_PATH "/dir", 0700)); assert_success(os_mkdir(SANDBOX_PATH "/dir/subdir", 0700)); (void)fops_dir_size(SANDBOX_PATH, 0, &no_cancellation); assert_ulong_equal(0, fentry_get_size(&lwin, &dir)); assert_ulong_equal(0, fentry_get_size(&lwin, &subdir)); /* WAIT_FOR() is used to wait for file system changes to be visible, they seem * to be postponed on Windows (at least in Wine, but Windows can do that * too). */ copy_file(TEST_DATA_PATH "/various-sizes/block-size-file", SANDBOX_PATH "/dir/file"); WAIT_FOR((dir.mtime += 1000, fentry_get_size(&lwin, &dir) == 8192), 100/*ms*/); assert_ulong_equal(8192, fentry_get_size(&lwin, &dir)); assert_ulong_equal(0, fentry_get_size(&lwin, &subdir)); copy_file(TEST_DATA_PATH "/various-sizes/block-size-file", SANDBOX_PATH "/dir/subdir/file"); WAIT_FOR((subdir.mtime += 1000, fentry_get_size(&lwin, &subdir) == 8192), 100/*ms*/); assert_ulong_equal(8192, fentry_get_size(&lwin, &subdir)); assert_ulong_equal(16384, fentry_get_size(&lwin, &dir)); assert_success(remove(SANDBOX_PATH "/dir/subdir/file")); WAIT_FOR((subdir.mtime += 1000, fentry_get_size(&lwin, &subdir) == 0), 100/*ms*/); assert_ulong_equal(0, fentry_get_size(&lwin, &subdir)); assert_ulong_equal(8192, fentry_get_size(&lwin, &dir)); assert_success(remove(SANDBOX_PATH "/dir/file")); dir.mtime += 1000; assert_ulong_equal(0, fentry_get_size(&lwin, &dir)); assert_ulong_equal(0, fentry_get_size(&lwin, &subdir)); assert_success(rmdir(SANDBOX_PATH "/dir/subdir")); assert_success(rmdir(SANDBOX_PATH "/dir")); update_string(&cfg.shell, NULL); } TEST(fentry_get_nitems_calculates_number_of_items) { char origin[] = TEST_DATA_PATH; const dir_entry_t entry = { .name = "existing-files", .origin = origin, .type = FT_DIR }; update_string(&cfg.shell, ""); assert_success(stats_init(&cfg)); assert_ulong_equal(3, fentry_get_nitems(&lwin, &entry)); update_string(&cfg.shell, NULL); } TEST(fentry_get_nitems_recalculates_number_of_items) { char origin[] = SANDBOX_PATH; dir_entry_t entry = { .name = ".", .origin = origin, .type = FT_DIR }; update_string(&cfg.shell, ""); assert_success(stats_init(&cfg)); assert_ulong_equal(0, fentry_get_nitems(&lwin, &entry)); create_file(SANDBOX_PATH "/a"); entry.mtime = time(NULL) + 10; assert_ulong_equal(1, fentry_get_nitems(&lwin, &entry)); update_string(&cfg.shell, NULL); assert_success(remove(SANDBOX_PATH "/a")); } TEST(root_path_does_not_get_more_than_one_slash, IF(not_windows)) { const char type_decs_slash[FT_COUNT][2][9] = { [FT_DIR][DECORATION_SUFFIX][0] = '/', }; const char type_decs_brackets[FT_COUNT][2][9] = { [FT_DIR][DECORATION_PREFIX][0] = '[', [FT_DIR][DECORATION_SUFFIX][0] = ']', }; char name[NAME_MAX + 1]; flist_custom_start(&lwin, "test"); flist_custom_add(&lwin, "/"); assert_true(flist_custom_finish(&lwin, CV_REGULAR, 0) == 0); get_short_path_of(&lwin, &lwin.dir_entry[0], 1, 1, sizeof(name), name); assert_string_equal("/", name); memcpy(&cfg.type_decs, &type_decs_slash, sizeof(cfg.type_decs)); get_short_path_of(&lwin, &lwin.dir_entry[0], 1, 1, sizeof(name), name); assert_string_equal("/", name); memcpy(&cfg.type_decs, &type_decs_brackets, sizeof(cfg.type_decs)); } TEST(duplicated_entries_detected) { lwin.list_rows = 2; lwin.list_pos = 0; lwin.top_line = 0; lwin.dir_entry = dynarray_cextend(NULL, lwin.list_rows*sizeof(*lwin.dir_entry)); lwin.dir_entry[0].name = strdup("lfile0"); lwin.dir_entry[0].origin = &lwin.curr_dir[0]; lwin.dir_entry[1].name = strdup("lfile0"); lwin.dir_entry[1].origin = &lwin.curr_dir[0]; check_file_uniqueness(&lwin); assert_int_equal(1, lwin.list_rows); assert_true(lwin.has_dups); } TEST(cache_handles_noexec_dirs, IF(regular_unix_user)) { assert_success(os_mkdir(SANDBOX_PATH "/dir", 0700)); create_file(SANDBOX_PATH "/dir/file"); assert_success(chmod(SANDBOX_PATH "/dir", 0666)); cached_entries_t cache = {}; assert_true(flist_update_cache(&lwin, &cache, SANDBOX_PATH "/dir")); assert_int_equal(0, cache.entries.nentries); flist_free_cache(&cache); assert_success(chmod(SANDBOX_PATH "/dir", 0777)); assert_success(remove(SANDBOX_PATH "/dir/file")); assert_success(rmdir(SANDBOX_PATH "/dir")); } TEST(filename_is_formatted_according_to_column_and_filetype) { char origin[] = ""; dir_entry_t file_entry = { .name = "a.b.c", .type = FT_REG, .origin = origin, }; dir_entry_t dir_entry = { .name = "a.b.c", .type = FT_DIR, .origin = origin, }; column_data_t cdt = { .view = &lwin }; format_info_t info = { .data = &cdt }; char name[16]; memset(&cfg.type_decs, '\0', sizeof(cfg.type_decs)); cfg.type_decs[FT_DIR][DECORATION_SUFFIX][0] = '/'; cdt.entry = &dir_entry; info.id = SK_BY_INAME; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.b.c/", name); info.id = SK_BY_NAME; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.b.c/", name); info.id = SK_BY_FILEROOT; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.b.c/", name); info.id = SK_BY_ROOT; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.b/", name); cdt.entry = &file_entry; info.id = SK_BY_INAME; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.b.c", name); info.id = SK_BY_NAME; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.b.c", name); info.id = SK_BY_FILEROOT; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.b", name); info.id = SK_BY_ROOT; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.b", name); } TEST(filename_is_escaped_only_for_display) { char origin[] = ""; column_data_t cdt = { .view = &lwin }; format_info_t info = { .data = &cdt }; char name[16]; memset(&cfg.type_decs, '\0', sizeof(cfg.type_decs)); cfg.type_decs[FT_DIR][DECORATION_SUFFIX][0] = '/'; dir_entry_t dir_entry = { .name = "a.\x11.c", .type = FT_DIR, .origin = origin, }; cdt.entry = &dir_entry; info.id = SK_BY_INAME; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.^Q.c/", name); info.id = SK_BY_NAME; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.^Q.c/", name); info.id = SK_BY_FILEROOT; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.^Q.c/", name); info.id = SK_BY_ROOT; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.^Q/", name); get_short_path_of(&lwin, cdt.entry, NF_FULL, /*drop_prefix=*/1, sizeof(name), name); assert_string_equal("/a.\x11.c/", name); dir_entry_t file_entry = { .name = "a.\x10.c", .type = FT_REG, .origin = origin, }; cdt.entry = &file_entry; info.id = SK_BY_INAME; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.^P.c", name); info.id = SK_BY_NAME; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.^P.c", name); info.id = SK_BY_FILEROOT; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.^P", name); info.id = SK_BY_ROOT; format_name(NULL, sizeof(name), name, &info); assert_string_equal("a.^P", name); get_short_path_of(&lwin, cdt.entry, NF_FULL, /*drop_prefix=*/1, sizeof(name), name); assert_string_equal("/a.\x10.c", name); } TEST(fview_previews_works) { lwin.list_rows = 2; lwin.dir_entry = dynarray_cextend(NULL, lwin.list_rows*sizeof(*lwin.dir_entry)); lwin.dir_entry[0].name = strdup("file"); lwin.dir_entry[0].origin = &lwin.curr_dir[0]; lwin.dir_entry[1].name = strdup("dir"); lwin.dir_entry[1].origin = &lwin.curr_dir[0]; lwin.dir_entry[1].type = FT_DIR; strcpy(lwin.curr_dir, "/tests/fake"); lwin.miller_preview = MP_DIRS; lwin.list_pos = 0; assert_false(fview_previews(&lwin, "/tests/fake/file")); lwin.list_pos = 1; assert_false(fview_previews(&lwin, "/tests/fake/dir")); assert_false(fview_previews(&lwin, "/unrelated/path")); lwin.miller_view = 1; lwin.list_pos = 0; assert_false(fview_previews(&lwin, "/tests/fake/file")); lwin.list_pos = 1; assert_false(fview_previews(&lwin, "/tests/fake/dir")); assert_false(fview_previews(&lwin, "/unrelated/path")); lwin.miller_preview = MP_FILES; lwin.list_pos = 0; assert_true(fview_previews(&lwin, "/tests/fake/file")); lwin.list_pos = 1; assert_false(fview_previews(&lwin, "/tests/fake/dir")); assert_false(fview_previews(&lwin, "/unrelated/path")); lwin.miller_preview = MP_ALL; lwin.list_pos = 0; assert_true(fview_previews(&lwin, "/tests/fake/file")); lwin.list_pos = 1; assert_false(fview_previews(&lwin, "/tests/fake/dir")); assert_false(fview_previews(&lwin, "/unrelated/path")); } TEST(fentry_points_to_works, IF(not_windows)) { make_symlink(".", SANDBOX_PATH "/link"); make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), SANDBOX_PATH, "", cwd); load_dir_list(&lwin, 1); assert_int_equal(1, lwin.list_rows); assert_true(fentry_points_to(&lwin.dir_entry[0], lwin.curr_dir)); char link_path[PATH_MAX + 1]; make_abs_path(link_path, sizeof(link_path), SANDBOX_PATH, "link", cwd); assert_true(fentry_points_to(&lwin.dir_entry[0], link_path)); remove_file(SANDBOX_PATH "/link"); } /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ /* vim: set cinoptions+=t0 filetype=c : */