diff options
| author | Edward Thomson <ethomson@edwardthomson.com> | 2021-11-16 23:29:22 -0500 |
|---|---|---|
| committer | Edward Thomson <ethomson@edwardthomson.com> | 2022-02-22 22:07:45 -0500 |
| commit | 3344fddc97bbdea9c1b6ebb6f7fb6dbd70b41dfb (patch) | |
| tree | fd6368a72944571c51627b40c592e7d58e0036e1 /tests/libgit2/attr | |
| parent | 91ba089663f5efc3bd4ba14a5099372cf5ce57a6 (diff) | |
| download | libgit2-3344fddc97bbdea9c1b6ebb6f7fb6dbd70b41dfb.tar.gz | |
refactor: `tests` is now `tests/libgit2`
Like we want to separate libgit2 and utility source code, we want to
separate libgit2 and utility tests. Start by moving all the tests into
libgit2.
Diffstat (limited to 'tests/libgit2/attr')
| -rw-r--r-- | tests/libgit2/attr/attr_expect.h | 43 | ||||
| -rw-r--r-- | tests/libgit2/attr/file.c | 243 | ||||
| -rw-r--r-- | tests/libgit2/attr/flags.c | 108 | ||||
| -rw-r--r-- | tests/libgit2/attr/lookup.c | 263 | ||||
| -rw-r--r-- | tests/libgit2/attr/macro.c | 197 | ||||
| -rw-r--r-- | tests/libgit2/attr/repo.c | 405 |
6 files changed, 1259 insertions, 0 deletions
diff --git a/tests/libgit2/attr/attr_expect.h b/tests/libgit2/attr/attr_expect.h new file mode 100644 index 000000000..faf7a1181 --- /dev/null +++ b/tests/libgit2/attr/attr_expect.h @@ -0,0 +1,43 @@ +#ifndef __CLAR_TEST_ATTR_EXPECT__ +#define __CLAR_TEST_ATTR_EXPECT__ + +enum attr_expect_t { + EXPECT_FALSE, + EXPECT_TRUE, + EXPECT_UNDEFINED, + EXPECT_STRING +}; + +struct attr_expected { + const char *path; + const char *attr; + enum attr_expect_t expected; + const char *expected_str; +}; + +GIT_INLINE(void) attr_check_expected( + enum attr_expect_t expected, + const char *expected_str, + const char *name, + const char *value) +{ + switch (expected) { + case EXPECT_TRUE: + cl_assert_(GIT_ATTR_IS_TRUE(value), name); + break; + + case EXPECT_FALSE: + cl_assert_(GIT_ATTR_IS_FALSE(value), name); + break; + + case EXPECT_UNDEFINED: + cl_assert_(GIT_ATTR_IS_UNSPECIFIED(value), name); + break; + + case EXPECT_STRING: + cl_assert_equal_s(expected_str, value); + break; + } +} + +#endif diff --git a/tests/libgit2/attr/file.c b/tests/libgit2/attr/file.c new file mode 100644 index 000000000..1ac48c0fe --- /dev/null +++ b/tests/libgit2/attr/file.c @@ -0,0 +1,243 @@ +#include "clar_libgit2.h" +#include "attr_file.h" +#include "attr_expect.h" + +#define get_rule(X) ((git_attr_rule *)git_vector_get(&file->rules,(X))) +#define get_assign(R,Y) ((git_attr_assignment *)git_vector_get(&(R)->assigns,(Y))) + +void test_attr_file__simple_read(void) +{ + git_attr_file *file; + git_attr_assignment *assign; + git_attr_rule *rule; + + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0"))); + + cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path); + cl_assert(file->rules.length == 1); + + rule = get_rule(0); + cl_assert(rule != NULL); + cl_assert_equal_s("*", rule->match.pattern); + cl_assert(rule->match.length == 1); + cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0); + + cl_assert(rule->assigns.length == 1); + assign = get_assign(rule, 0); + cl_assert(assign != NULL); + cl_assert_equal_s("binary", assign->name); + cl_assert(GIT_ATTR_IS_TRUE(assign->value)); + + git_attr_file__free(file); +} + +void test_attr_file__match_variants(void) +{ + git_attr_file *file; + git_attr_rule *rule; + git_attr_assignment *assign; + + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1"))); + + cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path); + cl_assert(file->rules.length == 10); + + /* let's do a thorough check of this rule, then just verify + * the things that are unique for the later rules + */ + rule = get_rule(0); + cl_assert(rule); + cl_assert_equal_s("pat0", rule->match.pattern); + cl_assert(rule->match.length == strlen("pat0")); + cl_assert(rule->assigns.length == 1); + assign = get_assign(rule,0); + cl_assert_equal_s("attr0", assign->name); + cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name)); + cl_assert(GIT_ATTR_IS_TRUE(assign->value)); + + rule = get_rule(1); + cl_assert_equal_s("pat1", rule->match.pattern); + cl_assert(rule->match.length == strlen("pat1")); + cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0); + + rule = get_rule(2); + cl_assert_equal_s("pat2", rule->match.pattern); + cl_assert(rule->match.length == strlen("pat2")); + cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_DIRECTORY) != 0); + + rule = get_rule(3); + cl_assert_equal_s("pat3dir/pat3file", rule->match.pattern); + cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_FULLPATH) != 0); + + rule = get_rule(4); + cl_assert_equal_s("pat4.*", rule->match.pattern); + cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0); + + rule = get_rule(5); + cl_assert_equal_s("*.pat5", rule->match.pattern); + cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0); + + rule = get_rule(7); + cl_assert_equal_s("pat7[a-e]??[xyz]", rule->match.pattern); + cl_assert(rule->assigns.length == 1); + cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0); + assign = get_assign(rule,0); + cl_assert_equal_s("attr7", assign->name); + cl_assert(GIT_ATTR_IS_TRUE(assign->value)); + + rule = get_rule(8); + cl_assert_equal_s("pat8 with spaces", rule->match.pattern); + cl_assert(rule->match.length == strlen("pat8 with spaces")); + + rule = get_rule(9); + cl_assert_equal_s("pat9", rule->match.pattern); + + git_attr_file__free(file); +} + +static void check_one_assign( + git_attr_file *file, + int rule_idx, + int assign_idx, + const char *pattern, + const char *name, + enum attr_expect_t expected, + const char *expected_str) +{ + git_attr_rule *rule = get_rule(rule_idx); + git_attr_assignment *assign = get_assign(rule, assign_idx); + + cl_assert_equal_s(pattern, rule->match.pattern); + cl_assert(rule->assigns.length == 1); + cl_assert_equal_s(name, assign->name); + cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name)); + + attr_check_expected(expected, expected_str, assign->name, assign->value); +} + +void test_attr_file__assign_variants(void) +{ + git_attr_file *file; + git_attr_rule *rule; + git_attr_assignment *assign; + + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr2"))); + + cl_assert_equal_s(cl_fixture("attr/attr2"), file->entry->path); + cl_assert(file->rules.length == 11); + + check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL); + check_one_assign(file, 1, 0, "pat1", "neg", EXPECT_FALSE, NULL); + check_one_assign(file, 2, 0, "*", "notundef", EXPECT_TRUE, NULL); + check_one_assign(file, 3, 0, "pat2", "notundef", EXPECT_UNDEFINED, NULL); + check_one_assign(file, 4, 0, "pat3", "assigned", EXPECT_STRING, "test-value"); + check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars"); + check_one_assign(file, 6, 0, "pat5", "empty", EXPECT_TRUE, NULL); + check_one_assign(file, 7, 0, "pat6", "negempty", EXPECT_FALSE, NULL); + + rule = get_rule(8); + cl_assert_equal_s("pat7", rule->match.pattern); + cl_assert(rule->assigns.length == 5); + /* assignments will be sorted by hash value, so we have to do + * lookups by search instead of by position + */ + assign = git_attr_rule__lookup_assignment(rule, "multiple"); + cl_assert(assign); + cl_assert_equal_s("multiple", assign->name); + cl_assert(GIT_ATTR_IS_TRUE(assign->value)); + assign = git_attr_rule__lookup_assignment(rule, "single"); + cl_assert(assign); + cl_assert_equal_s("single", assign->name); + cl_assert(GIT_ATTR_IS_FALSE(assign->value)); + assign = git_attr_rule__lookup_assignment(rule, "values"); + cl_assert(assign); + cl_assert_equal_s("values", assign->name); + cl_assert_equal_s("1", assign->value); + assign = git_attr_rule__lookup_assignment(rule, "also"); + cl_assert(assign); + cl_assert_equal_s("also", assign->name); + cl_assert_equal_s("a-really-long-value/*", assign->value); + assign = git_attr_rule__lookup_assignment(rule, "happy"); + cl_assert(assign); + cl_assert_equal_s("happy", assign->name); + cl_assert_equal_s("yes!", assign->value); + assign = git_attr_rule__lookup_assignment(rule, "other"); + cl_assert(!assign); + + rule = get_rule(9); + cl_assert_equal_s("pat8", rule->match.pattern); + cl_assert(rule->assigns.length == 2); + assign = git_attr_rule__lookup_assignment(rule, "again"); + cl_assert(assign); + cl_assert_equal_s("again", assign->name); + cl_assert(GIT_ATTR_IS_TRUE(assign->value)); + assign = git_attr_rule__lookup_assignment(rule, "another"); + cl_assert(assign); + cl_assert_equal_s("another", assign->name); + cl_assert_equal_s("12321", assign->value); + + check_one_assign(file, 10, 0, "pat9", "at-eof", EXPECT_FALSE, NULL); + + git_attr_file__free(file); +} + +static void assert_examples(git_attr_file *file) +{ + git_attr_rule *rule; + git_attr_assignment *assign; + + rule = get_rule(0); + cl_assert_equal_s("*.java", rule->match.pattern); + cl_assert(rule->assigns.length == 3); + assign = git_attr_rule__lookup_assignment(rule, "diff"); + cl_assert_equal_s("diff", assign->name); + cl_assert_equal_s("java", assign->value); + assign = git_attr_rule__lookup_assignment(rule, "crlf"); + cl_assert_equal_s("crlf", assign->name); + cl_assert(GIT_ATTR_IS_FALSE(assign->value)); + assign = git_attr_rule__lookup_assignment(rule, "myAttr"); + cl_assert_equal_s("myAttr", assign->name); + cl_assert(GIT_ATTR_IS_TRUE(assign->value)); + assign = git_attr_rule__lookup_assignment(rule, "missing"); + cl_assert(assign == NULL); + + rule = get_rule(1); + cl_assert_equal_s("NoMyAttr.java", rule->match.pattern); + cl_assert(rule->assigns.length == 1); + assign = get_assign(rule, 0); + cl_assert_equal_s("myAttr", assign->name); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(assign->value)); + + rule = get_rule(2); + cl_assert_equal_s("README", rule->match.pattern); + cl_assert(rule->assigns.length == 1); + assign = get_assign(rule, 0); + cl_assert_equal_s("caveat", assign->name); + cl_assert_equal_s("unspecified", assign->value); +} + +void test_attr_file__check_attr_examples(void) +{ + git_attr_file *file; + + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3"))); + cl_assert_equal_s(cl_fixture("attr/attr3"), file->entry->path); + cl_assert(file->rules.length == 3); + + assert_examples(file); + + git_attr_file__free(file); +} + +void test_attr_file__whitespace(void) +{ + git_attr_file *file; + + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr4"))); + cl_assert_equal_s(cl_fixture("attr/attr4"), file->entry->path); + cl_assert(file->rules.length == 3); + + assert_examples(file); + + git_attr_file__free(file); +} diff --git a/tests/libgit2/attr/flags.c b/tests/libgit2/attr/flags.c new file mode 100644 index 000000000..6470ec732 --- /dev/null +++ b/tests/libgit2/attr/flags.c @@ -0,0 +1,108 @@ +#include "clar_libgit2.h" +#include "git2/attr.h" + +void test_attr_flags__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_attr_flags__bare(void) +{ + git_repository *repo = cl_git_sandbox_init("testrepo.git"); + const char *value; + + cl_assert(git_repository_is_bare(repo)); + + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM, "README.md", "diff")); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(value)); +} + +void test_attr_flags__index_vs_workdir(void) +{ + git_repository *repo = cl_git_sandbox_init("attr_index"); + const char *value; + + cl_assert(!git_repository_is_bare(repo)); + + /* wd then index */ + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX, + "README.md", "bar")); + cl_assert(GIT_ATTR_IS_FALSE(value)); + + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX, + "README.md", "blargh")); + cl_assert_equal_s(value, "goop"); + + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX, + "README.txt", "foo")); + cl_assert(GIT_ATTR_IS_FALSE(value)); + + /* index then wd */ + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE, + "README.md", "bar")); + cl_assert(GIT_ATTR_IS_TRUE(value)); + + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE, + "README.md", "blargh")); + cl_assert_equal_s(value, "garble"); + + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE, + "README.txt", "foo")); + cl_assert(GIT_ATTR_IS_TRUE(value)); +} + +void test_attr_flags__subdir(void) +{ + git_repository *repo = cl_git_sandbox_init("attr_index"); + const char *value; + + /* wd then index */ + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX, + "sub/sub/README.md", "bar")); + cl_assert_equal_s(value, "1234"); + + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX, + "sub/sub/README.txt", "another")); + cl_assert_equal_s(value, "one"); + + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX, + "sub/sub/README.txt", "again")); + cl_assert(GIT_ATTR_IS_TRUE(value)); + + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX, + "sub/sub/README.txt", "beep")); + cl_assert_equal_s(value, "10"); + + /* index then wd */ + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE, + "sub/sub/README.md", "bar")); + cl_assert_equal_s(value, "1337"); + + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE, + "sub/sub/README.txt", "another")); + cl_assert_equal_s(value, "one"); + + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE, + "sub/sub/README.txt", "again")); + cl_assert(GIT_ATTR_IS_TRUE(value)); + + cl_git_pass(git_attr_get( + &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE, + "sub/sub/README.txt", "beep")); + cl_assert_equal_s(value, "5"); +} + diff --git a/tests/libgit2/attr/lookup.c b/tests/libgit2/attr/lookup.c new file mode 100644 index 000000000..f19f38fbb --- /dev/null +++ b/tests/libgit2/attr/lookup.c @@ -0,0 +1,263 @@ +#include "clar_libgit2.h" +#include "attr_file.h" + +#include "attr_expect.h" + +void test_attr_lookup__simple(void) +{ + git_attr_file *file; + git_attr_path path; + const char *value = NULL; + + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0"))); + cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path); + cl_assert(file->rules.length == 1); + + cl_git_pass(git_attr_path__init(&path, "test", NULL, GIT_DIR_FLAG_UNKNOWN)); + cl_assert_equal_s("test", path.path); + cl_assert_equal_s("test", path.basename); + cl_assert(!path.is_dir); + + cl_git_pass(git_attr_file__lookup_one(file,&path,"binary",&value)); + cl_assert(GIT_ATTR_IS_TRUE(value)); + + cl_git_pass(git_attr_file__lookup_one(file,&path,"missing",&value)); + cl_assert(!value); + + git_attr_path__free(&path); + git_attr_file__free(file); +} + +static void run_test_cases(git_attr_file *file, struct attr_expected *cases, int force_dir) +{ + git_attr_path path; + const char *value = NULL; + struct attr_expected *c; + int error; + + for (c = cases; c->path != NULL; c++) { + cl_git_pass(git_attr_path__init(&path, c->path, NULL, GIT_DIR_FLAG_UNKNOWN)); + + if (force_dir) + path.is_dir = 1; + + error = git_attr_file__lookup_one(file,&path,c->attr,&value); + cl_git_pass(error); + + attr_check_expected(c->expected, c->expected_str, c->attr, value); + + git_attr_path__free(&path); + } +} + +void test_attr_lookup__match_variants(void) +{ + git_attr_file *file; + git_attr_path path; + + struct attr_expected dir_cases[] = { + { "pat2", "attr2", EXPECT_TRUE, NULL }, + { "/testing/for/pat2", "attr2", EXPECT_TRUE, NULL }, + { "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL }, + { "/fun/fun/fun/pat4.dir", "attr4", EXPECT_TRUE, NULL }, + { "foo.pat5", "attr5", EXPECT_TRUE, NULL }, + { NULL, NULL, 0, NULL } + }; + + struct attr_expected cases[] = { + /* pat0 -> simple match */ + { "pat0", "attr0", EXPECT_TRUE, NULL }, + { "/testing/for/pat0", "attr0", EXPECT_TRUE, NULL }, + { "relative/to/pat0", "attr0", EXPECT_TRUE, NULL }, + { "this-contains-pat0-inside", "attr0", EXPECT_UNDEFINED, NULL }, + { "this-aint-right", "attr0", EXPECT_UNDEFINED, NULL }, + { "/this/pat0/dont/match", "attr0", EXPECT_UNDEFINED, NULL }, + /* negative match */ + { "pat0", "attr1", EXPECT_TRUE, NULL }, + { "pat1", "attr1", EXPECT_UNDEFINED, NULL }, + { "/testing/for/pat1", "attr1", EXPECT_UNDEFINED, NULL }, + { "/testing/for/pat0", "attr1", EXPECT_TRUE, NULL }, + { "/testing/for/pat1/inside", "attr1", EXPECT_TRUE, NULL }, + { "misc", "attr1", EXPECT_TRUE, NULL }, + /* dir match */ + { "pat2", "attr2", EXPECT_UNDEFINED, NULL }, + { "/testing/for/pat2", "attr2", EXPECT_UNDEFINED, NULL }, + { "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL }, + /* path match */ + { "pat3file", "attr3", EXPECT_UNDEFINED, NULL }, + { "/pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL }, + { "pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL }, + /* pattern* match */ + { "pat4.txt", "attr4", EXPECT_TRUE, NULL }, + { "/fun/fun/fun/pat4.c", "attr4", EXPECT_TRUE, NULL }, + { "pat4.", "attr4", EXPECT_TRUE, NULL }, + { "pat4", "attr4", EXPECT_UNDEFINED, NULL }, + /* *pattern match */ + { "foo.pat5", "attr5", EXPECT_TRUE, NULL }, + { "/this/is/ok.pat5", "attr5", EXPECT_TRUE, NULL }, + { "/this/is/bad.pat5/yousee.txt", "attr5", EXPECT_UNDEFINED, NULL }, + { "foo.pat5", "attr100", EXPECT_UNDEFINED, NULL }, + /* glob match with slashes */ + { "foo.pat6", "attr6", EXPECT_UNDEFINED, NULL }, + { "pat6/pat6/foobar.pat6", "attr6", EXPECT_TRUE, NULL }, + { "pat6/pat6/.pat6", "attr6", EXPECT_TRUE, NULL }, + { "pat6/pat6/extra/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL }, + { "/prefix/pat6/pat6/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL }, + { "/pat6/pat6/foobar.pat6", "attr6", EXPECT_TRUE, NULL }, + /* complex pattern */ + { "pat7a12z", "attr7", EXPECT_TRUE, NULL }, + { "pat7e__x", "attr7", EXPECT_TRUE, NULL }, + { "pat7b/1y", "attr7", EXPECT_UNDEFINED, NULL }, /* ? does not match / */ + { "pat7e_x", "attr7", EXPECT_UNDEFINED, NULL }, + { "pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL }, + { "pat7zzzz", "attr7", EXPECT_UNDEFINED, NULL }, + { "/this/can/be/anything/pat7a12z", "attr7", EXPECT_TRUE, NULL }, + { "but/it/still/must/match/pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL }, + { "pat7aaay.fail", "attr7", EXPECT_UNDEFINED, NULL }, + /* pattern with spaces */ + { "pat8 with spaces", "attr8", EXPECT_TRUE, NULL }, + { "/gotta love/pat8 with spaces", "attr8", EXPECT_TRUE, NULL }, + { "failing pat8 with spaces", "attr8", EXPECT_UNDEFINED, NULL }, + { "spaces", "attr8", EXPECT_UNDEFINED, NULL }, + /* pattern at eof */ + { "pat9", "attr9", EXPECT_TRUE, NULL }, + { "/eof/pat9", "attr9", EXPECT_TRUE, NULL }, + { "pat", "attr9", EXPECT_UNDEFINED, NULL }, + { "at9", "attr9", EXPECT_UNDEFINED, NULL }, + { "pat9.fail", "attr9", EXPECT_UNDEFINED, NULL }, + /* sentinel at end */ + { NULL, NULL, 0, NULL } + }; + + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1"))); + cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path); + cl_assert(file->rules.length == 10); + + cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL, GIT_DIR_FLAG_UNKNOWN)); + cl_assert_equal_s("pat0", path.basename); + + run_test_cases(file, cases, 0); + run_test_cases(file, dir_cases, 1); + + git_attr_file__free(file); + git_attr_path__free(&path); +} + +void test_attr_lookup__assign_variants(void) +{ + git_attr_file *file; + + struct attr_expected cases[] = { + /* pat0 -> simple assign */ + { "pat0", "simple", EXPECT_TRUE, NULL }, + { "/testing/pat0", "simple", EXPECT_TRUE, NULL }, + { "pat0", "fail", EXPECT_UNDEFINED, NULL }, + { "/testing/pat0", "fail", EXPECT_UNDEFINED, NULL }, + /* negative assign */ + { "pat1", "neg", EXPECT_FALSE, NULL }, + { "/testing/pat1", "neg", EXPECT_FALSE, NULL }, + { "pat1", "fail", EXPECT_UNDEFINED, NULL }, + { "/testing/pat1", "fail", EXPECT_UNDEFINED, NULL }, + /* forced undef */ + { "pat1", "notundef", EXPECT_TRUE, NULL }, + { "pat2", "notundef", EXPECT_UNDEFINED, NULL }, + { "/lead/in/pat1", "notundef", EXPECT_TRUE, NULL }, + { "/lead/in/pat2", "notundef", EXPECT_UNDEFINED, NULL }, + /* assign value */ + { "pat3", "assigned", EXPECT_STRING, "test-value" }, + { "pat3", "notassigned", EXPECT_UNDEFINED, NULL }, + /* assign value */ + { "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars" }, + { "pat4", "notassigned-rule-with-more-chars", EXPECT_UNDEFINED, NULL }, + /* empty assignments */ + { "pat5", "empty", EXPECT_TRUE, NULL }, + { "pat6", "negempty", EXPECT_FALSE, NULL }, + /* multiple assignment */ + { "pat7", "multiple", EXPECT_TRUE, NULL }, + { "pat7", "single", EXPECT_FALSE, NULL }, + { "pat7", "values", EXPECT_STRING, "1" }, + { "pat7", "also", EXPECT_STRING, "a-really-long-value/*" }, + { "pat7", "happy", EXPECT_STRING, "yes!" }, + { "pat8", "again", EXPECT_TRUE, NULL }, + { "pat8", "another", EXPECT_STRING, "12321" }, + /* bad assignment */ + { "patbad0", "simple", EXPECT_UNDEFINED, NULL }, + { "patbad0", "notundef", EXPECT_TRUE, NULL }, + { "patbad1", "simple", EXPECT_UNDEFINED, NULL }, + /* eof assignment */ + { "pat9", "at-eof", EXPECT_FALSE, NULL }, + /* sentinel at end */ + { NULL, NULL, 0, NULL } + }; + + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr2"))); + cl_assert(file->rules.length == 11); + + run_test_cases(file, cases, 0); + + git_attr_file__free(file); +} + +void test_attr_lookup__check_attr_examples(void) +{ + git_attr_file *file; + + struct attr_expected cases[] = { + { "foo.java", "diff", EXPECT_STRING, "java" }, + { "foo.java", "crlf", EXPECT_FALSE, NULL }, + { "foo.java", "myAttr", EXPECT_TRUE, NULL }, + { "foo.java", "other", EXPECT_UNDEFINED, NULL }, + { "/prefix/dir/foo.java", "diff", EXPECT_STRING, "java" }, + { "/prefix/dir/foo.java", "crlf", EXPECT_FALSE, NULL }, + { "/prefix/dir/foo.java", "myAttr", EXPECT_TRUE, NULL }, + { "/prefix/dir/foo.java", "other", EXPECT_UNDEFINED, NULL }, + { "NoMyAttr.java", "crlf", EXPECT_FALSE, NULL }, + { "NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL }, + { "NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL }, + { "/prefix/dir/NoMyAttr.java", "crlf", EXPECT_FALSE, NULL }, + { "/prefix/dir/NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL }, + { "/prefix/dir/NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL }, + { "README", "caveat", EXPECT_STRING, "unspecified" }, + { "/specific/path/README", "caveat", EXPECT_STRING, "unspecified" }, + { "README", "missing", EXPECT_UNDEFINED, NULL }, + { "/specific/path/README", "missing", EXPECT_UNDEFINED, NULL }, + /* sentinel at end */ + { NULL, NULL, 0, NULL } + }; + + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3"))); + cl_assert(file->rules.length == 3); + + run_test_cases(file, cases, 0); + + git_attr_file__free(file); +} + +void test_attr_lookup__from_buffer(void) +{ + git_attr_file *file; + git_attr_file_source source = {0}; + + struct attr_expected cases[] = { + { "abc", "foo", EXPECT_TRUE, NULL }, + { "abc", "bar", EXPECT_TRUE, NULL }, + { "abc", "baz", EXPECT_TRUE, NULL }, + { "aaa", "foo", EXPECT_TRUE, NULL }, + { "aaa", "bar", EXPECT_UNDEFINED, NULL }, + { "aaa", "baz", EXPECT_TRUE, NULL }, + { "qqq", "foo", EXPECT_UNDEFINED, NULL }, + { "qqq", "bar", EXPECT_UNDEFINED, NULL }, + { "qqq", "baz", EXPECT_TRUE, NULL }, + { NULL, NULL, 0, NULL } + }; + + cl_git_pass(git_attr_file__new(&file, NULL, &source)); + + cl_git_pass(git_attr_file__parse_buffer(NULL, file, "a* foo\nabc bar\n* baz", true)); + + cl_assert(file->rules.length == 3); + + run_test_cases(file, cases, 0); + + git_attr_file__free(file); +} diff --git a/tests/libgit2/attr/macro.c b/tests/libgit2/attr/macro.c new file mode 100644 index 000000000..1fbfd137f --- /dev/null +++ b/tests/libgit2/attr/macro.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "clar_libgit2.h" + +#include "git2/sys/repository.h" +#include "attr.h" + +static git_repository *g_repo = NULL; + +void test_attr_macro__cleanup(void) +{ + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +void test_attr_macro__macros(void) +{ + const char *names[7] = { "rootattr", "binary", "diff", "crlf", "merge", "text", "frotz" }; + const char *names2[5] = { "mymacro", "positive", "negative", "rootattr", "another" }; + const char *names3[3] = { "macro2", "multi2", "multi3" }; + const char *values[7]; + + g_repo = cl_git_sandbox_init("attr"); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "binfile", 7, names)); + + cl_assert(GIT_ATTR_IS_TRUE(values[0])); + cl_assert(GIT_ATTR_IS_TRUE(values[1])); + cl_assert(GIT_ATTR_IS_FALSE(values[2])); + cl_assert(GIT_ATTR_IS_FALSE(values[3])); + cl_assert(GIT_ATTR_IS_FALSE(values[4])); + cl_assert(GIT_ATTR_IS_FALSE(values[5])); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[6])); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 5, names2)); + + cl_assert(GIT_ATTR_IS_TRUE(values[0])); + cl_assert(GIT_ATTR_IS_TRUE(values[1])); + cl_assert(GIT_ATTR_IS_FALSE(values[2])); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3])); + cl_assert_equal_s("77", values[4]); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 3, names3)); + + cl_assert(GIT_ATTR_IS_TRUE(values[0])); + cl_assert(GIT_ATTR_IS_FALSE(values[1])); + cl_assert_equal_s("answer", values[2]); +} + +void test_attr_macro__bad_macros(void) +{ + const char *names[6] = { "rootattr", "positive", "negative", + "firstmacro", "secondmacro", "thirdmacro" }; + const char *values[6]; + + g_repo = cl_git_sandbox_init("attr"); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_bad", 6, names)); + + /* these three just confirm that the "mymacro" rule ran */ + cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[0])); + cl_assert(GIT_ATTR_IS_TRUE(values[1])); + cl_assert(GIT_ATTR_IS_FALSE(values[2])); + + /* file contains: + * # let's try some malicious macro defs + * [attr]firstmacro -thirdmacro -secondmacro + * [attr]secondmacro firstmacro -firstmacro + * [attr]thirdmacro secondmacro=hahaha -firstmacro + * macro_bad firstmacro secondmacro thirdmacro + * + * firstmacro assignment list ends up with: + * -thirdmacro -secondmacro + * secondmacro assignment list expands "firstmacro" and ends up with: + * -thirdmacro -secondmacro -firstmacro + * thirdmacro assignment don't expand so list ends up with: + * secondmacro="hahaha" + * + * macro_bad assignment list ends up with: + * -thirdmacro -secondmacro firstmacro && + * -thirdmacro -secondmacro -firstmacro secondmacro && + * secondmacro="hahaha" thirdmacro + * + * so summary results should be: + * -firstmacro secondmacro="hahaha" thirdmacro + */ + cl_assert(GIT_ATTR_IS_FALSE(values[3])); + cl_assert_equal_s("hahaha", values[4]); + cl_assert(GIT_ATTR_IS_TRUE(values[5])); +} + +void test_attr_macro__macros_in_root_wd_apply(void) +{ + const char *value; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_pass(p_mkdir("empty_standard_repo/dir", 0777)); + cl_git_rewritefile("empty_standard_repo/.gitattributes", "[attr]customattr key=value\n"); + cl_git_rewritefile("empty_standard_repo/dir/.gitattributes", "file customattr\n"); + + cl_git_pass(git_attr_get(&value, g_repo, 0, "dir/file", "key")); + cl_assert_equal_s(value, "value"); +} + +void test_attr_macro__changing_macro_in_root_wd_updates_attributes(void) +{ + const char *value; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_rewritefile("empty_standard_repo/.gitattributes", + "[attr]customattr key=first\n" + "file customattr\n"); + cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "key")); + cl_assert_equal_s(value, "first"); + + cl_git_rewritefile("empty_standard_repo/.gitattributes", + "[attr]customattr key=second\n" + "file customattr\n"); + cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "key")); + cl_assert_equal_s(value, "second"); +} + +void test_attr_macro__macros_in_subdir_do_not_apply(void) +{ + const char *value; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_pass(p_mkdir("empty_standard_repo/dir", 0777)); + cl_git_rewritefile("empty_standard_repo/dir/.gitattributes", + "[attr]customattr key=value\n" + "file customattr\n"); + + /* This should _not_ pass, as macros in subdirectories shall be ignored */ + cl_git_pass(git_attr_get(&value, g_repo, 0, "dir/file", "key")); + cl_assert_equal_p(value, NULL); +} + +void test_attr_macro__adding_macro_succeeds(void) +{ + const char *value; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_pass(git_attr_add_macro(g_repo, "macro", "key=value")); + cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro\n"); + + cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "key")); + cl_assert_equal_s(value, "value"); +} + +void test_attr_macro__adding_boolean_macros_succeeds(void) +{ + const char *value; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_pass(git_attr_add_macro(g_repo, "macro-pos", "positive")); + cl_git_pass(git_attr_add_macro(g_repo, "macro-neg", "-negative")); + cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro-pos macro-neg\n"); + + cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "positive")); + cl_assert(GIT_ATTR_IS_TRUE(value)); + cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "negative")); + cl_assert(GIT_ATTR_IS_FALSE(value)); +} + +void test_attr_macro__redefining_macro_succeeds(void) +{ + const char *value; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_pass(git_attr_add_macro(g_repo, "macro", "key=value1")); + cl_git_pass(git_attr_add_macro(g_repo, "macro", "key=value2")); + cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro"); + + cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "key")); + cl_assert_equal_s(value, "value2"); +} + +void test_attr_macro__recursive_macro_resolves(void) +{ + const char *value; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_pass(git_attr_add_macro(g_repo, "expandme", "key=value")); + cl_git_pass(git_attr_add_macro(g_repo, "macro", "expandme")); + cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro"); + + cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "key")); + cl_assert_equal_s(value, "value"); +} diff --git a/tests/libgit2/attr/repo.c b/tests/libgit2/attr/repo.c new file mode 100644 index 000000000..abd238154 --- /dev/null +++ b/tests/libgit2/attr/repo.c @@ -0,0 +1,405 @@ +#include "clar_libgit2.h" +#include "futils.h" +#include "git2/attr.h" +#include "attr.h" +#include "sysdir.h" + +#include "attr_expect.h" +#include "git2/sys/repository.h" + +static git_repository *g_repo = NULL; + +void test_attr_repo__initialize(void) +{ + g_repo = cl_git_sandbox_init("attr"); +} + +void test_attr_repo__cleanup(void) +{ + cl_git_sandbox_cleanup(); + g_repo = NULL; + cl_sandbox_set_search_path_defaults(); +} + +static struct attr_expected get_one_test_cases[] = { + { "root_test1", "repoattr", EXPECT_TRUE, NULL }, + { "root_test1", "rootattr", EXPECT_TRUE, NULL }, + { "root_test1", "missingattr", EXPECT_UNDEFINED, NULL }, + { "root_test1", "subattr", EXPECT_UNDEFINED, NULL }, + { "root_test1", "negattr", EXPECT_UNDEFINED, NULL }, + { "root_test2", "repoattr", EXPECT_TRUE, NULL }, + { "root_test2", "rootattr", EXPECT_FALSE, NULL }, + { "root_test2", "missingattr", EXPECT_UNDEFINED, NULL }, + { "root_test2", "multiattr", EXPECT_FALSE, NULL }, + { "root_test3", "repoattr", EXPECT_TRUE, NULL }, + { "root_test3", "rootattr", EXPECT_UNDEFINED, NULL }, + { "root_test3", "multiattr", EXPECT_STRING, "3" }, + { "root_test3", "multi2", EXPECT_UNDEFINED, NULL }, + { "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL }, + { "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL }, + { "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL }, + { "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" }, + { "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL }, + { "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL }, + { "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL }, + { "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL }, + { "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL }, + { "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" }, + { "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL }, + { "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" }, + { "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL }, + { "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" }, + { "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL }, + { "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL }, + { "does-not-exist", "foo", EXPECT_STRING, "yes" }, + { "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL }, + { "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" }, + { "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL }, +}; + +void test_attr_repo__get_one(void) +{ + int i; + + for (i = 0; i < (int)ARRAY_SIZE(get_one_test_cases); ++i) { + struct attr_expected *scan = &get_one_test_cases[i]; + const char *value; + + cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr)); + attr_check_expected( + scan->expected, scan->expected_str, scan->attr, value); + } + + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/attributes")); + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitattributes")); + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE_SOURCE_FILE, "sub/.gitattributes")); +} + +void test_attr_repo__get_one_start_deep(void) +{ + int i; + + for (i = (int)ARRAY_SIZE(get_one_test_cases) - 1; i >= 0; --i) { + struct attr_expected *scan = &get_one_test_cases[i]; + const char *value; + + cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr)); + attr_check_expected( + scan->expected, scan->expected_str, scan->attr, value); + } + + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/attributes")); + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitattributes")); + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE_SOURCE_FILE, "sub/.gitattributes")); +} + +void test_attr_repo__get_many(void) +{ + const char *names[4] = { "repoattr", "rootattr", "missingattr", "subattr" }; + const char *values[4]; + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test1", 4, names)); + + cl_assert(GIT_ATTR_IS_TRUE(values[0])); + cl_assert(GIT_ATTR_IS_TRUE(values[1])); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2])); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3])); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test2", 4, names)); + + cl_assert(GIT_ATTR_IS_TRUE(values[0])); + cl_assert(GIT_ATTR_IS_FALSE(values[1])); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2])); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3])); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/subdir_test1", 4, names)); + + cl_assert(GIT_ATTR_IS_TRUE(values[0])); + cl_assert(GIT_ATTR_IS_TRUE(values[1])); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2])); + cl_assert_equal_s("yes", values[3]); +} + +void test_attr_repo__get_many_in_place(void) +{ + const char *vals[4] = { "repoattr", "rootattr", "missingattr", "subattr" }; + + /* it should be legal to look up values into the same array that has + * the attribute names, overwriting each name as the value is found. + */ + + cl_git_pass(git_attr_get_many(vals, g_repo, 0, "sub/subdir_test1", 4, vals)); + + cl_assert(GIT_ATTR_IS_TRUE(vals[0])); + cl_assert(GIT_ATTR_IS_TRUE(vals[1])); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(vals[2])); + cl_assert_equal_s("yes", vals[3]); +} + +static int count_attrs( + const char *name, + const char *value, + void *payload) +{ + GIT_UNUSED(name); + GIT_UNUSED(value); + + *((int *)payload) += 1; + + return 0; +} + +#define CANCEL_VALUE 12345 + +static int cancel_iteration( + const char *name, + const char *value, + void *payload) +{ + GIT_UNUSED(name); + GIT_UNUSED(value); + + *((int *)payload) -= 1; + + if (*((int *)payload) < 0) + return CANCEL_VALUE; + + return 0; +} + +void test_attr_repo__foreach(void) +{ + int count; + + count = 0; + cl_git_pass(git_attr_foreach( + g_repo, 0, "root_test1", &count_attrs, &count)); + cl_assert(count == 2); + + count = 0; + cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test1", + &count_attrs, &count)); + cl_assert(count == 4); /* repoattr, rootattr, subattr, negattr */ + + count = 0; + cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test2.txt", + &count_attrs, &count)); + cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */ + + count = 2; + cl_assert_equal_i( + CANCEL_VALUE, git_attr_foreach( + g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count) + ); +} + +void test_attr_repo__manpage_example(void) +{ + const char *value; + + cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "foo")); + cl_assert(GIT_ATTR_IS_TRUE(value)); + + cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "bar")); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(value)); + + cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "baz")); + cl_assert(GIT_ATTR_IS_FALSE(value)); + + cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "merge")); + cl_assert_equal_s("filfre", value); + + cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "frotz")); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(value)); +} + +#define CONTENT "I'm going to be dynamically processed\r\n" \ + "And my line endings...\r\n" \ + "...are going to be\n" \ + "normalized!\r\n" + +#define GITATTR "* text=auto\n" \ + "*.txt text\n" \ + "*.data binary\n" + +static void add_to_workdir(const char *filename, const char *content) +{ + git_str buf = GIT_STR_INIT; + + cl_git_pass(git_str_joinpath(&buf, "attr", filename)); + cl_git_rewritefile(git_str_cstr(&buf), content); + + git_str_dispose(&buf); +} + +static void assert_proper_normalization(git_index *index, const char *filename, const char *expected_sha) +{ + size_t index_pos; + const git_index_entry *entry; + + add_to_workdir(filename, CONTENT); + cl_git_pass(git_index_add_bypath(index, filename)); + + cl_assert(!git_index_find(&index_pos, index, filename)); + + entry = git_index_get_byindex(index, index_pos); + cl_assert_equal_i(0, git_oid_streq(&entry->id, expected_sha)); +} + +void test_attr_repo__staging_properly_normalizes_line_endings_according_to_gitattributes_directives(void) +{ + git_index* index; + + cl_git_pass(git_repository_index(&index, g_repo)); + + add_to_workdir(".gitattributes", GITATTR); + + assert_proper_normalization(index, "text.txt", "22c74203bace3c2e950278c7ab08da0fca9f4e9b"); + assert_proper_normalization(index, "huh.dunno", "22c74203bace3c2e950278c7ab08da0fca9f4e9b"); + assert_proper_normalization(index, "binary.data", "66eeff1fcbacf589e6d70aa70edd3fce5be2b37c"); + + git_index_free(index); +} + +void test_attr_repo__bare_repo_with_index(void) +{ + const char *names[4] = { "test1", "test2", "test3", "test4" }; + const char *values[4]; + git_index *index; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_mkfile( + "attr/.gitattributes", + "*.txt test1 test2=foobar -test3\n" + "trial.txt -test1 test2=barfoo !test3 test4\n"); + cl_git_pass(git_index_add_bypath(index, ".gitattributes")); + git_index_free(index); + + cl_must_pass(p_unlink("attr/.gitattributes")); + cl_assert(!git_fs_path_exists("attr/.gitattributes")); + + cl_git_pass(git_repository_set_bare(g_repo)); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "file.txt", 4, names)); + + cl_assert(GIT_ATTR_IS_TRUE(values[0])); + cl_assert_equal_s("foobar", values[1]); + cl_assert(GIT_ATTR_IS_FALSE(values[2])); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3])); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "trial.txt", 4, names)); + + cl_assert(GIT_ATTR_IS_FALSE(values[0])); + cl_assert_equal_s("barfoo", values[1]); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2])); + cl_assert(GIT_ATTR_IS_TRUE(values[3])); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/sub/subdir.txt", 4, names)); + + cl_assert(GIT_ATTR_IS_TRUE(values[0])); + cl_assert_equal_s("foobar", values[1]); + cl_assert(GIT_ATTR_IS_FALSE(values[2])); + cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3])); +} + +void test_attr_repo__sysdir(void) +{ + git_str sysdir = GIT_STR_INIT; + const char *value; + + cl_git_pass(p_mkdir("system", 0777)); + cl_git_rewritefile("system/gitattributes", "file merge=foo"); + cl_git_pass(git_str_joinpath(&sysdir, clar_sandbox_path(), "system")); + cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, sysdir.ptr)); + g_repo = cl_git_sandbox_reopen(); + + cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "merge")); + cl_assert_equal_s(value, "foo"); + + cl_git_pass(p_unlink("system/gitattributes")); + cl_git_pass(p_rmdir("system")); + git_str_dispose(&sysdir); +} + +void test_attr_repo__sysdir_with_session(void) +{ + const char *values[2], *attrs[2] = { "foo", "bar" }; + git_str sysdir = GIT_STR_INIT; + git_attr_session session; + + cl_git_pass(p_mkdir("system", 0777)); + cl_git_rewritefile("system/gitattributes", "file foo=1 bar=2"); + cl_git_pass(git_str_joinpath(&sysdir, clar_sandbox_path(), "system")); + cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, sysdir.ptr)); + g_repo = cl_git_sandbox_reopen(); + + cl_git_pass(git_attr_session__init(&session, g_repo)); + cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, NULL, "file", ARRAY_SIZE(attrs), attrs)); + + cl_assert_equal_s(values[0], "1"); + cl_assert_equal_s(values[1], "2"); + + cl_git_pass(p_unlink("system/gitattributes")); + cl_git_pass(p_rmdir("system")); + git_str_dispose(&sysdir); + git_attr_session__free(&session); +} + +void test_attr_repo__rewrite(void) +{ + const char *value; + + cl_git_rewritefile("attr/.gitattributes", "file.txt foo=first\n"); + cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo")); + cl_assert_equal_s(value, "first"); + + cl_git_rewritefile("attr/.gitattributes", "file.txt foo=second\n"); + cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo")); + cl_assert_equal_s(value, "second"); + + cl_git_rewritefile("attr/.gitattributes", "file.txt other=value\n"); + cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo")); + cl_assert_equal_p(value, NULL); +} + +void test_attr_repo__rewrite_sysdir(void) +{ + git_str sysdir = GIT_STR_INIT; + const char *value; + + cl_git_pass(p_mkdir("system", 0777)); + cl_git_pass(git_str_joinpath(&sysdir, clar_sandbox_path(), "system")); + cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, sysdir.ptr)); + g_repo = cl_git_sandbox_reopen(); + + cl_git_rewritefile("system/gitattributes", "file foo=first"); + cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "foo")); + cl_assert_equal_s(value, "first"); + + cl_git_rewritefile("system/gitattributes", "file foo=second"); + cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "foo")); + cl_assert_equal_s(value, "second"); + + git_str_dispose(&sysdir); +} + +void test_attr_repo__unlink(void) +{ + const char *value; + + cl_git_rewritefile("attr/.gitattributes", "file.txt foo=value1\n"); + cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo")); + cl_assert_equal_s(value, "value1"); + + cl_git_pass(p_unlink("attr/.gitattributes")); + + cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo")); + cl_assert_equal_p(value, NULL); +} |
