]> git.proxmox.com Git - libgit2.git/blob - tests/attr/repo.c
Merge pull request #2561 from jacquesg/merge-skip
[libgit2.git] / tests / attr / repo.c
1 #include "clar_libgit2.h"
2 #include "fileops.h"
3 #include "git2/attr.h"
4 #include "attr.h"
5
6 #include "attr_expect.h"
7 #include "git2/sys/repository.h"
8
9 static git_repository *g_repo = NULL;
10
11 void test_attr_repo__initialize(void)
12 {
13 g_repo = cl_git_sandbox_init("attr");
14 }
15
16 void test_attr_repo__cleanup(void)
17 {
18 cl_git_sandbox_cleanup();
19 g_repo = NULL;
20 }
21
22 static struct attr_expected get_one_test_cases[] = {
23 { "root_test1", "repoattr", EXPECT_TRUE, NULL },
24 { "root_test1", "rootattr", EXPECT_TRUE, NULL },
25 { "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
26 { "root_test1", "subattr", EXPECT_UNDEFINED, NULL },
27 { "root_test1", "negattr", EXPECT_UNDEFINED, NULL },
28 { "root_test2", "repoattr", EXPECT_TRUE, NULL },
29 { "root_test2", "rootattr", EXPECT_FALSE, NULL },
30 { "root_test2", "missingattr", EXPECT_UNDEFINED, NULL },
31 { "root_test2", "multiattr", EXPECT_FALSE, NULL },
32 { "root_test3", "repoattr", EXPECT_TRUE, NULL },
33 { "root_test3", "rootattr", EXPECT_UNDEFINED, NULL },
34 { "root_test3", "multiattr", EXPECT_STRING, "3" },
35 { "root_test3", "multi2", EXPECT_UNDEFINED, NULL },
36 { "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL },
37 { "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL },
38 { "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL },
39 { "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" },
40 { "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL },
41 { "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL },
42 { "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL },
43 { "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL },
44 { "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL },
45 { "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" },
46 { "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL },
47 { "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" },
48 { "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL },
49 { "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" },
50 { "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL },
51 { "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL },
52 { "does-not-exist", "foo", EXPECT_STRING, "yes" },
53 { "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
54 { "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" },
55 { "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL },
56 };
57
58 void test_attr_repo__get_one(void)
59 {
60 int i;
61
62 for (i = 0; i < (int)ARRAY_SIZE(get_one_test_cases); ++i) {
63 struct attr_expected *scan = &get_one_test_cases[i];
64 const char *value;
65
66 cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
67 attr_check_expected(
68 scan->expected, scan->expected_str, scan->attr, value);
69 }
70
71 cl_assert(git_attr_cache__is_cached(
72 g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
73 cl_assert(git_attr_cache__is_cached(
74 g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
75 cl_assert(git_attr_cache__is_cached(
76 g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
77 }
78
79 void test_attr_repo__get_one_start_deep(void)
80 {
81 int i;
82
83 for (i = (int)ARRAY_SIZE(get_one_test_cases) - 1; i >= 0; --i) {
84 struct attr_expected *scan = &get_one_test_cases[i];
85 const char *value;
86
87 cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
88 attr_check_expected(
89 scan->expected, scan->expected_str, scan->attr, value);
90 }
91
92 cl_assert(git_attr_cache__is_cached(
93 g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes"));
94 cl_assert(git_attr_cache__is_cached(
95 g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes"));
96 cl_assert(git_attr_cache__is_cached(
97 g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes"));
98 }
99
100 void test_attr_repo__get_many(void)
101 {
102 const char *names[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
103 const char *values[4];
104
105 cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test1", 4, names));
106
107 cl_assert(GIT_ATTR_TRUE(values[0]));
108 cl_assert(GIT_ATTR_TRUE(values[1]));
109 cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
110 cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
111
112 cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test2", 4, names));
113
114 cl_assert(GIT_ATTR_TRUE(values[0]));
115 cl_assert(GIT_ATTR_FALSE(values[1]));
116 cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
117 cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
118
119 cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/subdir_test1", 4, names));
120
121 cl_assert(GIT_ATTR_TRUE(values[0]));
122 cl_assert(GIT_ATTR_TRUE(values[1]));
123 cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
124 cl_assert_equal_s("yes", values[3]);
125 }
126
127 void test_attr_repo__get_many_in_place(void)
128 {
129 const char *vals[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
130
131 /* it should be legal to look up values into the same array that has
132 * the attribute names, overwriting each name as the value is found.
133 */
134
135 cl_git_pass(git_attr_get_many(vals, g_repo, 0, "sub/subdir_test1", 4, vals));
136
137 cl_assert(GIT_ATTR_TRUE(vals[0]));
138 cl_assert(GIT_ATTR_TRUE(vals[1]));
139 cl_assert(GIT_ATTR_UNSPECIFIED(vals[2]));
140 cl_assert_equal_s("yes", vals[3]);
141 }
142
143 static int count_attrs(
144 const char *name,
145 const char *value,
146 void *payload)
147 {
148 GIT_UNUSED(name);
149 GIT_UNUSED(value);
150
151 *((int *)payload) += 1;
152
153 return 0;
154 }
155
156 #define CANCEL_VALUE 12345
157
158 static int cancel_iteration(
159 const char *name,
160 const char *value,
161 void *payload)
162 {
163 GIT_UNUSED(name);
164 GIT_UNUSED(value);
165
166 *((int *)payload) -= 1;
167
168 if (*((int *)payload) < 0)
169 return CANCEL_VALUE;
170
171 return 0;
172 }
173
174 void test_attr_repo__foreach(void)
175 {
176 int count;
177
178 count = 0;
179 cl_git_pass(git_attr_foreach(
180 g_repo, 0, "root_test1", &count_attrs, &count));
181 cl_assert(count == 2);
182
183 count = 0;
184 cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test1",
185 &count_attrs, &count));
186 cl_assert(count == 4); /* repoattr, rootattr, subattr, negattr */
187
188 count = 0;
189 cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test2.txt",
190 &count_attrs, &count));
191 cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */
192
193 count = 2;
194 cl_assert_equal_i(
195 CANCEL_VALUE, git_attr_foreach(
196 g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count)
197 );
198 }
199
200 void test_attr_repo__manpage_example(void)
201 {
202 const char *value;
203
204 cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "foo"));
205 cl_assert(GIT_ATTR_TRUE(value));
206
207 cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "bar"));
208 cl_assert(GIT_ATTR_UNSPECIFIED(value));
209
210 cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "baz"));
211 cl_assert(GIT_ATTR_FALSE(value));
212
213 cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "merge"));
214 cl_assert_equal_s("filfre", value);
215
216 cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "frotz"));
217 cl_assert(GIT_ATTR_UNSPECIFIED(value));
218 }
219
220 void test_attr_repo__macros(void)
221 {
222 const char *names[5] = { "rootattr", "binary", "diff", "crlf", "frotz" };
223 const char *names2[5] = { "mymacro", "positive", "negative", "rootattr", "another" };
224 const char *names3[3] = { "macro2", "multi2", "multi3" };
225 const char *values[5];
226
227 cl_git_pass(git_attr_get_many(values, g_repo, 0, "binfile", 5, names));
228
229 cl_assert(GIT_ATTR_TRUE(values[0]));
230 cl_assert(GIT_ATTR_TRUE(values[1]));
231 cl_assert(GIT_ATTR_FALSE(values[2]));
232 cl_assert(GIT_ATTR_FALSE(values[3]));
233 cl_assert(GIT_ATTR_UNSPECIFIED(values[4]));
234
235 cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 5, names2));
236
237 cl_assert(GIT_ATTR_TRUE(values[0]));
238 cl_assert(GIT_ATTR_TRUE(values[1]));
239 cl_assert(GIT_ATTR_FALSE(values[2]));
240 cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
241 cl_assert_equal_s("77", values[4]);
242
243 cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 3, names3));
244
245 cl_assert(GIT_ATTR_TRUE(values[0]));
246 cl_assert(GIT_ATTR_FALSE(values[1]));
247 cl_assert_equal_s("answer", values[2]);
248 }
249
250 void test_attr_repo__bad_macros(void)
251 {
252 const char *names[6] = { "rootattr", "positive", "negative",
253 "firstmacro", "secondmacro", "thirdmacro" };
254 const char *values[6];
255
256 cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_bad", 6, names));
257
258 /* these three just confirm that the "mymacro" rule ran */
259 cl_assert(GIT_ATTR_UNSPECIFIED(values[0]));
260 cl_assert(GIT_ATTR_TRUE(values[1]));
261 cl_assert(GIT_ATTR_FALSE(values[2]));
262
263 /* file contains:
264 * # let's try some malicious macro defs
265 * [attr]firstmacro -thirdmacro -secondmacro
266 * [attr]secondmacro firstmacro -firstmacro
267 * [attr]thirdmacro secondmacro=hahaha -firstmacro
268 * macro_bad firstmacro secondmacro thirdmacro
269 *
270 * firstmacro assignment list ends up with:
271 * -thirdmacro -secondmacro
272 * secondmacro assignment list expands "firstmacro" and ends up with:
273 * -thirdmacro -secondmacro -firstmacro
274 * thirdmacro assignment don't expand so list ends up with:
275 * secondmacro="hahaha"
276 *
277 * macro_bad assignment list ends up with:
278 * -thirdmacro -secondmacro firstmacro &&
279 * -thirdmacro -secondmacro -firstmacro secondmacro &&
280 * secondmacro="hahaha" thirdmacro
281 *
282 * so summary results should be:
283 * -firstmacro secondmacro="hahaha" thirdmacro
284 */
285 cl_assert(GIT_ATTR_FALSE(values[3]));
286 cl_assert_equal_s("hahaha", values[4]);
287 cl_assert(GIT_ATTR_TRUE(values[5]));
288 }
289
290 #define CONTENT "I'm going to be dynamically processed\r\n" \
291 "And my line endings...\r\n" \
292 "...are going to be\n" \
293 "normalized!\r\n"
294
295 #define GITATTR "* text=auto\n" \
296 "*.txt text\n" \
297 "*.data binary\n"
298
299 static void add_to_workdir(const char *filename, const char *content)
300 {
301 git_buf buf = GIT_BUF_INIT;
302
303 cl_git_pass(git_buf_joinpath(&buf, "attr", filename));
304 cl_git_rewritefile(git_buf_cstr(&buf), content);
305
306 git_buf_free(&buf);
307 }
308
309 static void assert_proper_normalization(git_index *index, const char *filename, const char *expected_sha)
310 {
311 size_t index_pos;
312 const git_index_entry *entry;
313
314 add_to_workdir(filename, CONTENT);
315 cl_git_pass(git_index_add_bypath(index, filename));
316
317 cl_assert(!git_index_find(&index_pos, index, filename));
318
319 entry = git_index_get_byindex(index, index_pos);
320 cl_assert_equal_i(0, git_oid_streq(&entry->id, expected_sha));
321 }
322
323 void test_attr_repo__staging_properly_normalizes_line_endings_according_to_gitattributes_directives(void)
324 {
325 git_index* index;
326
327 cl_git_pass(git_repository_index(&index, g_repo));
328
329 add_to_workdir(".gitattributes", GITATTR);
330
331 assert_proper_normalization(index, "text.txt", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
332 assert_proper_normalization(index, "huh.dunno", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
333 assert_proper_normalization(index, "binary.data", "66eeff1fcbacf589e6d70aa70edd3fce5be2b37c");
334
335 git_index_free(index);
336 }
337
338 void test_attr_repo__bare_repo_with_index(void)
339 {
340 const char *names[4] = { "test1", "test2", "test3", "test4" };
341 const char *values[4];
342 git_index *index;
343
344 cl_git_pass(git_repository_index(&index, g_repo));
345
346 cl_git_mkfile(
347 "attr/.gitattributes",
348 "*.txt test1 test2=foobar -test3\n"
349 "trial.txt -test1 test2=barfoo !test3 test4\n");
350 cl_git_pass(git_index_add_bypath(index, ".gitattributes"));
351 git_index_free(index);
352
353 cl_must_pass(p_unlink("attr/.gitattributes"));
354 cl_assert(!git_path_exists("attr/.gitattributes"));
355
356 cl_git_pass(git_repository_set_bare(g_repo));
357
358 cl_git_pass(git_attr_get_many(values, g_repo, 0, "file.txt", 4, names));
359
360 cl_assert(GIT_ATTR_TRUE(values[0]));
361 cl_assert_equal_s("foobar", values[1]);
362 cl_assert(GIT_ATTR_FALSE(values[2]));
363 cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
364
365 cl_git_pass(git_attr_get_many(values, g_repo, 0, "trial.txt", 4, names));
366
367 cl_assert(GIT_ATTR_FALSE(values[0]));
368 cl_assert_equal_s("barfoo", values[1]);
369 cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
370 cl_assert(GIT_ATTR_TRUE(values[3]));
371 }