1 #include "clar_libgit2.h"
5 static const char *blob_oid
= "fa49b077972391ad58037050f2a75f74e3671e92";
6 static const char *first_tree
= "181037049a54a1eb5fab404658a3a250b44335d7";
7 static const char *second_tree
= "f60079018b664e4e79329a7ef9559c8d9e0378d1";
8 static const char *third_tree
= "eb86d8b81d6adbd5290a935d6c9976882de98488";
10 static git_repository
*g_repo
;
12 /* Fixture setup and teardown */
13 void test_object_tree_write__initialize(void)
15 g_repo
= cl_git_sandbox_init("testrepo");
18 void test_object_tree_write__cleanup(void)
20 cl_git_sandbox_cleanup();
22 cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION
, 1));
25 void test_object_tree_write__from_memory(void)
27 /* write a tree from a memory */
28 git_treebuilder
*builder
;
30 git_oid id
, bid
, rid
, id2
;
32 git_oid_fromstr(&id
, first_tree
);
33 git_oid_fromstr(&id2
, second_tree
);
34 git_oid_fromstr(&bid
, blob_oid
);
36 /* create a second tree from first tree using `git_treebuilder_insert`
37 * on REPOSITORY_FOLDER.
39 cl_git_pass(git_tree_lookup(&tree
, g_repo
, &id
));
40 cl_git_pass(git_treebuilder_new(&builder
, g_repo
, tree
));
42 cl_git_fail(git_treebuilder_insert(NULL
, builder
, "",
43 &bid
, GIT_FILEMODE_BLOB
));
44 cl_git_fail(git_treebuilder_insert(NULL
, builder
, "/",
45 &bid
, GIT_FILEMODE_BLOB
));
46 cl_git_fail(git_treebuilder_insert(NULL
, builder
, ".git",
47 &bid
, GIT_FILEMODE_BLOB
));
48 cl_git_fail(git_treebuilder_insert(NULL
, builder
, "..",
49 &bid
, GIT_FILEMODE_BLOB
));
50 cl_git_fail(git_treebuilder_insert(NULL
, builder
, ".",
51 &bid
, GIT_FILEMODE_BLOB
));
52 cl_git_fail(git_treebuilder_insert(NULL
, builder
, "folder/new.txt",
53 &bid
, GIT_FILEMODE_BLOB
));
55 cl_git_pass(git_treebuilder_insert(
56 NULL
, builder
, "new.txt", &bid
, GIT_FILEMODE_BLOB
));
58 cl_git_pass(git_treebuilder_write(&rid
, builder
));
60 cl_assert(git_oid_cmp(&rid
, &id2
) == 0);
62 git_treebuilder_free(builder
);
66 void test_object_tree_write__subtree(void)
68 /* write a hierarchical tree from a memory */
69 git_treebuilder
*builder
;
71 git_oid id
, bid
, subtree_id
, id2
, id3
;
74 git_oid_fromstr(&id
, first_tree
);
75 git_oid_fromstr(&id2
, second_tree
);
76 git_oid_fromstr(&id3
, third_tree
);
77 git_oid_fromstr(&bid
, blob_oid
);
80 cl_git_pass(git_treebuilder_new(&builder
, g_repo
, NULL
));
81 cl_git_pass(git_treebuilder_insert(
82 NULL
, builder
, "new.txt", &bid
, GIT_FILEMODE_BLOB
)); /* -V536 */
83 cl_git_pass(git_treebuilder_write(&subtree_id
, builder
));
84 git_treebuilder_free(builder
);
86 /* create parent tree */
87 cl_git_pass(git_tree_lookup(&tree
, g_repo
, &id
));
88 cl_git_pass(git_treebuilder_new(&builder
, g_repo
, tree
));
89 cl_git_pass(git_treebuilder_insert(
90 NULL
, builder
, "new", &subtree_id
, GIT_FILEMODE_TREE
)); /* -V536 */
91 cl_git_pass(git_treebuilder_write(&id_hiearar
, builder
));
92 git_treebuilder_free(builder
);
95 cl_assert(git_oid_cmp(&id_hiearar
, &id3
) == 0);
97 /* check data is correct */
98 cl_git_pass(git_tree_lookup(&tree
, g_repo
, &id_hiearar
));
99 cl_assert(2 == git_tree_entrycount(tree
));
104 * And the Lord said: Is this tree properly sorted?
106 void test_object_tree_write__sorted_subtrees(void)
108 git_treebuilder
*builder
;
111 int position_c
= -1, position_cake
= -1, position_config
= -1;
115 const char *filename
;
117 { GIT_FILEMODE_BLOB
, ".gitattributes" },
118 { GIT_FILEMODE_BLOB
, ".gitignore" },
119 { GIT_FILEMODE_BLOB
, ".htaccess" },
120 { GIT_FILEMODE_BLOB
, "Capfile" },
121 { GIT_FILEMODE_BLOB
, "Makefile"},
122 { GIT_FILEMODE_BLOB
, "README"},
123 { GIT_FILEMODE_TREE
, "app"},
124 { GIT_FILEMODE_TREE
, "cake"},
125 { GIT_FILEMODE_TREE
, "config"},
126 { GIT_FILEMODE_BLOB
, "c"},
127 { GIT_FILEMODE_BLOB
, "git_test.txt"},
128 { GIT_FILEMODE_BLOB
, "htaccess.htaccess"},
129 { GIT_FILEMODE_BLOB
, "index.php"},
130 { GIT_FILEMODE_TREE
, "plugins"},
131 { GIT_FILEMODE_TREE
, "schemas"},
132 { GIT_FILEMODE_TREE
, "ssl-certs"},
133 { GIT_FILEMODE_TREE
, "vendors"}
136 git_oid bid
, tid
, tree_oid
;
138 cl_git_pass(git_oid_fromstr(&bid
, blob_oid
));
139 cl_git_pass(git_oid_fromstr(&tid
, first_tree
));
141 cl_git_pass(git_treebuilder_new(&builder
, g_repo
, NULL
));
143 for (i
= 0; i
< ARRAY_SIZE(entries
); ++i
) {
144 git_oid
*id
= entries
[i
].attr
== GIT_FILEMODE_TREE
? &tid
: &bid
;
146 cl_git_pass(git_treebuilder_insert(NULL
,
147 builder
, entries
[i
].filename
, id
, entries
[i
].attr
));
150 cl_git_pass(git_treebuilder_write(&tree_oid
, builder
));
152 cl_git_pass(git_tree_lookup(&tree
, g_repo
, &tree_oid
));
153 for (i
= 0; i
< git_tree_entrycount(tree
); i
++) {
154 const git_tree_entry
*entry
= git_tree_entry_byindex(tree
, i
);
156 if (strcmp(entry
->filename
, "c") == 0)
159 if (strcmp(entry
->filename
, "cake") == 0)
162 if (strcmp(entry
->filename
, "config") == 0)
168 cl_assert(position_c
!= -1);
169 cl_assert(position_cake
!= -1);
170 cl_assert(position_config
!= -1);
172 cl_assert(position_c
< position_cake
);
173 cl_assert(position_cake
< position_config
);
175 git_treebuilder_free(builder
);
180 const char *filename
;
182 { GIT_FILEMODE_BLOB
, "aardvark" },
183 { GIT_FILEMODE_BLOB
, ".first" },
184 { GIT_FILEMODE_BLOB
, "apple" },
185 { GIT_FILEMODE_BLOB
, "last"},
186 { GIT_FILEMODE_BLOB
, "apple_after"},
187 { GIT_FILEMODE_BLOB
, "after_aardvark"},
191 void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
193 git_treebuilder
*builder
;
194 int i
, aardvark_i
, apple_i
, apple_after_i
, apple_extra_i
, last_i
;
195 git_oid entry_oid
, tree_oid
;
198 cl_git_pass(git_oid_fromstr(&entry_oid
, blob_oid
));
200 cl_git_pass(git_treebuilder_new(&builder
, g_repo
, NULL
));
202 cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder
));
204 for (i
= 0; _entries
[i
].filename
; ++i
)
205 cl_git_pass(git_treebuilder_insert(NULL
,
206 builder
, _entries
[i
].filename
, &entry_oid
, _entries
[i
].attr
));
208 cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder
));
210 cl_git_pass(git_treebuilder_remove(builder
, "apple"));
211 cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder
));
213 cl_git_pass(git_treebuilder_remove(builder
, "apple_after"));
214 cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder
));
216 cl_git_pass(git_treebuilder_insert(
217 NULL
, builder
, "before_last", &entry_oid
, GIT_FILEMODE_BLOB
));
218 cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder
));
220 /* reinsert apple_after */
221 cl_git_pass(git_treebuilder_insert(
222 NULL
, builder
, "apple_after", &entry_oid
, GIT_FILEMODE_BLOB
));
223 cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder
));
225 cl_git_pass(git_treebuilder_remove(builder
, "last"));
226 cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder
));
229 cl_git_pass(git_treebuilder_insert(
230 NULL
, builder
, "last", &entry_oid
, GIT_FILEMODE_BLOB
));
231 cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder
));
233 cl_git_pass(git_treebuilder_insert(
234 NULL
, builder
, "apple_extra", &entry_oid
, GIT_FILEMODE_BLOB
));
235 cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder
));
237 cl_git_pass(git_treebuilder_write(&tree_oid
, builder
));
239 git_treebuilder_free(builder
);
241 cl_git_pass(git_tree_lookup(&tree
, g_repo
, &tree_oid
));
243 cl_assert_equal_i(7, (int)git_tree_entrycount(tree
));
245 cl_assert(git_tree_entry_byname(tree
, ".first") != NULL
);
246 cl_assert(git_tree_entry_byname(tree
, "apple") == NULL
);
247 cl_assert(git_tree_entry_byname(tree
, "apple_after") != NULL
);
248 cl_assert(git_tree_entry_byname(tree
, "apple_extra") != NULL
);
249 cl_assert(git_tree_entry_byname(tree
, "last") != NULL
);
251 aardvark_i
= apple_i
= apple_after_i
= apple_extra_i
= last_i
= -1;
253 for (i
= 0; i
< 7; ++i
) {
254 const git_tree_entry
*entry
= git_tree_entry_byindex(tree
, i
);
256 if (!strcmp(entry
->filename
, "aardvark"))
258 else if (!strcmp(entry
->filename
, "apple"))
260 else if (!strcmp(entry
->filename
, "apple_after"))
262 else if (!strcmp(entry
->filename
, "apple_extra"))
264 else if (!strcmp(entry
->filename
, "last"))
268 cl_assert_equal_i(-1, apple_i
);
269 cl_assert_equal_i(6, last_i
);
270 cl_assert(aardvark_i
< apple_after_i
);
271 cl_assert(apple_after_i
< apple_extra_i
);
276 static int treebuilder_filter_prefixed(
277 const git_tree_entry
*entry
, void *payload
)
279 return !git__prefixcmp(git_tree_entry_name(entry
), payload
);
282 void test_object_tree_write__filtering(void)
284 git_treebuilder
*builder
;
286 git_oid entry_oid
, tree_oid
;
289 git_oid_fromstr(&entry_oid
, blob_oid
);
291 cl_git_pass(git_treebuilder_new(&builder
, g_repo
, NULL
));
293 for (i
= 0; _entries
[i
].filename
; ++i
)
294 cl_git_pass(git_treebuilder_insert(NULL
,
295 builder
, _entries
[i
].filename
, &entry_oid
, _entries
[i
].attr
));
297 cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder
));
299 cl_assert(git_treebuilder_get(builder
, "apple") != NULL
);
300 cl_assert(git_treebuilder_get(builder
, "aardvark") != NULL
);
301 cl_assert(git_treebuilder_get(builder
, "last") != NULL
);
303 git_treebuilder_filter(builder
, treebuilder_filter_prefixed
, "apple");
305 cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder
));
307 cl_assert(git_treebuilder_get(builder
, "apple") == NULL
);
308 cl_assert(git_treebuilder_get(builder
, "aardvark") != NULL
);
309 cl_assert(git_treebuilder_get(builder
, "last") != NULL
);
311 git_treebuilder_filter(builder
, treebuilder_filter_prefixed
, "a");
313 cl_assert_equal_i(2, (int)git_treebuilder_entrycount(builder
));
315 cl_assert(git_treebuilder_get(builder
, "aardvark") == NULL
);
316 cl_assert(git_treebuilder_get(builder
, "last") != NULL
);
318 cl_git_pass(git_treebuilder_write(&tree_oid
, builder
));
320 git_treebuilder_free(builder
);
322 cl_git_pass(git_tree_lookup(&tree
, g_repo
, &tree_oid
));
324 cl_assert_equal_i(2, (int)git_tree_entrycount(tree
));
329 void test_object_tree_write__cruel_paths(void)
331 static const char *the_paths
[] = {
333 " : * ? \" \n < > |",
339 REP1024("1234"), /* 4096 char string */
340 REP1024("12345678"), /* 8192 char string */
341 "\xC5\xAA\x6E\xC4\xAD\x63\xC5\x8D\x64\x65\xCC\xBD", /* Ūnĭcōde̽ */
344 git_treebuilder
*builder
;
346 git_oid id
, bid
, subid
;
351 git_oid_fromstr(&bid
, blob_oid
);
354 cl_git_pass(git_treebuilder_new(&builder
, g_repo
, NULL
));
355 for (scan
= the_paths
; *scan
; ++scan
) {
356 cl_git_pass(git_treebuilder_insert(
357 NULL
, builder
, *scan
, &bid
, GIT_FILEMODE_BLOB
));
360 cl_git_pass(git_treebuilder_write(&id
, builder
));
361 git_treebuilder_free(builder
);
363 /* check data is correct */
364 cl_git_pass(git_tree_lookup(&tree
, g_repo
, &id
));
366 cl_assert_equal_i(count
, git_tree_entrycount(tree
));
368 for (scan
= the_paths
; *scan
; ++scan
) {
369 const git_tree_entry
*cte
= git_tree_entry_byname(tree
, *scan
);
370 cl_assert(cte
!= NULL
);
371 cl_assert_equal_s(*scan
, git_tree_entry_name(cte
));
373 for (scan
= the_paths
; *scan
; ++scan
) {
374 cl_git_pass(git_tree_entry_bypath(&te
, tree
, *scan
));
375 cl_assert_equal_s(*scan
, git_tree_entry_name(te
));
376 git_tree_entry_free(te
);
381 /* let's try longer paths */
382 cl_git_pass(git_treebuilder_new(&builder
, g_repo
, NULL
));
383 for (scan
= the_paths
; *scan
; ++scan
) {
384 cl_git_pass(git_treebuilder_insert(
385 NULL
, builder
, *scan
, &id
, GIT_FILEMODE_TREE
));
387 cl_git_pass(git_treebuilder_write(&subid
, builder
));
388 git_treebuilder_free(builder
);
390 /* check data is correct */
391 cl_git_pass(git_tree_lookup(&tree
, g_repo
, &subid
));
393 cl_assert_equal_i(count
, git_tree_entrycount(tree
));
395 for (i
= 0; i
< count
; ++i
) {
396 for (j
= 0; j
< count
; ++j
) {
397 git_str b
= GIT_STR_INIT
;
398 cl_git_pass(git_str_joinpath(&b
, the_paths
[i
], the_paths
[j
]));
399 cl_git_pass(git_tree_entry_bypath(&te
, tree
, b
.ptr
));
400 cl_assert_equal_s(the_paths
[j
], git_tree_entry_name(te
));
401 git_tree_entry_free(te
);
409 void test_object_tree_write__protect_filesystems(void)
411 git_treebuilder
*builder
;
414 cl_git_pass(git_oid_fromstr(&bid
, "fa49b077972391ad58037050f2a75f74e3671e92"));
416 /* Ensure that (by default) we can write objects with funny names on
417 * platforms that are not affected.
419 cl_git_pass(git_treebuilder_new(&builder
, g_repo
, NULL
));
421 cl_git_fail(git_treebuilder_insert(NULL
, builder
, ".git.", &bid
, GIT_FILEMODE_BLOB
));
422 cl_git_fail(git_treebuilder_insert(NULL
, builder
, "git~1", &bid
, GIT_FILEMODE_BLOB
));
425 cl_git_pass(git_treebuilder_insert(NULL
, builder
, ".git\xef\xbb\xbf", &bid
, GIT_FILEMODE_BLOB
));
426 cl_git_pass(git_treebuilder_insert(NULL
, builder
, ".git\xe2\x80\xad", &bid
, GIT_FILEMODE_BLOB
));
429 git_treebuilder_free(builder
);
431 /* Now turn on core.protectHFS and core.protectNTFS and validate that these
432 * paths are rejected.
435 cl_repo_set_bool(g_repo
, "core.protectHFS", true);
436 cl_repo_set_bool(g_repo
, "core.protectNTFS", true);
438 cl_git_pass(git_treebuilder_new(&builder
, g_repo
, NULL
));
440 cl_git_fail(git_treebuilder_insert(NULL
, builder
, ".git.", &bid
, GIT_FILEMODE_BLOB
));
441 cl_git_fail(git_treebuilder_insert(NULL
, builder
, "git~1", &bid
, GIT_FILEMODE_BLOB
));
443 cl_git_fail(git_treebuilder_insert(NULL
, builder
, ".git\xef\xbb\xbf", &bid
, GIT_FILEMODE_BLOB
));
444 cl_git_fail(git_treebuilder_insert(NULL
, builder
, ".git\xe2\x80\xad", &bid
, GIT_FILEMODE_BLOB
));
445 cl_git_fail(git_treebuilder_insert(NULL
, builder
, ".git::$INDEX_ALLOCATION/dummy-file", &bid
, GIT_FILEMODE_BLOB
));
447 git_treebuilder_free(builder
);
450 static void test_invalid_objects(bool should_allow_invalid
)
452 git_treebuilder
*builder
;
453 git_oid valid_blob_id
, invalid_blob_id
, valid_tree_id
, invalid_tree_id
;
455 #define assert_allowed(expr) \
456 clar__assert(!(expr) == should_allow_invalid, \
457 __FILE__, __func__, __LINE__, \
458 (should_allow_invalid ? \
459 "Expected function call to succeed: " #expr : \
460 "Expected function call to fail: " #expr), \
463 cl_git_pass(git_oid_fromstr(&valid_blob_id
, blob_oid
));
464 cl_git_pass(git_oid_fromstr(&invalid_blob_id
,
465 "1234567890123456789012345678901234567890"));
466 cl_git_pass(git_oid_fromstr(&valid_tree_id
, first_tree
));
467 cl_git_pass(git_oid_fromstr(&invalid_tree_id
,
468 "0000000000111111111122222222223333333333"));
470 cl_git_pass(git_treebuilder_new(&builder
, g_repo
, NULL
));
472 /* test valid blobs and trees (these should always pass) */
473 cl_git_pass(git_treebuilder_insert(NULL
, builder
, "file.txt", &valid_blob_id
, GIT_FILEMODE_BLOB
));
474 cl_git_pass(git_treebuilder_insert(NULL
, builder
, "folder", &valid_tree_id
, GIT_FILEMODE_TREE
));
476 /* replace valid files and folders with invalid ones */
477 assert_allowed(git_treebuilder_insert(NULL
, builder
, "file.txt", &invalid_blob_id
, GIT_FILEMODE_BLOB
));
478 assert_allowed(git_treebuilder_insert(NULL
, builder
, "folder", &invalid_blob_id
, GIT_FILEMODE_BLOB
));
480 /* insert new invalid files and folders */
481 assert_allowed(git_treebuilder_insert(NULL
, builder
, "invalid_file.txt", &invalid_blob_id
, GIT_FILEMODE_BLOB
));
482 assert_allowed(git_treebuilder_insert(NULL
, builder
, "invalid_folder", &invalid_blob_id
, GIT_FILEMODE_BLOB
));
484 /* insert valid blobs as trees and trees as blobs */
485 assert_allowed(git_treebuilder_insert(NULL
, builder
, "file_as_folder", &valid_blob_id
, GIT_FILEMODE_TREE
));
486 assert_allowed(git_treebuilder_insert(NULL
, builder
, "folder_as_file.txt", &valid_tree_id
, GIT_FILEMODE_BLOB
));
488 #undef assert_allowed
490 git_treebuilder_free(builder
);
493 static void test_inserting_submodule(void)
495 git_treebuilder
*bld
;
498 cl_git_pass(git_oid_fromstr(&sm_id
, "da39a3ee5e6b4b0d3255bfef95601890afd80709"));
499 cl_git_pass(git_treebuilder_new(&bld
, g_repo
, NULL
));
500 cl_git_pass(git_treebuilder_insert(NULL
, bld
, "sm", &sm_id
, GIT_FILEMODE_COMMIT
));
501 git_treebuilder_free(bld
);
504 void test_object_tree_write__object_validity(void)
506 /* Ensure that we cannot add invalid objects by default */
507 test_invalid_objects(false);
508 test_inserting_submodule();
510 /* Ensure that we can turn off validation */
511 cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION
, 0));
512 test_invalid_objects(true);
513 test_inserting_submodule();
516 void test_object_tree_write__invalid_null_oid(void)
518 git_treebuilder
*bld
;
519 git_oid null_oid
= {{0}};
521 cl_git_pass(git_treebuilder_new(&bld
, g_repo
, NULL
));
522 cl_git_fail(git_treebuilder_insert(NULL
, bld
, "null_oid_file", &null_oid
, GIT_FILEMODE_BLOB
));
523 cl_assert(git_error_last() && strstr(git_error_last()->message
, "null OID") != NULL
);
525 git_treebuilder_free(bld
);