Add tests for case insensitive tree iterator This adds a test case for ci tree iteration when there is a name conflict. This points out a behavior quirk in the current version that I'd like to fix - namely, all tree entries get mapped to one version of the case pattern in the ci code - i.e. even if you have A/1.txt and a/2.txt, both will be reported as a/1.txt and a/2.txt because we only copy the name of a file at a given frame once. It would be nice to fix this, but I'm worried about how complex that is if you get a/B/c/1.txt and A/b/C/2.txt. It may require a walk up the frames whenever you advance to the next item in a blended equivalence class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c
index 3d72d7a..63613e9 100644
--- a/tests-clar/repo/iterator.c
+++ b/tests-clar/repo/iterator.c
@@ -1,6 +1,7 @@
#include "clar_libgit2.h"
#include "iterator.h"
#include "repository.h"
+#include <stdarg.h>
static git_repository *g_repo;
@@ -24,11 +25,19 @@ static void expect_iterator_items(
const git_index_entry *entry;
int count;
int no_trees = !(git_iterator_flags(i) & GIT_ITERATOR_INCLUDE_TREES);
+ bool v = false;
+
+ if (expected_flat < 0) { v = true; expected_flat = -expected_flat; }
+ if (expected_total < 0) { v = true; expected_total = -expected_total; }
count = 0;
cl_git_pass(git_iterator_current(&entry, i));
+ if (v) fprintf(stderr, "== %s ==\n", no_trees ? "notrees" : "trees");
+
while (entry != NULL) {
+ if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode);
+
if (no_trees)
cl_assert(entry->mode != GIT_FILEMODE_TREE);
@@ -57,7 +66,11 @@ static void expect_iterator_items(
count = 0;
cl_git_pass(git_iterator_current(&entry, i));
+ if (v) fprintf(stderr, "-- %s --\n", no_trees ? "notrees" : "trees");
+
while (entry != NULL) {
+ if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode);
+
if (no_trees)
cl_assert(entry->mode != GIT_FILEMODE_TREE);
@@ -392,6 +405,86 @@ void test_repo_iterator__tree_more(void)
git_tree_free(head);
}
+/* "b=name,t=name", blob_id, tree_id */
+static void build_test_tree(
+ git_oid *out, git_repository *repo, const char *fmt, ...)
+{
+ git_oid *id;
+ git_treebuilder *builder;
+ const char *scan = fmt, *next;
+ char type, delimiter;
+ git_filemode_t mode;
+ git_buf name = GIT_BUF_INIT;
+ va_list arglist;
+
+ cl_git_pass(git_treebuilder_create(&builder, NULL)); /* start builder */
+
+ va_start(arglist, fmt);
+ while (*scan) {
+ switch (type = *scan++) {
+ case 't': case 'T': mode = GIT_FILEMODE_TREE; break;
+ case 'b': case 'B': mode = GIT_FILEMODE_BLOB; break;
+ default:
+ cl_assert(type == 't' || type == 'T' || type == 'b' || type == 'B');
+ }
+
+ delimiter = *scan++; /* read and skip delimiter */
+ for (next = scan; *next && *next != delimiter; ++next)
+ /* seek end */;
+ cl_git_pass(git_buf_set(&name, scan, (size_t)(next - scan)));
+ for (scan = next; *scan && (*scan == delimiter || *scan == ','); ++scan)
+ /* skip delimiter and optional comma */;
+
+ id = va_arg(arglist, git_oid *);
+
+ cl_git_pass(git_treebuilder_insert(NULL, builder, name.ptr, id, mode));
+ }
+ va_end(arglist);
+
+ cl_git_pass(git_treebuilder_write(out, repo, builder));
+
+ git_treebuilder_free(builder);
+ git_buf_free(&name);
+}
+
+void test_repo_iterator__tree_case_conflicts(void)
+{
+ const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6";
+ git_tree *tree;
+ git_oid blob_id, biga_id, littlea_id, tree_id;
+ git_iterator *i;
+ const char *expect_cs[] = {
+ "A/1.file", "A/3.file", "a/2.file", "a/4.file" };
+ const char *expect_ci[] = {
+ "a/1.file", "a/2.file", "a/3.file", "a/4.file" };
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */
+
+ /* create tree with: A/1.file, A/3.file, a/2.file, a/4.file */
+ build_test_tree(
+ &biga_id, g_repo, "b/1.file/,b/3.file/", &blob_id, &blob_id);
+ build_test_tree(
+ &littlea_id, g_repo, "b/2.file/,b/4.file/", &blob_id, &blob_id);
+ build_test_tree(
+ &tree_id, g_repo, "t/A/,t/a/", &biga_id, &littlea_id);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
+ expect_iterator_items(i, 4, expect_cs, 4, expect_cs);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL));
+ expect_iterator_items(i, 4, expect_ci, -4, expect_ci);
+ git_iterator_free(i);
+
+ git_tree_free(tree);
+}
+
void test_repo_iterator__workdir(void)
{
git_iterator *i;