summaryrefslogtreecommitdiff
path: root/tests/attr
diff options
context:
space:
mode:
authorBen Straub <bs@github.com>2013-11-14 14:05:52 -0800
committerBen Straub <bs@github.com>2013-11-14 14:05:52 -0800
commit1782038144ef3413831801bb9c2f3038a84ac6f4 (patch)
treef074cc30890a20f5418c10fae1815ca516588a27 /tests/attr
parent7b947bf5cc59eefa83c28eb5f5fd8434207ebb8b (diff)
downloadlibgit2-1782038144ef3413831801bb9c2f3038a84ac6f4.tar.gz
Rename tests-clar to tests
Diffstat (limited to 'tests/attr')
-rw-r--r--tests/attr/attr_expect.h43
-rw-r--r--tests/attr/file.c224
-rw-r--r--tests/attr/flags.c108
-rw-r--r--tests/attr/ignore.c102
-rw-r--r--tests/attr/lookup.c262
-rw-r--r--tests/attr/repo.c310
6 files changed, 1049 insertions, 0 deletions
diff --git a/tests/attr/attr_expect.h b/tests/attr/attr_expect.h
new file mode 100644
index 000000000..70f1ab4f5
--- /dev/null
+++ b/tests/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_TRUE(value), name);
+ break;
+
+ case EXPECT_FALSE:
+ cl_assert_(GIT_ATTR_FALSE(value), name);
+ break;
+
+ case EXPECT_UNDEFINED:
+ cl_assert_(GIT_ATTR_UNSPECIFIED(value), name);
+ break;
+
+ case EXPECT_STRING:
+ cl_assert_equal_s(expected_str, value);
+ break;
+ }
+}
+
+#endif
diff --git a/tests/attr/file.c b/tests/attr/file.c
new file mode 100644
index 000000000..4eb1d22fe
--- /dev/null
+++ b/tests/attr/file.c
@@ -0,0 +1,224 @@
+#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__new_and_load(&file, cl_fixture("attr/attr0")));
+
+ cl_assert_equal_s(cl_fixture("attr/attr0"), file->key + 2);
+ 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_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__new_and_load(&file, cl_fixture("attr/attr1")));
+
+ cl_assert_equal_s(cl_fixture("attr/attr1"), file->key + 2);
+ 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_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_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__new_and_load(&file, cl_fixture("attr/attr2")));
+
+ cl_assert_equal_s(cl_fixture("attr/attr2"), file->key + 2);
+ 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_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_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_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);
+}
+
+void test_attr_file__check_attr_examples(void)
+{
+ git_attr_file *file;
+ git_attr_rule *rule;
+ git_attr_assignment *assign;
+
+ cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr3")));
+ cl_assert_equal_s(cl_fixture("attr/attr3"), file->key + 2);
+ cl_assert(file->rules.length == 3);
+
+ 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_FALSE(assign->value));
+ assign = git_attr_rule__lookup_assignment(rule, "myAttr");
+ cl_assert_equal_s("myAttr", assign->name);
+ cl_assert(GIT_ATTR_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_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);
+
+ git_attr_file__free(file);
+}
diff --git a/tests/attr/flags.c b/tests/attr/flags.c
new file mode 100644
index 000000000..80c6e1171
--- /dev/null
+++ b/tests/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_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_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_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_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_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_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_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/attr/ignore.c b/tests/attr/ignore.c
new file mode 100644
index 000000000..0f945ebf6
--- /dev/null
+++ b/tests/attr/ignore.c
@@ -0,0 +1,102 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "fileops.h"
+
+static git_repository *g_repo = NULL;
+
+void test_attr_ignore__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("attr");
+}
+
+void test_attr_ignore__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+void assert_is_ignored(bool expected, const char *filepath)
+{
+ int is_ignored;
+
+ cl_git_pass(git_ignore_path_is_ignored(&is_ignored, g_repo, filepath));
+ cl_assert_equal_b(expected, is_ignored);
+}
+
+void test_attr_ignore__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_attr_ignore__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_attr_ignore__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_attr_ignore__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_attr_ignore__expand_tilde_to_homedir(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_config *cfg;
+
+ assert_is_ignored(false, "example.global_with_tilde");
+
+ /* construct fake home with fake global excludes */
+
+ cl_must_pass(p_mkdir("home", 0777));
+ cl_git_pass(git_path_prettify(&path, "home", NULL));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+
+ cl_git_mkfile("home/globalexcludes", "# 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", "~/globalexcludes"));
+ 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_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL));
+
+ git_buf_free(&path);
+}
diff --git a/tests/attr/lookup.c b/tests/attr/lookup.c
new file mode 100644
index 000000000..200bdd2c7
--- /dev/null
+++ b/tests/attr/lookup.c
@@ -0,0 +1,262 @@
+#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__new_and_load(&file, cl_fixture("attr/attr0")));
+ cl_assert_equal_s(cl_fixture("attr/attr0"), file->key + 2);
+ cl_assert(file->rules.length == 1);
+
+ cl_git_pass(git_attr_path__init(&path, "test", NULL));
+ 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_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));
+
+ 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__new_and_load(&file, cl_fixture("attr/attr1")));
+ cl_assert_equal_s(cl_fixture("attr/attr1"), file->key + 2);
+ cl_assert(file->rules.length == 10);
+
+ cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL));
+ 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__new_and_load(&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__new_and_load(&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;
+
+ 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, 0, NULL, NULL));
+
+ cl_git_pass(git_attr_file__parse_buffer(NULL, NULL, "a* foo\nabc bar\n* baz", file));
+
+ cl_assert(file->rules.length == 3);
+
+ run_test_cases(file, cases, 0);
+
+ git_attr_file__free(file);
+}
diff --git a/tests/attr/repo.c b/tests/attr/repo.c
new file mode 100644
index 000000000..ef2ad5ce9
--- /dev/null
+++ b/tests/attr/repo.c
@@ -0,0 +1,310 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "git2/attr.h"
+#include "attr.h"
+
+#include "attr_expect.h"
+
+static git_repository *g_repo = NULL;
+
+void test_attr_repo__initialize(void)
+{
+ /* Before each test, instantiate the attr repo from the fixtures and
+ * rename the .gitted to .git so it is a repo with a working dir.
+ * Also rename gitattributes to .gitattributes, because it contains
+ * macro definitions which are only allowed in the root.
+ */
+ g_repo = cl_git_sandbox_init("attr");
+}
+
+void test_attr_repo__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+void test_attr_repo__get_one(void)
+{
+ struct attr_expected 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 },
+ { NULL, NULL, 0, NULL }
+ }, *scan;
+
+ for (scan = test_cases; scan->path != NULL; scan++) {
+ 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, 0, ".git/info/attributes"));
+ cl_assert(git_attr_cache__is_cached(g_repo, 0, ".gitattributes"));
+ cl_assert(git_attr_cache__is_cached(g_repo, 0, "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_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test2", 4, names));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_FALSE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/subdir_test1", 4, names));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_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_TRUE(vals[0]));
+ cl_assert(GIT_ATTR_TRUE(vals[1]));
+ cl_assert(GIT_ATTR_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;
+}
+
+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 -1;
+
+ 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(
+ GIT_EUSER, 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_TRUE(value));
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "bar"));
+ cl_assert(GIT_ATTR_UNSPECIFIED(value));
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "baz"));
+ cl_assert(GIT_ATTR_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_UNSPECIFIED(value));
+}
+
+void test_attr_repo__macros(void)
+{
+ const char *names[5] = { "rootattr", "binary", "diff", "crlf", "frotz" };
+ const char *names2[5] = { "mymacro", "positive", "negative", "rootattr", "another" };
+ const char *names3[3] = { "macro2", "multi2", "multi3" };
+ const char *values[5];
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "binfile", 5, names));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_FALSE(values[2]));
+ cl_assert(GIT_ATTR_FALSE(values[3]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[4]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 5, names2));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_FALSE(values[2]));
+ cl_assert(GIT_ATTR_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_TRUE(values[0]));
+ cl_assert(GIT_ATTR_FALSE(values[1]));
+ cl_assert_equal_s("answer", values[2]);
+}
+
+void test_attr_repo__bad_macros(void)
+{
+ const char *names[6] = { "rootattr", "positive", "negative",
+ "firstmacro", "secondmacro", "thirdmacro" };
+ const char *values[6];
+
+ 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_UNSPECIFIED(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_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_FALSE(values[3]));
+ cl_assert_equal_s("hahaha", values[4]);
+ cl_assert(GIT_ATTR_TRUE(values[5]));
+}
+
+#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_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&buf, "attr", filename));
+ cl_git_rewritefile(git_buf_cstr(&buf), content);
+
+ git_buf_free(&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->oid, 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);
+}