diff options
Diffstat (limited to 'tests/ignore/path.c')
-rw-r--r-- | tests/ignore/path.c | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/tests/ignore/path.c b/tests/ignore/path.c new file mode 100644 index 000000000..bfed297c2 --- /dev/null +++ b/tests/ignore/path.c @@ -0,0 +1,540 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "path.h" +#include "fileops.h" + +static git_repository *g_repo = NULL; + +void test_ignore_path__initialize(void) +{ + g_repo = cl_git_sandbox_init("attr"); +} + +void test_ignore_path__cleanup(void) +{ + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +static void assert_is_ignored_( + bool expected, const char *filepath, const char *file, int line) +{ + int is_ignored = 0; + + cl_git_expect( + git_ignore_path_is_ignored(&is_ignored, g_repo, filepath), 0, file, line); + + clar__assert_equal( + file, line, "expected != is_ignored", 1, "%d", + (int)(expected != 0), (int)(is_ignored != 0)); +} +#define assert_is_ignored(expected, filepath) \ + assert_is_ignored_(expected, filepath, __FILE__, __LINE__) + +void test_ignore_path__honor_temporary_rules(void) +{ + cl_git_rewritefile("attr/.gitignore", "/NewFolder\n/NewFolder/NewFolder"); + + assert_is_ignored(false, "File.txt"); + assert_is_ignored(true, "NewFolder"); + assert_is_ignored(true, "NewFolder/NewFolder"); + assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); +} + +void test_ignore_path__allow_root(void) +{ + cl_git_rewritefile("attr/.gitignore", "/"); + + assert_is_ignored(false, "File.txt"); + assert_is_ignored(false, "NewFolder"); + assert_is_ignored(false, "NewFolder/NewFolder"); + assert_is_ignored(false, "NewFolder/NewFolder/File.txt"); +} + +void test_ignore_path__ignore_space(void) +{ + cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder \n/NewFolder/NewFolder"); + + assert_is_ignored(false, "File.txt"); + assert_is_ignored(true, "NewFolder"); + assert_is_ignored(true, "NewFolder/NewFolder"); + assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); +} + +void test_ignore_path__intermittent_space(void) +{ + cl_git_rewritefile("attr/.gitignore", "foo bar\n"); + + assert_is_ignored(false, "foo"); + assert_is_ignored(false, "bar"); + assert_is_ignored(true, "foo bar"); +} + +void test_ignore_path__trailing_space(void) +{ + cl_git_rewritefile( + "attr/.gitignore", + "foo \n" + "bar \n" + ); + + assert_is_ignored(true, "foo"); + assert_is_ignored(false, "foo "); + assert_is_ignored(true, "bar"); + assert_is_ignored(false, "bar "); + assert_is_ignored(false, "bar "); +} + +void test_ignore_path__escaped_trailing_spaces(void) +{ + cl_git_rewritefile( + "attr/.gitignore", + "foo\\ \n" + "bar\\ \\ \n" + "baz \\ \n" + "qux\\ \n" + ); + + assert_is_ignored(false, "foo"); + assert_is_ignored(true, "foo "); + assert_is_ignored(false, "bar"); + assert_is_ignored(false, "bar "); + assert_is_ignored(true, "bar "); + assert_is_ignored(true, "baz "); + assert_is_ignored(false, "baz "); + assert_is_ignored(true, "qux "); + assert_is_ignored(false, "qux"); + assert_is_ignored(false, "qux "); +} + +void test_ignore_path__ignore_dir(void) +{ + cl_git_rewritefile("attr/.gitignore", "dir/\n"); + + assert_is_ignored(true, "dir"); + assert_is_ignored(true, "dir/file"); +} + +void test_ignore_path__ignore_dir_with_trailing_space(void) +{ + cl_git_rewritefile("attr/.gitignore", "dir/ \n"); + + assert_is_ignored(true, "dir"); + assert_is_ignored(true, "dir/file"); +} + +void test_ignore_path__ignore_root(void) +{ + cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder\n/NewFolder/NewFolder"); + + assert_is_ignored(false, "File.txt"); + assert_is_ignored(true, "NewFolder"); + assert_is_ignored(true, "NewFolder/NewFolder"); + assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); +} + +void test_ignore_path__full_paths(void) +{ + cl_git_rewritefile("attr/.gitignore", "Folder/*/Contained"); + + assert_is_ignored(true, "Folder/Middle/Contained"); + assert_is_ignored(false, "Folder/Middle/More/More/Contained"); + + cl_git_rewritefile("attr/.gitignore", "Folder/**/Contained"); + + assert_is_ignored(true, "Folder/Middle/Contained"); + assert_is_ignored(true, "Folder/Middle/More/More/Contained"); + + cl_git_rewritefile("attr/.gitignore", "Folder/**/Contained/*/Child"); + + assert_is_ignored(true, "Folder/Middle/Contained/Happy/Child"); + assert_is_ignored(false, "Folder/Middle/Contained/Not/Happy/Child"); + assert_is_ignored(true, "Folder/Middle/More/More/Contained/Happy/Child"); + assert_is_ignored(false, "Folder/Middle/More/More/Contained/Not/Happy/Child"); +} + +void test_ignore_path__more_starstar_cases(void) +{ + cl_must_pass(p_unlink("attr/.gitignore")); + cl_git_mkfile( + "attr/dir/.gitignore", + "sub/**/*.html\n"); + + assert_is_ignored(false, "aaa.html"); + assert_is_ignored(false, "dir"); + assert_is_ignored(false, "dir/sub"); + assert_is_ignored(true, "dir/sub/sub2/aaa.html"); + assert_is_ignored(true, "dir/sub/aaa.html"); + assert_is_ignored(false, "dir/aaa.html"); + assert_is_ignored(false, "sub"); + assert_is_ignored(false, "sub/aaa.html"); + assert_is_ignored(false, "sub/sub2/aaa.html"); +} + +void test_ignore_path__leading_stars(void) +{ + cl_git_rewritefile( + "attr/.gitignore", + "*/onestar\n" + "**/twostars\n" + "*/parent1/kid1/*\n" + "**/parent2/kid2/*\n"); + + assert_is_ignored(true, "dir1/onestar"); + assert_is_ignored(true, "dir1/onestar/child"); /* in ignored dir */ + assert_is_ignored(false, "dir1/dir2/onestar"); + + assert_is_ignored(true, "dir1/twostars"); + assert_is_ignored(true, "dir1/twostars/child"); /* in ignored dir */ + assert_is_ignored(true, "dir1/dir2/twostars"); + assert_is_ignored(true, "dir1/dir2/twostars/child"); /* in ignored dir */ + assert_is_ignored(true, "dir1/dir2/dir3/twostars"); + + assert_is_ignored(true, "dir1/parent1/kid1/file"); + assert_is_ignored(true, "dir1/parent1/kid1/file/inside/parent"); + assert_is_ignored(false, "dir1/dir2/parent1/kid1/file"); + assert_is_ignored(false, "dir1/parent1/file"); + assert_is_ignored(false, "dir1/kid1/file"); + + assert_is_ignored(true, "dir1/parent2/kid2/file"); + assert_is_ignored(true, "dir1/parent2/kid2/file/inside/parent"); + assert_is_ignored(true, "dir1/dir2/parent2/kid2/file"); + assert_is_ignored(true, "dir1/dir2/dir3/parent2/kid2/file"); + assert_is_ignored(false, "dir1/parent2/file"); + assert_is_ignored(false, "dir1/kid2/file"); +} + +void test_ignore_path__globs_and_path_delimiters(void) +{ + cl_git_rewritefile("attr/.gitignore", "foo/bar/**"); + assert_is_ignored(true, "foo/bar/baz"); + assert_is_ignored(true, "foo/bar/baz/quux"); + + cl_git_rewritefile("attr/.gitignore", "_*/"); + assert_is_ignored(true, "sub/_test/a/file"); + assert_is_ignored(false, "test_folder/file"); + assert_is_ignored(true, "_test/file"); + assert_is_ignored(true, "_test/a/file"); + + cl_git_rewritefile("attr/.gitignore", "**/_*/"); + assert_is_ignored(true, "sub/_test/a/file"); + assert_is_ignored(false, "test_folder/file"); + assert_is_ignored(true, "_test/file"); + assert_is_ignored(true, "_test/a/file"); + + cl_git_rewritefile("attr/.gitignore", "**/_*/foo/bar/*ux"); + + assert_is_ignored(true, "sub/_test/foo/bar/qux/file"); + assert_is_ignored(true, "_test/foo/bar/qux/file"); + assert_is_ignored(true, "_test/foo/bar/crux/file"); + assert_is_ignored(false, "_test/foo/bar/code/file"); +} + +void test_ignore_path__skip_gitignore_directory(void) +{ + cl_git_rewritefile("attr/.git/info/exclude", "/NewFolder\n/NewFolder/NewFolder"); + p_unlink("attr/.gitignore"); + cl_assert(!git_path_exists("attr/.gitignore")); + p_mkdir("attr/.gitignore", 0777); + cl_git_mkfile("attr/.gitignore/garbage.txt", "new_file\n"); + + assert_is_ignored(false, "File.txt"); + assert_is_ignored(true, "NewFolder"); + assert_is_ignored(true, "NewFolder/NewFolder"); + assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); +} + +void test_ignore_path__subdirectory_gitignore(void) +{ + p_unlink("attr/.gitignore"); + cl_assert(!git_path_exists("attr/.gitignore")); + cl_git_mkfile( + "attr/.gitignore", + "file1\n"); + p_mkdir("attr/dir", 0777); + cl_git_mkfile( + "attr/dir/.gitignore", + "file2/\n"); + + assert_is_ignored(true, "file1"); + assert_is_ignored(true, "dir/file1"); + assert_is_ignored(true, "dir/file2/actual_file"); /* in ignored dir */ + assert_is_ignored(false, "dir/file3"); +} + +void test_ignore_path__expand_tilde_to_homedir(void) +{ + git_config *cfg; + + assert_is_ignored(false, "example.global_with_tilde"); + + cl_fake_home(); + + /* construct fake home with fake global excludes */ + cl_git_mkfile("home/globalexclude", "# found me\n*.global_with_tilde\n"); + + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexclude")); + git_config_free(cfg); + + git_attr_cache_flush(g_repo); /* must reset to pick up change */ + + assert_is_ignored(true, "example.global_with_tilde"); + + cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES)); + + cl_fake_home_cleanup(NULL); + + git_attr_cache_flush(g_repo); /* must reset to pick up change */ + + assert_is_ignored(false, "example.global_with_tilde"); +} + +/* Ensure that the .gitignore in the subdirectory only affects + * items in the subdirectory. */ +void test_ignore_path__gitignore_in_subdir(void) +{ + cl_git_rmfile("attr/.gitignore"); + + cl_must_pass(p_mkdir("attr/dir1", 0777)); + cl_must_pass(p_mkdir("attr/dir1/dir2", 0777)); + cl_must_pass(p_mkdir("attr/dir1/dir2/dir3", 0777)); + + cl_git_mkfile("attr/dir1/dir2/dir3/.gitignore", "dir1/\ndir1/subdir/"); + + assert_is_ignored(false, "dir1/file"); + assert_is_ignored(false, "dir1/dir2/file"); + assert_is_ignored(false, "dir1/dir2/dir3/file"); + assert_is_ignored(true, "dir1/dir2/dir3/dir1/file"); + assert_is_ignored(true, "dir1/dir2/dir3/dir1/subdir/foo"); + + if (cl_repo_get_bool(g_repo, "core.ignorecase")) { + cl_git_mkfile("attr/dir1/dir2/dir3/.gitignore", "DiR1/\nDiR1/subdir/\n"); + + assert_is_ignored(false, "dir1/file"); + assert_is_ignored(false, "dir1/dir2/file"); + assert_is_ignored(false, "dir1/dir2/dir3/file"); + assert_is_ignored(true, "dir1/dir2/dir3/dir1/file"); + assert_is_ignored(true, "dir1/dir2/dir3/dir1/subdir/foo"); + } +} + +/* Ensure that files do not match folder cases */ +void test_ignore_path__dont_ignore_files_for_folder(void) +{ + cl_git_rmfile("attr/.gitignore"); + + cl_git_mkfile("attr/dir/.gitignore", "test/\n"); + + /* Create "test" as a file; ensure it is not ignored. */ + cl_git_mkfile("attr/dir/test", "This is a file."); + + assert_is_ignored(false, "dir/test"); + if (cl_repo_get_bool(g_repo, "core.ignorecase")) + assert_is_ignored(false, "dir/TeSt"); + + /* Create "test" as a directory; ensure it is ignored. */ + cl_git_rmfile("attr/dir/test"); + cl_must_pass(p_mkdir("attr/dir/test", 0777)); + + assert_is_ignored(true, "dir/test"); + if (cl_repo_get_bool(g_repo, "core.ignorecase")) + assert_is_ignored(true, "dir/TeSt"); + + /* Remove "test" entirely; ensure it is not ignored. + * (As it doesn't exist, it is not a directory.) + */ + cl_must_pass(p_rmdir("attr/dir/test")); + + assert_is_ignored(false, "dir/test"); + if (cl_repo_get_bool(g_repo, "core.ignorecase")) + assert_is_ignored(false, "dir/TeSt"); +} + +void test_ignore_path__symlink_to_outside(void) +{ +#ifdef GIT_WIN32 + cl_skip(); +#endif + + cl_git_rewritefile("attr/.gitignore", "symlink\n"); + cl_git_mkfile("target", "target"); + cl_git_pass(p_symlink("../target", "attr/symlink")); + assert_is_ignored(true, "symlink"); + assert_is_ignored(true, "lala/../symlink"); +} + +void test_ignore_path__test(void) +{ + cl_git_rewritefile("attr/.gitignore", + "/*/\n" + "!/src\n"); + assert_is_ignored(false, "src/foo.c"); + assert_is_ignored(false, "src/foo/foo.c"); + assert_is_ignored(false, "README.md"); + assert_is_ignored(true, "dist/foo.o"); + assert_is_ignored(true, "bin/foo"); +} + +void test_ignore_path__unignore_dir_succeeds(void) +{ + cl_git_rewritefile("attr/.gitignore", + "*.c\n" + "!src/*.c\n"); + assert_is_ignored(false, "src/foo.c"); + assert_is_ignored(true, "src/foo/foo.c"); +} + +void test_ignore_path__case_insensitive_unignores_previous_rule(void) +{ + git_config *cfg; + + cl_git_rewritefile("attr/.gitignore", + "/case\n" + "!/Case/\n"); + + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_bool(cfg, "core.ignorecase", true)); + + cl_must_pass(p_mkdir("attr/case", 0755)); + cl_git_mkfile("attr/case/file", "content"); + + assert_is_ignored(false, "case/file"); +} + +void test_ignore_path__case_sensitive_unignore_does_nothing(void) +{ + git_config *cfg; + + cl_git_rewritefile("attr/.gitignore", + "/case\n" + "!/Case/\n"); + + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_bool(cfg, "core.ignorecase", false)); + + cl_must_pass(p_mkdir("attr/case", 0755)); + cl_git_mkfile("attr/case/file", "content"); + + assert_is_ignored(true, "case/file"); +} + +void test_ignore_path__ignored_subdirfiles_with_subdir_rule(void) +{ + cl_git_rewritefile( + "attr/.gitignore", + "dir/*\n" + "!dir/sub1/sub2/**\n"); + + assert_is_ignored(true, "dir/a.test"); + assert_is_ignored(true, "dir/sub1/a.test"); + assert_is_ignored(true, "dir/sub1/sub2"); +} + +void test_ignore_path__ignored_subdirfiles_with_negations(void) +{ + cl_git_rewritefile( + "attr/.gitignore", + "dir/*\n" + "!dir/a.test\n"); + + assert_is_ignored(false, "dir/a.test"); + assert_is_ignored(true, "dir/b.test"); + assert_is_ignored(true, "dir/sub1/c.test"); +} + +void test_ignore_path__negative_directory_rules_only_match_directories(void) +{ + cl_git_rewritefile( + "attr/.gitignore", + "*\n" + "!/**/\n" + "!*.keep\n" + "!.gitignore\n" + ); + + assert_is_ignored(true, "src"); + assert_is_ignored(true, "src/A"); + assert_is_ignored(false, "src/"); + assert_is_ignored(false, "src/A.keep"); + assert_is_ignored(false, ".gitignore"); +} + +void test_ignore_path__escaped_character(void) +{ + cl_git_rewritefile("attr/.gitignore", "\\c\n"); + assert_is_ignored(true, "c"); + assert_is_ignored(false, "\\c"); +} + +void test_ignore_path__escaped_newline(void) +{ + cl_git_rewritefile( + "attr/.gitignore", + "\\\nnewline\n" + ); + + assert_is_ignored(true, "\nnewline"); +} + +void test_ignore_path__escaped_glob(void) +{ + cl_git_rewritefile("attr/.gitignore", "\\*\n"); + assert_is_ignored(true, "*"); + assert_is_ignored(false, "foo"); +} + +void test_ignore_path__escaped_comments(void) +{ + cl_git_rewritefile( + "attr/.gitignore", + "#foo\n" + "\\#bar\n" + "\\##baz\n" + "\\#\\\\#qux\n" + ); + + assert_is_ignored(false, "#foo"); + assert_is_ignored(true, "#bar"); + assert_is_ignored(false, "\\#bar"); + assert_is_ignored(true, "##baz"); + assert_is_ignored(false, "\\##baz"); + assert_is_ignored(true, "#\\#qux"); + assert_is_ignored(false, "##qux"); + assert_is_ignored(false, "\\##qux"); +} + +void test_ignore_path__escaped_slash(void) +{ + cl_git_rewritefile( + "attr/.gitignore", + "\\\\\n" + "\\\\preceding\n" + "inter\\\\mittent\n" + "trailing\\\\\n" + ); + +#ifndef GIT_WIN32 + assert_is_ignored(true, "\\"); + assert_is_ignored(true, "\\preceding"); +#endif + assert_is_ignored(true, "inter\\mittent"); + assert_is_ignored(true, "trailing\\"); +} + +void test_ignore_path__escaped_space(void) +{ + cl_git_rewritefile( + "attr/.gitignore", + "foo\\\\ \n" + "bar\\\\\\ \n"); + assert_is_ignored(true, "foo\\"); + assert_is_ignored(false, "foo\\ "); + assert_is_ignored(false, "foo\\\\ "); + assert_is_ignored(false, "foo\\\\"); + assert_is_ignored(true, "bar\\ "); + assert_is_ignored(false, "bar\\\\"); + assert_is_ignored(false, "bar\\\\ "); + assert_is_ignored(false, "bar\\\\\\"); + assert_is_ignored(false, "bar\\\\\\ "); +} |