#include "clar_libgit2.h" #include "fileops.h" typedef struct name_data { int count; /* return count */ char *name; /* filename */ } name_data; typedef struct walk_data { char *sub; /* sub-directory name */ name_data *names; /* name state data */ git_buf path; } walk_data; static char *top_dir = "dir-walk"; static walk_data *state_loc; static void setup(walk_data *d) { name_data *n; cl_must_pass(p_mkdir(top_dir, 0777)); cl_must_pass(p_chdir(top_dir)); if (strcmp(d->sub, ".") != 0) cl_must_pass(p_mkdir(d->sub, 0777)); cl_git_pass(git_buf_sets(&d->path, d->sub)); state_loc = d; for (n = d->names; n->name; n++) { git_file fd = p_creat(n->name, 0666); cl_assert(fd >= 0); p_close(fd); n->count = 0; } } static void dirent_cleanup__cb(void *_d) { walk_data *d = _d; name_data *n; for (n = d->names; n->name; n++) { cl_must_pass(p_unlink(n->name)); } if (strcmp(d->sub, ".") != 0) cl_must_pass(p_rmdir(d->sub)); cl_must_pass(p_chdir("..")); cl_must_pass(p_rmdir(top_dir)); git_buf_free(&d->path); } static void check_counts(walk_data *d) { name_data *n; for (n = d->names; n->name; n++) { cl_assert(n->count == 1); } } static int update_count(name_data *data, const char *name) { name_data *n; for (n = data; n->name; n++) { if (!strcmp(n->name, name)) { n->count++; return 0; } } return GIT_ERROR; } static int one_entry(void *state, git_buf *path) { walk_data *d = (walk_data *) state; if (state != state_loc) return GIT_ERROR; if (path != &d->path) return GIT_ERROR; return update_count(d->names, path->ptr); } static name_data dot_names[] = { { 0, "./a" }, { 0, "./asdf" }, { 0, "./pack-foo.pack" }, { 0, NULL } }; static walk_data dot = { ".", dot_names, GIT_BUF_INIT }; /* make sure that the '.' folder is not traversed */ void test_core_dirent__dont_traverse_dot(void) { cl_set_cleanup(&dirent_cleanup__cb, &dot); setup(&dot); cl_git_pass(git_path_direach(&dot.path, 0, one_entry, &dot)); check_counts(&dot); } static name_data sub_names[] = { { 0, "sub/a" }, { 0, "sub/asdf" }, { 0, "sub/pack-foo.pack" }, { 0, NULL } }; static walk_data sub = { "sub", sub_names, GIT_BUF_INIT }; /* traverse a subfolder */ void test_core_dirent__traverse_subfolder(void) { cl_set_cleanup(&dirent_cleanup__cb, &sub); setup(&sub); cl_git_pass(git_path_direach(&sub.path, 0, one_entry, &sub)); check_counts(&sub); } static walk_data sub_slash = { "sub/", sub_names, GIT_BUF_INIT }; /* traverse a slash-terminated subfolder */ void test_core_dirent__traverse_slash_terminated_folder(void) { cl_set_cleanup(&dirent_cleanup__cb, &sub_slash); setup(&sub_slash); cl_git_pass(git_path_direach(&sub_slash.path, 0, one_entry, &sub_slash)); check_counts(&sub_slash); } static name_data empty_names[] = { { 0, NULL } }; static walk_data empty = { "empty", empty_names, GIT_BUF_INIT }; /* make sure that empty folders are not traversed */ void test_core_dirent__dont_traverse_empty_folders(void) { cl_set_cleanup(&dirent_cleanup__cb, &empty); setup(&empty); cl_git_pass(git_path_direach(&empty.path, 0, one_entry, &empty)); check_counts(&empty); /* make sure callback not called */ cl_assert(git_path_is_empty_dir(empty.path.ptr)); } static name_data odd_names[] = { { 0, "odd/.a" }, { 0, "odd/..c" }, /* the following don't work on cygwin/win32 */ /* { 0, "odd/.b." }, */ /* { 0, "odd/..d.." }, */ { 0, NULL } }; static walk_data odd = { "odd", odd_names, GIT_BUF_INIT }; /* make sure that strange looking filenames ('..c') are traversed */ void test_core_dirent__traverse_weird_filenames(void) { cl_set_cleanup(&dirent_cleanup__cb, &odd); setup(&odd); cl_git_pass(git_path_direach(&odd.path, 0, one_entry, &odd)); check_counts(&odd); } /* test filename length limits */ void test_core_dirent__length_limits(void) { char *big_filename = (char *)git__malloc(FILENAME_MAX + 1); memset(big_filename, 'a', FILENAME_MAX + 1); big_filename[FILENAME_MAX] = 0; cl_must_fail(p_creat(big_filename, 0666)); git__free(big_filename); } void test_core_dirent__empty_dir(void) { cl_must_pass(p_mkdir("empty_dir", 0777)); cl_assert(git_path_is_empty_dir("empty_dir")); cl_git_mkfile("empty_dir/content", "whatever\n"); cl_assert(!git_path_is_empty_dir("empty_dir")); cl_assert(!git_path_is_empty_dir("empty_dir/content")); cl_must_pass(p_unlink("empty_dir/content")); cl_must_pass(p_mkdir("empty_dir/content", 0777)); cl_assert(!git_path_is_empty_dir("empty_dir")); cl_assert(git_path_is_empty_dir("empty_dir/content")); cl_must_pass(p_rmdir("empty_dir/content")); cl_must_pass(p_rmdir("empty_dir")); } static void handle_next(git_path_diriter *diriter, walk_data *walk) { const char *fullpath, *filename; size_t fullpath_len, filename_len; cl_git_pass(git_path_diriter_fullpath(&fullpath, &fullpath_len, diriter)); cl_git_pass(git_path_diriter_filename(&filename, &filename_len, diriter)); cl_assert_equal_strn(fullpath, "sub/", 4); cl_assert_equal_s(fullpath+4, filename); update_count(walk->names, fullpath); } /* test directory iterator */ void test_core_dirent__diriter_with_fullname(void) { git_path_diriter diriter = GIT_PATH_DIRITER_INIT; int error; cl_set_cleanup(&dirent_cleanup__cb, &sub); setup(&sub); cl_git_pass(git_path_diriter_init(&diriter, sub.path.ptr, 0)); while ((error = git_path_diriter_next(&diriter)) == 0) handle_next(&diriter, &sub); cl_assert_equal_i(error, GIT_ITEROVER); git_path_diriter_free(&diriter); check_counts(&sub); } void test_core_dirent__diriter_at_directory_root(void) { git_path_diriter diriter = GIT_PATH_DIRITER_INIT; const char *sandbox_path, *path; char *root_path; size_t path_len; int root_offset, error; sandbox_path = clar_sandbox_path(); cl_assert((root_offset = git_path_root(sandbox_path)) >= 0); cl_assert(root_path = git__calloc(1, root_offset + 2)); strncpy(root_path, sandbox_path, root_offset + 1); cl_git_pass(git_path_diriter_init(&diriter, root_path, 0)); while ((error = git_path_diriter_next(&diriter)) == 0) { cl_git_pass(git_path_diriter_fullpath(&path, &path_len, &diriter)); cl_assert(path_len > (size_t)(root_offset + 1)); cl_assert(path[root_offset+1] != '/'); } cl_assert_equal_i(error, GIT_ITEROVER); git_path_diriter_free(&diriter); git__free(root_path); }