summaryrefslogtreecommitdiff
path: root/tests/core
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/core
parent7b947bf5cc59eefa83c28eb5f5fd8434207ebb8b (diff)
downloadlibgit2-1782038144ef3413831801bb9c2f3038a84ac6f4.tar.gz
Rename tests-clar to tests
Diffstat (limited to 'tests/core')
-rw-r--r--tests/core/bitvec.c64
-rw-r--r--tests/core/buffer.c1039
-rw-r--r--tests/core/caps.c31
-rw-r--r--tests/core/copy.c126
-rw-r--r--tests/core/dirent.c236
-rw-r--r--tests/core/env.c303
-rw-r--r--tests/core/errors.c87
-rw-r--r--tests/core/filebuf.c126
-rw-r--r--tests/core/hex.c22
-rw-r--r--tests/core/iconv.c68
-rw-r--r--tests/core/mkdir.c188
-rw-r--r--tests/core/oid.c70
-rw-r--r--tests/core/oidmap.c110
-rw-r--r--tests/core/opts.c19
-rw-r--r--tests/core/path.c583
-rw-r--r--tests/core/pool.c145
-rw-r--r--tests/core/posix.c99
-rw-r--r--tests/core/rmdir.c98
-rw-r--r--tests/core/sortedcache.c363
-rw-r--r--tests/core/stat.c97
-rw-r--r--tests/core/string.c41
-rw-r--r--tests/core/strmap.c102
-rw-r--r--tests/core/strtol.c37
-rw-r--r--tests/core/vector.c275
24 files changed, 4329 insertions, 0 deletions
diff --git a/tests/core/bitvec.c b/tests/core/bitvec.c
new file mode 100644
index 000000000..48d7b99f0
--- /dev/null
+++ b/tests/core/bitvec.c
@@ -0,0 +1,64 @@
+#include "clar_libgit2.h"
+#include "bitvec.h"
+
+#if 0
+static void print_bitvec(git_bitvec *bv)
+{
+ int b;
+
+ if (!bv->length) {
+ for (b = 63; b >= 0; --b)
+ fprintf(stderr, "%d", (bv->u.bits & (1ul << b)) ? 1 : 0);
+ } else {
+ for (b = bv->length * 8; b >= 0; --b)
+ fprintf(stderr, "%d", (bv->u.ptr[b >> 3] & (b & 0x0ff)) ? 1 : 0);
+ }
+ fprintf(stderr, "\n");
+}
+#endif
+
+static void set_some_bits(git_bitvec *bv, size_t length)
+{
+ size_t i;
+
+ for (i = 0; i < length; ++i) {
+ if (i % 3 == 0 || i % 7 == 0)
+ git_bitvec_set(bv, i, true);
+ }
+}
+
+static void check_some_bits(git_bitvec *bv, size_t length)
+{
+ size_t i;
+
+ for (i = 0; i < length; ++i)
+ cl_assert_equal_b(i % 3 == 0 || i % 7 == 0, git_bitvec_get(bv, i));
+}
+
+void test_core_bitvec__0(void)
+{
+ git_bitvec bv;
+
+ cl_git_pass(git_bitvec_init(&bv, 32));
+ set_some_bits(&bv, 16);
+ check_some_bits(&bv, 16);
+ git_bitvec_clear(&bv);
+ set_some_bits(&bv, 32);
+ check_some_bits(&bv, 32);
+ git_bitvec_clear(&bv);
+ set_some_bits(&bv, 64);
+ check_some_bits(&bv, 64);
+ git_bitvec_free(&bv);
+
+ cl_git_pass(git_bitvec_init(&bv, 128));
+ set_some_bits(&bv, 32);
+ check_some_bits(&bv, 32);
+ set_some_bits(&bv, 128);
+ check_some_bits(&bv, 128);
+ git_bitvec_free(&bv);
+
+ cl_git_pass(git_bitvec_init(&bv, 4000));
+ set_some_bits(&bv, 4000);
+ check_some_bits(&bv, 4000);
+ git_bitvec_free(&bv);
+}
diff --git a/tests/core/buffer.c b/tests/core/buffer.c
new file mode 100644
index 000000000..11d173d49
--- /dev/null
+++ b/tests/core/buffer.c
@@ -0,0 +1,1039 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "buf_text.h"
+#include "hashsig.h"
+#include "fileops.h"
+
+#define TESTSTR "Have you seen that? Have you seeeen that??"
+const char *test_string = TESTSTR;
+const char *test_string_x2 = TESTSTR TESTSTR;
+
+#define TESTSTR_4096 REP1024("1234")
+#define TESTSTR_8192 REP1024("12341234")
+const char *test_4096 = TESTSTR_4096;
+const char *test_8192 = TESTSTR_8192;
+
+/* test basic data concatenation */
+void test_core_buffer__0(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_assert(buf.size == 0);
+
+ git_buf_puts(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_string, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_string_x2, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* test git_buf_printf */
+void test_core_buffer__1(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_printf(&buf, "%s %s %d ", "shoop", "da", 23);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("shoop da 23 ", git_buf_cstr(&buf));
+
+ git_buf_printf(&buf, "%s %d", "woop", 42);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("shoop da 23 woop 42", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* more thorough test of concatenation options */
+void test_core_buffer__2(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ int i;
+ char data[128];
+
+ cl_assert(buf.size == 0);
+
+ /* this must be safe to do */
+ git_buf_free(&buf);
+ cl_assert(buf.size == 0);
+ cl_assert(buf.asize == 0);
+
+ /* empty buffer should be empty string */
+ cl_assert_equal_s("", git_buf_cstr(&buf));
+ cl_assert(buf.size == 0);
+ /* cl_assert(buf.asize == 0); -- should not assume what git_buf does */
+
+ /* free should set us back to the beginning */
+ git_buf_free(&buf);
+ cl_assert(buf.size == 0);
+ cl_assert(buf.asize == 0);
+
+ /* add letter */
+ git_buf_putc(&buf, '+');
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("+", git_buf_cstr(&buf));
+
+ /* add letter again */
+ git_buf_putc(&buf, '+');
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("++", git_buf_cstr(&buf));
+
+ /* let's try that a few times */
+ for (i = 0; i < 16; ++i) {
+ git_buf_putc(&buf, '+');
+ cl_assert(git_buf_oom(&buf) == 0);
+ }
+ cl_assert_equal_s("++++++++++++++++++", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+
+ /* add data */
+ git_buf_put(&buf, "xo", 2);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("xo", git_buf_cstr(&buf));
+
+ /* add letter again */
+ git_buf_put(&buf, "xo", 2);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("xoxo", git_buf_cstr(&buf));
+
+ /* let's try that a few times */
+ for (i = 0; i < 16; ++i) {
+ git_buf_put(&buf, "xo", 2);
+ cl_assert(git_buf_oom(&buf) == 0);
+ }
+ cl_assert_equal_s("xoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxo",
+ git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+
+ /* set to string */
+ git_buf_sets(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_string, git_buf_cstr(&buf));
+
+ /* append string */
+ git_buf_puts(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_string_x2, git_buf_cstr(&buf));
+
+ /* set to string again (should overwrite - not append) */
+ git_buf_sets(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_string, git_buf_cstr(&buf));
+
+ /* test clear */
+ git_buf_clear(&buf);
+ cl_assert_equal_s("", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+
+ /* test extracting data into buffer */
+ git_buf_puts(&buf, REP4("0123456789"));
+ cl_assert(git_buf_oom(&buf) == 0);
+
+ git_buf_copy_cstr(data, sizeof(data), &buf);
+ cl_assert_equal_s(REP4("0123456789"), data);
+ git_buf_copy_cstr(data, 11, &buf);
+ cl_assert_equal_s("0123456789", data);
+ git_buf_copy_cstr(data, 3, &buf);
+ cl_assert_equal_s("01", data);
+ git_buf_copy_cstr(data, 1, &buf);
+ cl_assert_equal_s("", data);
+
+ git_buf_copy_cstr(data, sizeof(data), &buf);
+ cl_assert_equal_s(REP4("0123456789"), data);
+
+ git_buf_sets(&buf, REP256("x"));
+ git_buf_copy_cstr(data, sizeof(data), &buf);
+ /* since sizeof(data) == 128, only 127 bytes should be copied */
+ cl_assert_equal_s(REP4(REP16("x")) REP16("x") REP16("x")
+ REP16("x") "xxxxxxxxxxxxxxx", data);
+
+ git_buf_free(&buf);
+
+ git_buf_copy_cstr(data, sizeof(data), &buf);
+ cl_assert_equal_s("", data);
+}
+
+/* let's do some tests with larger buffers to push our limits */
+void test_core_buffer__3(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ /* set to string */
+ git_buf_set(&buf, test_4096, 4096);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_4096, git_buf_cstr(&buf));
+
+ /* append string */
+ git_buf_puts(&buf, test_4096);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_8192, git_buf_cstr(&buf));
+
+ /* set to string again (should overwrite - not append) */
+ git_buf_set(&buf, test_4096, 4096);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_4096, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* let's try some producer/consumer tests */
+void test_core_buffer__4(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ int i;
+
+ for (i = 0; i < 10; ++i) {
+ git_buf_puts(&buf, "1234"); /* add 4 */
+ cl_assert(git_buf_oom(&buf) == 0);
+ git_buf_consume(&buf, buf.ptr + 2); /* eat the first two */
+ cl_assert(strlen(git_buf_cstr(&buf)) == (size_t)((i + 1) * 2));
+ }
+ /* we have appended 1234 10x and removed the first 20 letters */
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, NULL);
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, "invalid pointer");
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, buf.ptr);
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, buf.ptr + 1);
+ cl_assert_equal_s("2341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, buf.ptr + buf.size);
+ cl_assert_equal_s("", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+
+static void
+check_buf_append(
+ const char* data_a,
+ const char* data_b,
+ const char* expected_data,
+ size_t expected_size,
+ size_t expected_asize)
+{
+ git_buf tgt = GIT_BUF_INIT;
+
+ git_buf_sets(&tgt, data_a);
+ cl_assert(git_buf_oom(&tgt) == 0);
+ git_buf_puts(&tgt, data_b);
+ cl_assert(git_buf_oom(&tgt) == 0);
+ cl_assert_equal_s(expected_data, git_buf_cstr(&tgt));
+ cl_assert(tgt.size == expected_size);
+ if (expected_asize > 0)
+ cl_assert(tgt.asize == expected_asize);
+
+ git_buf_free(&tgt);
+}
+
+static void
+check_buf_append_abc(
+ const char* buf_a,
+ const char* buf_b,
+ const char* buf_c,
+ const char* expected_ab,
+ const char* expected_abc,
+ const char* expected_abca,
+ const char* expected_abcab,
+ const char* expected_abcabc)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_sets(&buf, buf_a);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(buf_a, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_b);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected_ab, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_c);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abc, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_a);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abca, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_b);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abcab, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_c);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abcabc, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* more variations on append tests */
+void test_core_buffer__5(void)
+{
+ check_buf_append("", "", "", 0, 8);
+ check_buf_append("a", "", "a", 1, 8);
+ check_buf_append("", "a", "a", 1, 8);
+ check_buf_append("", "a", "a", 1, 8);
+ check_buf_append("a", "", "a", 1, 8);
+ check_buf_append("a", "b", "ab", 2, 8);
+ check_buf_append("", "abcdefgh", "abcdefgh", 8, 16);
+ check_buf_append("abcdefgh", "", "abcdefgh", 8, 16);
+
+ /* buffer with starting asize will grow to:
+ * 1 -> 2, 2 -> 3, 3 -> 5, 4 -> 6, 5 -> 8, 6 -> 9,
+ * 7 -> 11, 8 -> 12, 9 -> 14, 10 -> 15, 11 -> 17, 12 -> 18,
+ * 13 -> 20, 14 -> 21, 15 -> 23, 16 -> 24, 17 -> 26, 18 -> 27,
+ * 19 -> 29, 20 -> 30, 21 -> 32, 22 -> 33, 23 -> 35, 24 -> 36,
+ * ...
+ * follow sequence until value > target size,
+ * then round up to nearest multiple of 8.
+ */
+
+ check_buf_append("abcdefgh", "/", "abcdefgh/", 9, 16);
+ check_buf_append("abcdefgh", "ijklmno", "abcdefghijklmno", 15, 16);
+ check_buf_append("abcdefgh", "ijklmnop", "abcdefghijklmnop", 16, 24);
+ check_buf_append("0123456789", "0123456789",
+ "01234567890123456789", 20, 24);
+ check_buf_append(REP16("x"), REP16("o"),
+ REP16("x") REP16("o"), 32, 40);
+
+ check_buf_append(test_4096, "", test_4096, 4096, 4104);
+ check_buf_append(test_4096, test_4096, test_8192, 8192, 9240);
+
+ /* check sequences of appends */
+ check_buf_append_abc("a", "b", "c",
+ "ab", "abc", "abca", "abcab", "abcabc");
+ check_buf_append_abc("a1", "b2", "c3",
+ "a1b2", "a1b2c3", "a1b2c3a1",
+ "a1b2c3a1b2", "a1b2c3a1b2c3");
+ check_buf_append_abc("a1/", "b2/", "c3/",
+ "a1/b2/", "a1/b2/c3/", "a1/b2/c3/a1/",
+ "a1/b2/c3/a1/b2/", "a1/b2/c3/a1/b2/c3/");
+}
+
+/* test swap */
+void test_core_buffer__6(void)
+{
+ git_buf a = GIT_BUF_INIT;
+ git_buf b = GIT_BUF_INIT;
+
+ git_buf_sets(&a, "foo");
+ cl_assert(git_buf_oom(&a) == 0);
+ git_buf_sets(&b, "bar");
+ cl_assert(git_buf_oom(&b) == 0);
+
+ cl_assert_equal_s("foo", git_buf_cstr(&a));
+ cl_assert_equal_s("bar", git_buf_cstr(&b));
+
+ git_buf_swap(&a, &b);
+
+ cl_assert_equal_s("bar", git_buf_cstr(&a));
+ cl_assert_equal_s("foo", git_buf_cstr(&b));
+
+ git_buf_free(&a);
+ git_buf_free(&b);
+}
+
+
+/* test detach/attach data */
+void test_core_buffer__7(void)
+{
+ const char *fun = "This is fun";
+ git_buf a = GIT_BUF_INIT;
+ char *b = NULL;
+
+ git_buf_sets(&a, "foo");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_equal_s("foo", git_buf_cstr(&a));
+
+ b = git_buf_detach(&a);
+
+ cl_assert_equal_s("foo", b);
+ cl_assert_equal_s("", a.ptr);
+ git__free(b);
+
+ b = git_buf_detach(&a);
+
+ cl_assert_equal_s(NULL, b);
+ cl_assert_equal_s("", a.ptr);
+
+ git_buf_free(&a);
+
+ b = git__strdup(fun);
+ git_buf_attach(&a, b, 0);
+
+ cl_assert_equal_s(fun, a.ptr);
+ cl_assert(a.size == strlen(fun));
+ cl_assert(a.asize == strlen(fun) + 1);
+
+ git_buf_free(&a);
+
+ b = git__strdup(fun);
+ git_buf_attach(&a, b, strlen(fun) + 1);
+
+ cl_assert_equal_s(fun, a.ptr);
+ cl_assert(a.size == strlen(fun));
+ cl_assert(a.asize == strlen(fun) + 1);
+
+ git_buf_free(&a);
+}
+
+
+static void
+check_joinbuf_2(
+ const char *a,
+ const char *b,
+ const char *expected)
+{
+ char sep = '/';
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_join(&buf, sep, a, b);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
+ git_buf_free(&buf);
+}
+
+static void
+check_joinbuf_n_2(
+ const char *a,
+ const char *b,
+ const char *expected)
+{
+ char sep = '/';
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_sets(&buf, a);
+ cl_assert(git_buf_oom(&buf) == 0);
+
+ git_buf_join_n(&buf, sep, 1, b);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+static void
+check_joinbuf_n_4(
+ const char *a,
+ const char *b,
+ const char *c,
+ const char *d,
+ const char *expected)
+{
+ char sep = ';';
+ git_buf buf = GIT_BUF_INIT;
+ git_buf_join_n(&buf, sep, 4, a, b, c, d);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
+ git_buf_free(&buf);
+}
+
+/* test join */
+void test_core_buffer__8(void)
+{
+ git_buf a = GIT_BUF_INIT;
+
+ git_buf_join_n(&a, '/', 1, "foo");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_equal_s("foo", git_buf_cstr(&a));
+
+ git_buf_join_n(&a, '/', 1, "bar");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_equal_s("foo/bar", git_buf_cstr(&a));
+
+ git_buf_join_n(&a, '/', 1, "baz");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_equal_s("foo/bar/baz", git_buf_cstr(&a));
+
+ git_buf_free(&a);
+
+ check_joinbuf_2(NULL, "", "");
+ check_joinbuf_2(NULL, "a", "a");
+ check_joinbuf_2(NULL, "/a", "/a");
+ check_joinbuf_2("", "", "");
+ check_joinbuf_2("", "a", "a");
+ check_joinbuf_2("", "/a", "/a");
+ check_joinbuf_2("a", "", "a/");
+ check_joinbuf_2("a", "/", "a/");
+ check_joinbuf_2("a", "b", "a/b");
+ check_joinbuf_2("/", "a", "/a");
+ check_joinbuf_2("/", "", "/");
+ check_joinbuf_2("/a", "/b", "/a/b");
+ check_joinbuf_2("/a", "/b/", "/a/b/");
+ check_joinbuf_2("/a/", "b/", "/a/b/");
+ check_joinbuf_2("/a/", "/b/", "/a/b/");
+ check_joinbuf_2("/a/", "//b/", "/a/b/");
+ check_joinbuf_2("/abcd", "/defg", "/abcd/defg");
+ check_joinbuf_2("/abcd", "/defg/", "/abcd/defg/");
+ check_joinbuf_2("/abcd/", "defg/", "/abcd/defg/");
+ check_joinbuf_2("/abcd/", "/defg/", "/abcd/defg/");
+
+ check_joinbuf_n_2("", "", "");
+ check_joinbuf_n_2("", "a", "a");
+ check_joinbuf_n_2("", "/a", "/a");
+ check_joinbuf_n_2("a", "", "a/");
+ check_joinbuf_n_2("a", "/", "a/");
+ check_joinbuf_n_2("a", "b", "a/b");
+ check_joinbuf_n_2("/", "a", "/a");
+ check_joinbuf_n_2("/", "", "/");
+ check_joinbuf_n_2("/a", "/b", "/a/b");
+ check_joinbuf_n_2("/a", "/b/", "/a/b/");
+ check_joinbuf_n_2("/a/", "b/", "/a/b/");
+ check_joinbuf_n_2("/a/", "/b/", "/a/b/");
+ check_joinbuf_n_2("/abcd", "/defg", "/abcd/defg");
+ check_joinbuf_n_2("/abcd", "/defg/", "/abcd/defg/");
+ check_joinbuf_n_2("/abcd/", "defg/", "/abcd/defg/");
+ check_joinbuf_n_2("/abcd/", "/defg/", "/abcd/defg/");
+
+ check_joinbuf_n_4("", "", "", "", "");
+ check_joinbuf_n_4("", "a", "", "", "a;");
+ check_joinbuf_n_4("a", "", "", "", "a;");
+ check_joinbuf_n_4("", "", "", "a", "a");
+ check_joinbuf_n_4("a", "b", "", ";c;d;", "a;b;c;d;");
+ check_joinbuf_n_4("a", "b", "", ";c;d", "a;b;c;d");
+ check_joinbuf_n_4("abcd", "efgh", "ijkl", "mnop", "abcd;efgh;ijkl;mnop");
+ check_joinbuf_n_4("abcd;", "efgh;", "ijkl;", "mnop;", "abcd;efgh;ijkl;mnop;");
+ check_joinbuf_n_4(";abcd;", ";efgh;", ";ijkl;", ";mnop;", ";abcd;efgh;ijkl;mnop;");
+}
+
+void test_core_buffer__9(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ /* just some exhaustive tests of various separator placement */
+ char *a[] = { "", "-", "a-", "-a", "-a-" };
+ char *b[] = { "", "-", "b-", "-b", "-b-" };
+ char sep[] = { 0, '-', '/' };
+ char *expect_null[] = { "", "-", "a-", "-a", "-a-",
+ "-", "--", "a--", "-a-", "-a--",
+ "b-", "-b-", "a-b-", "-ab-", "-a-b-",
+ "-b", "--b", "a--b", "-a-b", "-a--b",
+ "-b-", "--b-", "a--b-", "-a-b-", "-a--b-" };
+ char *expect_dash[] = { "", "-", "a-", "-a-", "-a-",
+ "-", "-", "a-", "-a-", "-a-",
+ "b-", "-b-", "a-b-", "-a-b-", "-a-b-",
+ "-b", "-b", "a-b", "-a-b", "-a-b",
+ "-b-", "-b-", "a-b-", "-a-b-", "-a-b-" };
+ char *expect_slas[] = { "", "-/", "a-/", "-a/", "-a-/",
+ "-", "-/-", "a-/-", "-a/-", "-a-/-",
+ "b-", "-/b-", "a-/b-", "-a/b-", "-a-/b-",
+ "-b", "-/-b", "a-/-b", "-a/-b", "-a-/-b",
+ "-b-", "-/-b-", "a-/-b-", "-a/-b-", "-a-/-b-" };
+ char **expect_values[] = { expect_null, expect_dash, expect_slas };
+ char separator, **expect;
+ unsigned int s, i, j;
+
+ for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {
+ separator = sep[s];
+ expect = expect_values[s];
+
+ for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {
+ for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {
+ git_buf_join(&buf, separator, a[i], b[j]);
+ cl_assert_equal_s(*expect, buf.ptr);
+ expect++;
+ }
+ }
+ }
+
+ git_buf_free(&buf);
+}
+
+void test_core_buffer__10(void)
+{
+ git_buf a = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_join_n(&a, '/', 1, "test"));
+ cl_assert_equal_s(a.ptr, "test");
+ cl_git_pass(git_buf_join_n(&a, '/', 1, "string"));
+ cl_assert_equal_s(a.ptr, "test/string");
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_join_n(&a, '/', 3, "test", "string", "join"));
+ cl_assert_equal_s(a.ptr, "test/string/join");
+ cl_git_pass(git_buf_join_n(&a, '/', 2, a.ptr, "more"));
+ cl_assert_equal_s(a.ptr, "test/string/join/test/string/join/more");
+
+ git_buf_free(&a);
+}
+
+void test_core_buffer__11(void)
+{
+ git_buf a = GIT_BUF_INIT;
+ git_strarray t;
+ char *t1[] = { "nothing", "in", "common" };
+ char *t2[] = { "something", "something else", "some other" };
+ char *t3[] = { "something", "some fun", "no fun" };
+ char *t4[] = { "happy", "happier", "happiest" };
+ char *t5[] = { "happiest", "happier", "happy" };
+ char *t6[] = { "no", "nope", "" };
+ char *t7[] = { "", "doesn't matter" };
+
+ t.strings = t1;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t2;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "some");
+
+ t.strings = t3;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t4;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "happ");
+
+ t.strings = t5;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "happ");
+
+ t.strings = t6;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t7;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ git_buf_free(&a);
+}
+
+void test_core_buffer__rfind_variants(void)
+{
+ git_buf a = GIT_BUF_INIT;
+ ssize_t len;
+
+ cl_git_pass(git_buf_sets(&a, "/this/is/it/"));
+
+ len = (ssize_t)git_buf_len(&a);
+
+ cl_assert(git_buf_rfind(&a, '/') == len - 1);
+ cl_assert(git_buf_rfind_next(&a, '/') == len - 4);
+
+ cl_assert(git_buf_rfind(&a, 'i') == len - 3);
+ cl_assert(git_buf_rfind_next(&a, 'i') == len - 3);
+
+ cl_assert(git_buf_rfind(&a, 'h') == 2);
+ cl_assert(git_buf_rfind_next(&a, 'h') == 2);
+
+ cl_assert(git_buf_rfind(&a, 'q') == -1);
+ cl_assert(git_buf_rfind_next(&a, 'q') == -1);
+
+ git_buf_free(&a);
+}
+
+void test_core_buffer__puts_escaped(void)
+{
+ git_buf a = GIT_BUF_INIT;
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "", ""));
+ cl_assert_equal_s("this is a test", a.ptr);
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "t", "\\"));
+ cl_assert_equal_s("\\this is a \\tes\\t", a.ptr);
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "i ", "__"));
+ cl_assert_equal_s("th__is__ __is__ a__ test", a.ptr);
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_text_puts_escape_regex(&a, "^match\\s*[A-Z]+.*"));
+ cl_assert_equal_s("\\^match\\\\s\\*\\[A-Z\\]\\+\\.\\*", a.ptr);
+
+ git_buf_free(&a);
+}
+
+static void assert_unescape(char *expected, char *to_unescape) {
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_sets(&buf, to_unescape));
+ git_buf_text_unescape(&buf);
+ cl_assert_equal_s(expected, buf.ptr);
+ cl_assert_equal_sz(strlen(expected), buf.size);
+
+ git_buf_free(&buf);
+}
+
+void test_core_buffer__unescape(void)
+{
+ assert_unescape("Escaped\\", "Es\\ca\\ped\\");
+ assert_unescape("Es\\caped\\", "Es\\\\ca\\ped\\\\");
+ assert_unescape("\\", "\\");
+ assert_unescape("\\", "\\\\");
+ assert_unescape("", "");
+}
+
+void test_core_buffer__base64(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ /* t h i s
+ * 0x 74 68 69 73
+ * 0b 01110100 01101000 01101001 01110011
+ * 0b 011101 000110 100001 101001 011100 110000
+ * 0x 1d 06 21 29 1c 30
+ * d G h p c w
+ */
+ cl_git_pass(git_buf_put_base64(&buf, "this", 4));
+ cl_assert_equal_s("dGhpcw==", buf.ptr);
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_buf_put_base64(&buf, "this!", 5));
+ cl_assert_equal_s("dGhpcyE=", buf.ptr);
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_buf_put_base64(&buf, "this!\n", 6));
+ cl_assert_equal_s("dGhpcyEK", buf.ptr);
+
+ git_buf_free(&buf);
+}
+
+void test_core_buffer__classify_with_utf8(void)
+{
+ char *data0 = "Simple text\n";
+ size_t data0len = 12;
+ char *data1 = "Is that UTF-8 data I see…\nYep!\n";
+ size_t data1len = 31;
+ char *data2 = "Internal NUL!!!\000\n\nI see you!\n";
+ size_t data2len = 29;
+ char *data3 = "\xef\xbb\xbfThis is UTF-8 with a BOM.\n";
+ size_t data3len = 20;
+ git_buf b;
+
+ b.ptr = data0; b.size = b.asize = data0len;
+ cl_assert(!git_buf_text_is_binary(&b));
+ cl_assert(!git_buf_text_contains_nul(&b));
+
+ b.ptr = data1; b.size = b.asize = data1len;
+ cl_assert(git_buf_text_is_binary(&b));
+ cl_assert(!git_buf_text_contains_nul(&b));
+
+ b.ptr = data2; b.size = b.asize = data2len;
+ cl_assert(git_buf_text_is_binary(&b));
+ cl_assert(git_buf_text_contains_nul(&b));
+
+ b.ptr = data3; b.size = b.asize = data3len;
+ cl_assert(!git_buf_text_is_binary(&b));
+ cl_assert(!git_buf_text_contains_nul(&b));
+}
+
+#define SIMILARITY_TEST_DATA_1 \
+ "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+ "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+ "020\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \
+ "030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \
+ "040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n"
+
+void test_core_buffer__similarity_metric(void)
+{
+ git_hashsig *a, *b;
+ git_buf buf = GIT_BUF_INIT;
+ int sim;
+
+ /* in the first case, we compare data to itself and expect 100% match */
+
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ cl_assert_equal_i(100, git_hashsig_compare(a, b));
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* if we change just a single byte, how much does that change magnify? */
+
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_buf_sets(&buf,
+ "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+ "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+ "x020x\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \
+ "030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \
+ "040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n"
+ ));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ sim = git_hashsig_compare(a, b);
+
+ cl_assert_in_range(95, sim, 100); /* expect >95% similarity */
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* let's try comparing data to a superset of itself */
+
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1
+ "050\n051\n052\n053\n054\n055\n056\n057\n058\n059\n"));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ sim = git_hashsig_compare(a, b);
+ /* 20% lines added ~= 10% lines changed */
+
+ cl_assert_in_range(85, sim, 95); /* expect similarity around 90% */
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* what if we keep about half the original data and add half new */
+
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_buf_sets(&buf,
+ "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+ "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+ "020x\n021\n022\n023\n024\n" \
+ "x25\nx26\nx27\nx28\nx29\n" \
+ "x30\nx31\nx32\nx33\nx34\nx35\nx36\nx37\nx38\nx39\n" \
+ "x40\nx41\nx42\nx43\nx44\nx45\nx46\nx47\nx48\nx49\n"
+ ));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ sim = git_hashsig_compare(a, b);
+ /* 50% lines changed */
+
+ cl_assert_in_range(40, sim, 60); /* expect in the 40-60% similarity range */
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* lastly, let's check that we can hash file content as well */
+
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ cl_git_pass(git_futils_mkdir("scratch", NULL, 0755, GIT_MKDIR_PATH));
+ cl_git_mkfile("scratch/testdata", SIMILARITY_TEST_DATA_1);
+ cl_git_pass(git_hashsig_create_fromfile(
+ &b, "scratch/testdata", GIT_HASHSIG_NORMAL));
+
+ cl_assert_equal_i(100, git_hashsig_compare(a, b));
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ git_buf_free(&buf);
+ git_futils_rmdir_r("scratch", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+
+void test_core_buffer__similarity_metric_whitespace(void)
+{
+ git_hashsig *a, *b;
+ git_buf buf = GIT_BUF_INIT;
+ int sim, i, j;
+ git_hashsig_option_t opt;
+ const char *tabbed =
+ " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\n"
+ " separator = sep[s];\n"
+ " expect = expect_values[s];\n"
+ "\n"
+ " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\n"
+ " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\n"
+ " git_buf_join(&buf, separator, a[i], b[j]);\n"
+ " cl_assert_equal_s(*expect, buf.ptr);\n"
+ " expect++;\n"
+ " }\n"
+ " }\n"
+ " }\n";
+ const char *spaced =
+ " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\n"
+ " separator = sep[s];\n"
+ " expect = expect_values[s];\n"
+ "\n"
+ " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\n"
+ " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\n"
+ " git_buf_join(&buf, separator, a[i], b[j]);\n"
+ " cl_assert_equal_s(*expect, buf.ptr);\n"
+ " expect++;\n"
+ " }\n"
+ " }\n"
+ " }\n";
+ const char *crlf_spaced2 =
+ " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\r\n"
+ " separator = sep[s];\r\n"
+ " expect = expect_values[s];\r\n"
+ "\r\n"
+ " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\r\n"
+ " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\r\n"
+ " git_buf_join(&buf, separator, a[i], b[j]);\r\n"
+ " cl_assert_equal_s(*expect, buf.ptr);\r\n"
+ " expect++;\r\n"
+ " }\r\n"
+ " }\r\n"
+ " }\r\n";
+ const char *text[3] = { tabbed, spaced, crlf_spaced2 };
+
+ /* let's try variations of our own code with whitespace changes */
+
+ for (opt = GIT_HASHSIG_NORMAL; opt <= GIT_HASHSIG_SMART_WHITESPACE; ++opt) {
+ for (i = 0; i < 3; ++i) {
+ for (j = 0; j < 3; ++j) {
+ cl_git_pass(git_buf_sets(&buf, text[i]));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, opt));
+
+ cl_git_pass(git_buf_sets(&buf, text[j]));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, opt));
+
+ sim = git_hashsig_compare(a, b);
+
+ if (opt == GIT_HASHSIG_NORMAL) {
+ if (i == j)
+ cl_assert_equal_i(100, sim);
+ else
+ cl_assert_in_range(0, sim, 30); /* pretty different */
+ } else {
+ cl_assert_equal_i(100, sim);
+ }
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+ }
+ }
+ }
+
+ git_buf_free(&buf);
+}
+
+#include "../filter/crlf.h"
+
+#define check_buf(expected,buf) do { \
+ cl_assert_equal_s(expected, buf.ptr); \
+ cl_assert_equal_sz(strlen(expected), buf.size); } while (0)
+
+void test_core_buffer__lf_and_crlf_conversions(void)
+{
+ git_buf src = GIT_BUF_INIT, tgt = GIT_BUF_INIT;
+
+ /* LF source */
+
+ git_buf_sets(&src, "lf\nlf\nlf\nlf\n");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("lf\r\nlf\r\nlf\r\nlf\r\n", tgt);
+
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(src.ptr, tgt);
+
+ git_buf_sets(&src, "\nlf\nlf\nlf\nlf\nlf");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\nlf\r\nlf\r\nlf\r\nlf\r\nlf", tgt);
+
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(src.ptr, tgt);
+
+ /* CRLF source */
+
+ git_buf_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n", tgt);
+ check_buf(src.ptr, tgt);
+
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("crlf\ncrlf\ncrlf\ncrlf\n", tgt);
+
+ git_buf_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf", tgt);
+ check_buf(src.ptr, tgt);
+
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\ncrlf\ncrlf\ncrlf\ncrlf\ncrlf", tgt);
+
+ /* CRLF in LF text */
+
+ git_buf_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\nlf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\ncrlf\r\n", tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\nlf\nlf\ncrlf\nlf\nlf\ncrlf\n", tgt);
+
+ /* LF in CRLF text */
+
+ git_buf_sets(&src, "\ncrlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\ncrlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf", tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\ncrlf\ncrlf\nlf\ncrlf\ncrlf", tgt);
+
+ /* bare CR test */
+
+ git_buf_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\rcrlf\r\nlf\r\nlf\r\ncr\rcrlf\r\nlf\r\ncr\r", tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt);
+
+ git_buf_sets(&src, "\rcr\r");
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf(src.ptr, tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\rcr\r", tgt);
+
+ git_buf_free(&src);
+ git_buf_free(&tgt);
+
+ /* blob correspondence tests */
+
+ git_buf_sets(&src, ALL_CRLF_TEXT_RAW);
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf(ALL_CRLF_TEXT_AS_CRLF, tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(ALL_CRLF_TEXT_AS_LF, tgt);
+ git_buf_free(&src);
+ git_buf_free(&tgt);
+
+ git_buf_sets(&src, ALL_LF_TEXT_RAW);
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf(ALL_LF_TEXT_AS_CRLF, tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(ALL_LF_TEXT_AS_LF, tgt);
+ git_buf_free(&src);
+ git_buf_free(&tgt);
+
+ git_buf_sets(&src, MORE_CRLF_TEXT_RAW);
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf(MORE_CRLF_TEXT_AS_CRLF, tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(MORE_CRLF_TEXT_AS_LF, tgt);
+ git_buf_free(&src);
+ git_buf_free(&tgt);
+
+ git_buf_sets(&src, MORE_LF_TEXT_RAW);
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf(MORE_LF_TEXT_AS_CRLF, tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(MORE_LF_TEXT_AS_LF, tgt);
+ git_buf_free(&src);
+ git_buf_free(&tgt);
+}
diff --git a/tests/core/caps.c b/tests/core/caps.c
new file mode 100644
index 000000000..68a518ed7
--- /dev/null
+++ b/tests/core/caps.c
@@ -0,0 +1,31 @@
+#include "clar_libgit2.h"
+
+void test_core_caps__0(void)
+{
+ int major, minor, rev, caps;
+
+ git_libgit2_version(&major, &minor, &rev);
+ cl_assert_equal_i(LIBGIT2_VER_MAJOR, major);
+ cl_assert_equal_i(LIBGIT2_VER_MINOR, minor);
+ cl_assert_equal_i(LIBGIT2_VER_REVISION, rev);
+
+ caps = git_libgit2_capabilities();
+
+#ifdef GIT_THREADS
+ cl_assert((caps & GIT_CAP_THREADS) != 0);
+#else
+ cl_assert((caps & GIT_CAP_THREADS) == 0);
+#endif
+
+#if defined(GIT_SSL) || defined(GIT_WINHTTP)
+ cl_assert((caps & GIT_CAP_HTTPS) != 0);
+#else
+ cl_assert((caps & GIT_CAP_HTTPS) == 0);
+#endif
+
+#if defined(GIT_SSH)
+ cl_assert((caps & GIT_CAP_SSH) != 0);
+#else
+ cl_assert((caps & GIT_CAP_SSH) == 0);
+#endif
+}
diff --git a/tests/core/copy.c b/tests/core/copy.c
new file mode 100644
index 000000000..c0c59c056
--- /dev/null
+++ b/tests/core/copy.c
@@ -0,0 +1,126 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "path.h"
+#include "posix.h"
+
+void test_core_copy__file(void)
+{
+ struct stat st;
+ const char *content = "This is some stuff to copy\n";
+
+ cl_git_mkfile("copy_me", content);
+
+ cl_git_pass(git_futils_cp("copy_me", "copy_me_two", 0664));
+
+ cl_git_pass(git_path_lstat("copy_me_two", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert(strlen(content) == (size_t)st.st_size);
+
+ cl_git_pass(p_unlink("copy_me_two"));
+ cl_git_pass(p_unlink("copy_me"));
+}
+
+void test_core_copy__file_in_dir(void)
+{
+ struct stat st;
+ const char *content = "This is some other stuff to copy\n";
+
+ cl_git_pass(git_futils_mkdir("an_dir/in_a_dir", NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_mkfile("an_dir/in_a_dir/copy_me", content);
+ cl_assert(git_path_isdir("an_dir"));
+
+ cl_git_pass(git_futils_mkpath2file
+ ("an_dir/second_dir/and_more/copy_me_two", 0775));
+
+ cl_git_pass(git_futils_cp
+ ("an_dir/in_a_dir/copy_me",
+ "an_dir/second_dir/and_more/copy_me_two",
+ 0664));
+
+ cl_git_pass(git_path_lstat("an_dir/second_dir/and_more/copy_me_two", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert(strlen(content) == (size_t)st.st_size);
+
+ cl_git_pass(git_futils_rmdir_r("an_dir", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_path_isdir("an_dir"));
+}
+
+void test_core_copy__tree(void)
+{
+ struct stat st;
+ const char *content = "File content\n";
+
+ cl_git_pass(git_futils_mkdir("src/b", NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("src/c/d", NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("src/c/e", NULL, 0775, GIT_MKDIR_PATH));
+
+ cl_git_mkfile("src/f1", content);
+ cl_git_mkfile("src/b/f2", content);
+ cl_git_mkfile("src/c/f3", content);
+ cl_git_mkfile("src/c/d/f4", content);
+ cl_git_mkfile("src/c/d/.f5", content);
+
+#ifndef GIT_WIN32
+ cl_assert(p_symlink("../../b/f2", "src/c/d/l1") == 0);
+#endif
+
+ cl_assert(git_path_isdir("src"));
+ cl_assert(git_path_isdir("src/b"));
+ cl_assert(git_path_isdir("src/c/d"));
+ cl_assert(git_path_isfile("src/c/d/f4"));
+
+ /* copy with no empty dirs, yes links, no dotfiles, no overwrite */
+
+ cl_git_pass(
+ git_futils_cp_r("src", "t1", GIT_CPDIR_COPY_SYMLINKS, 0) );
+
+ cl_assert(git_path_isdir("t1"));
+ cl_assert(git_path_isdir("t1/b"));
+ cl_assert(git_path_isdir("t1/c"));
+ cl_assert(git_path_isdir("t1/c/d"));
+ cl_assert(!git_path_isdir("t1/c/e"));
+
+ cl_assert(git_path_isfile("t1/f1"));
+ cl_assert(git_path_isfile("t1/b/f2"));
+ cl_assert(git_path_isfile("t1/c/f3"));
+ cl_assert(git_path_isfile("t1/c/d/f4"));
+ cl_assert(!git_path_isfile("t1/c/d/.f5"));
+
+ cl_git_pass(git_path_lstat("t1/c/f3", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert(strlen(content) == (size_t)st.st_size);
+
+#ifndef GIT_WIN32
+ cl_git_pass(git_path_lstat("t1/c/d/l1", &st));
+ cl_assert(S_ISLNK(st.st_mode));
+#endif
+
+ cl_git_pass(git_futils_rmdir_r("t1", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_path_isdir("t1"));
+
+ /* copy with empty dirs, no links, yes dotfiles, no overwrite */
+
+ cl_git_pass(
+ git_futils_cp_r("src", "t2", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_COPY_DOTFILES, 0) );
+
+ cl_assert(git_path_isdir("t2"));
+ cl_assert(git_path_isdir("t2/b"));
+ cl_assert(git_path_isdir("t2/c"));
+ cl_assert(git_path_isdir("t2/c/d"));
+ cl_assert(git_path_isdir("t2/c/e"));
+
+ cl_assert(git_path_isfile("t2/f1"));
+ cl_assert(git_path_isfile("t2/b/f2"));
+ cl_assert(git_path_isfile("t2/c/f3"));
+ cl_assert(git_path_isfile("t2/c/d/f4"));
+ cl_assert(git_path_isfile("t2/c/d/.f5"));
+
+#ifndef GIT_WIN32
+ cl_git_fail(git_path_lstat("t2/c/d/l1", &st));
+#endif
+
+ cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_path_isdir("t2"));
+
+ cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_RMDIR_REMOVE_FILES));
+}
diff --git a/tests/core/dirent.c b/tests/core/dirent.c
new file mode 100644
index 000000000..f17260362
--- /dev/null
+++ b/tests/core/dirent.c
@@ -0,0 +1,236 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+
+typedef struct name_data {
+ int count; /* return count */
+ char *name; /* filename */
+} name_data;
+
+typedef struct walk_data {
+ char *sub; /* sub-directory name */
+ name_data *names; /* name state data */
+ git_buf path;
+} walk_data;
+
+
+static char *top_dir = "dir-walk";
+static walk_data *state_loc;
+
+static void setup(walk_data *d)
+{
+ name_data *n;
+
+ cl_must_pass(p_mkdir(top_dir, 0777));
+
+ cl_must_pass(p_chdir(top_dir));
+
+ if (strcmp(d->sub, ".") != 0)
+ cl_must_pass(p_mkdir(d->sub, 0777));
+
+ cl_git_pass(git_buf_sets(&d->path, d->sub));
+
+ state_loc = d;
+
+ for (n = d->names; n->name; n++) {
+ git_file fd = p_creat(n->name, 0666);
+ cl_assert(fd >= 0);
+ p_close(fd);
+ n->count = 0;
+ }
+}
+
+static void dirent_cleanup__cb(void *_d)
+{
+ walk_data *d = _d;
+ name_data *n;
+
+ for (n = d->names; n->name; n++) {
+ cl_must_pass(p_unlink(n->name));
+ }
+
+ if (strcmp(d->sub, ".") != 0)
+ cl_must_pass(p_rmdir(d->sub));
+
+ cl_must_pass(p_chdir(".."));
+
+ cl_must_pass(p_rmdir(top_dir));
+
+ git_buf_free(&d->path);
+}
+
+static void check_counts(walk_data *d)
+{
+ name_data *n;
+
+ for (n = d->names; n->name; n++) {
+ cl_assert(n->count == 1);
+ }
+}
+
+static int one_entry(void *state, git_buf *path)
+{
+ walk_data *d = (walk_data *) state;
+ name_data *n;
+
+ if (state != state_loc)
+ return GIT_ERROR;
+
+ if (path != &d->path)
+ return GIT_ERROR;
+
+ for (n = d->names; n->name; n++) {
+ if (!strcmp(n->name, path->ptr)) {
+ n->count++;
+ return 0;
+ }
+ }
+
+ return GIT_ERROR;
+}
+
+
+static name_data dot_names[] = {
+ { 0, "./a" },
+ { 0, "./asdf" },
+ { 0, "./pack-foo.pack" },
+ { 0, NULL }
+};
+static walk_data dot = {
+ ".",
+ dot_names,
+ GIT_BUF_INIT
+};
+
+/* make sure that the '.' folder is not traversed */
+void test_core_dirent__dont_traverse_dot(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &dot);
+ setup(&dot);
+
+ cl_git_pass(git_path_direach(&dot.path, 0, one_entry, &dot));
+
+ check_counts(&dot);
+}
+
+
+static name_data sub_names[] = {
+ { 0, "sub/a" },
+ { 0, "sub/asdf" },
+ { 0, "sub/pack-foo.pack" },
+ { 0, NULL }
+};
+static walk_data sub = {
+ "sub",
+ sub_names,
+ GIT_BUF_INIT
+};
+
+/* traverse a subfolder */
+void test_core_dirent__traverse_subfolder(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &sub);
+ setup(&sub);
+
+ cl_git_pass(git_path_direach(&sub.path, 0, one_entry, &sub));
+
+ check_counts(&sub);
+}
+
+
+static walk_data sub_slash = {
+ "sub/",
+ sub_names,
+ GIT_BUF_INIT
+};
+
+/* traverse a slash-terminated subfolder */
+void test_core_dirent__traverse_slash_terminated_folder(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &sub_slash);
+ setup(&sub_slash);
+
+ cl_git_pass(git_path_direach(&sub_slash.path, 0, one_entry, &sub_slash));
+
+ check_counts(&sub_slash);
+}
+
+
+static name_data empty_names[] = {
+ { 0, NULL }
+};
+static walk_data empty = {
+ "empty",
+ empty_names,
+ GIT_BUF_INIT
+};
+
+/* make sure that empty folders are not traversed */
+void test_core_dirent__dont_traverse_empty_folders(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &empty);
+ setup(&empty);
+
+ cl_git_pass(git_path_direach(&empty.path, 0, one_entry, &empty));
+
+ check_counts(&empty);
+
+ /* make sure callback not called */
+ cl_assert(git_path_is_empty_dir(empty.path.ptr));
+}
+
+static name_data odd_names[] = {
+ { 0, "odd/.a" },
+ { 0, "odd/..c" },
+ /* the following don't work on cygwin/win32 */
+ /* { 0, "odd/.b." }, */
+ /* { 0, "odd/..d.." }, */
+ { 0, NULL }
+};
+static walk_data odd = {
+ "odd",
+ odd_names,
+ GIT_BUF_INIT
+};
+
+/* make sure that strange looking filenames ('..c') are traversed */
+void test_core_dirent__traverse_weird_filenames(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &odd);
+ setup(&odd);
+
+ cl_git_pass(git_path_direach(&odd.path, 0, one_entry, &odd));
+
+ check_counts(&odd);
+}
+
+/* test filename length limits */
+void test_core_dirent__length_limits(void)
+{
+ char *big_filename = (char *)git__malloc(FILENAME_MAX + 1);
+ memset(big_filename, 'a', FILENAME_MAX + 1);
+ big_filename[FILENAME_MAX] = 0;
+
+ cl_must_fail(p_creat(big_filename, 0666));
+
+ git__free(big_filename);
+}
+
+void test_core_dirent__empty_dir(void)
+{
+ cl_must_pass(p_mkdir("empty_dir", 0777));
+ cl_assert(git_path_is_empty_dir("empty_dir"));
+
+ cl_git_mkfile("empty_dir/content", "whatever\n");
+ cl_assert(!git_path_is_empty_dir("empty_dir"));
+ cl_assert(!git_path_is_empty_dir("empty_dir/content"));
+
+ cl_must_pass(p_unlink("empty_dir/content"));
+
+ cl_must_pass(p_mkdir("empty_dir/content", 0777));
+ cl_assert(!git_path_is_empty_dir("empty_dir"));
+ cl_assert(git_path_is_empty_dir("empty_dir/content"));
+
+ cl_must_pass(p_rmdir("empty_dir/content"));
+
+ cl_must_pass(p_rmdir("empty_dir"));
+}
diff --git a/tests/core/env.c b/tests/core/env.c
new file mode 100644
index 000000000..0fa6472d7
--- /dev/null
+++ b/tests/core/env.c
@@ -0,0 +1,303 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "path.h"
+
+#ifdef GIT_WIN32
+#define NUM_VARS 5
+static const char *env_vars[NUM_VARS] = {
+ "HOME", "HOMEDRIVE", "HOMEPATH", "USERPROFILE", "PROGRAMFILES"
+};
+#else
+#define NUM_VARS 1
+static const char *env_vars[NUM_VARS] = { "HOME" };
+#endif
+
+static char *env_save[NUM_VARS];
+
+static char *home_values[] = {
+ "fake_home",
+ "f\xc3\xa1ke_h\xc3\xb5me", /* all in latin-1 supplement */
+ "f\xc4\x80ke_\xc4\xa4ome", /* latin extended */
+ "f\xce\xb1\xce\xba\xce\xb5_h\xce\xbfm\xce\xad", /* having fun with greek */
+ "fa\xe0" "\xb8" "\x87" "e_\xe0" "\xb8" "\x99" "ome", /* thai characters */
+ "f\xe1\x9cx80ke_\xe1\x9c\x91ome", /* tagalog characters */
+ "\xe1\xb8\x9f\xe1\xba\xa2" "ke_ho" "\xe1" "\xb9" "\x81" "e", /* latin extended additional */
+ "\xf0\x9f\x98\x98\xf0\x9f\x98\x82", /* emoticons */
+ NULL
+};
+
+void test_core_env__initialize(void)
+{
+ int i;
+ for (i = 0; i < NUM_VARS; ++i) {
+ const char *original = cl_getenv(env_vars[i]);
+#ifdef GIT_WIN32
+ env_save[i] = (char *)original;
+#else
+ env_save[i] = original ? git__strdup(original) : NULL;
+#endif
+ }
+}
+
+static void reset_global_search_path(void)
+{
+ cl_git_pass(git_futils_dirs_set(GIT_FUTILS_DIR_GLOBAL, NULL));
+}
+
+static void reset_system_search_path(void)
+{
+ cl_git_pass(git_futils_dirs_set(GIT_FUTILS_DIR_SYSTEM, NULL));
+}
+
+void test_core_env__cleanup(void)
+{
+ int i;
+ char **val;
+
+ for (i = 0; i < NUM_VARS; ++i) {
+ cl_setenv(env_vars[i], env_save[i]);
+ git__free(env_save[i]);
+ env_save[i] = NULL;
+ }
+
+ /* these will probably have already been cleaned up, but if a test
+ * fails, then it's probably good to try and clear out these dirs
+ */
+ for (val = home_values; *val != NULL; val++) {
+ if (**val != '\0')
+ (void)p_rmdir(*val);
+ }
+
+ /* reset search paths to default */
+ reset_global_search_path();
+ reset_system_search_path();
+}
+
+static void setenv_and_check(const char *name, const char *value)
+{
+ char *check;
+
+ cl_git_pass(cl_setenv(name, value));
+
+ check = cl_getenv(name);
+ cl_assert_equal_s(value, check);
+#ifdef GIT_WIN32
+ git__free(check);
+#endif
+}
+
+void test_core_env__0(void)
+{
+ git_buf path = GIT_BUF_INIT, found = GIT_BUF_INIT;
+ char testfile[16], tidx = '0';
+ char **val;
+ const char *testname = "testfile";
+ size_t testlen = strlen(testname);
+
+ strncpy(testfile, testname, sizeof(testfile));
+ cl_assert_equal_s(testname, testfile);
+
+ for (val = home_values; *val != NULL; val++) {
+
+ /* if we can't make the directory, let's just assume
+ * we are on a filesystem that doesn't support the
+ * characters in question and skip this test...
+ */
+ if (p_mkdir(*val, 0777) != 0) {
+ *val = ""; /* mark as not created */
+ continue;
+ }
+
+ cl_git_pass(git_path_prettify(&path, *val, NULL));
+
+ /* vary testfile name in each directory so accidentally leaving
+ * an environment variable set from a previous iteration won't
+ * accidentally make this test pass...
+ */
+ testfile[testlen] = tidx++;
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, testfile));
+ cl_git_mkfile(path.ptr, "find me");
+ git_buf_rtruncate_at_char(&path, '/');
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile));
+
+ setenv_and_check("HOME", path.ptr);
+ reset_global_search_path();
+
+ cl_git_pass(git_futils_find_global_file(&found, testfile));
+
+ cl_setenv("HOME", env_save[0]);
+ reset_global_search_path();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile));
+
+#ifdef GIT_WIN32
+ setenv_and_check("HOMEDRIVE", NULL);
+ setenv_and_check("HOMEPATH", NULL);
+ setenv_and_check("USERPROFILE", path.ptr);
+ reset_global_search_path();
+
+ cl_git_pass(git_futils_find_global_file(&found, testfile));
+
+ {
+ int root = git_path_root(path.ptr);
+ char old;
+
+ if (root >= 0) {
+ setenv_and_check("USERPROFILE", NULL);
+ reset_global_search_path();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile));
+
+ old = path.ptr[root];
+ path.ptr[root] = '\0';
+ setenv_and_check("HOMEDRIVE", path.ptr);
+ path.ptr[root] = old;
+ setenv_and_check("HOMEPATH", &path.ptr[root]);
+ reset_global_search_path();
+
+ cl_git_pass(git_futils_find_global_file(&found, testfile));
+ }
+ }
+#endif
+
+ (void)p_rmdir(*val);
+ }
+
+ git_buf_free(&path);
+ git_buf_free(&found);
+}
+
+
+void test_core_env__1(void)
+{
+ git_buf path = GIT_BUF_INIT;
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&path, "nonexistentfile"));
+
+ cl_git_pass(cl_setenv("HOME", "doesnotexist"));
+#ifdef GIT_WIN32
+ cl_git_pass(cl_setenv("HOMEPATH", "doesnotexist"));
+ cl_git_pass(cl_setenv("USERPROFILE", "doesnotexist"));
+#endif
+ reset_global_search_path();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&path, "nonexistentfile"));
+
+ cl_git_pass(cl_setenv("HOME", NULL));
+#ifdef GIT_WIN32
+ cl_git_pass(cl_setenv("HOMEPATH", NULL));
+ cl_git_pass(cl_setenv("USERPROFILE", NULL));
+#endif
+ reset_global_search_path();
+ reset_system_search_path();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&path, "nonexistentfile"));
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_system_file(&path, "nonexistentfile"));
+
+#ifdef GIT_WIN32
+ cl_git_pass(cl_setenv("PROGRAMFILES", NULL));
+ reset_system_search_path();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_system_file(&path, "nonexistentfile"));
+#endif
+
+ git_buf_free(&path);
+}
+
+static void check_global_searchpath(
+ const char *path, int position, const char *file, git_buf *temp)
+{
+ char out[GIT_PATH_MAX];
+
+ /* build and set new path */
+ if (position < 0)
+ cl_git_pass(git_buf_join(temp, GIT_PATH_LIST_SEPARATOR, path, "$PATH"));
+ else if (position > 0)
+ cl_git_pass(git_buf_join(temp, GIT_PATH_LIST_SEPARATOR, "$PATH", path));
+ else
+ cl_git_pass(git_buf_sets(temp, path));
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, temp->ptr));
+
+ /* get path and make sure $PATH expansion worked */
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, out, sizeof(out)));
+
+ if (position < 0)
+ cl_assert(git__prefixcmp(out, path) == 0);
+ else if (position > 0)
+ cl_assert(git__suffixcmp(out, path) == 0);
+ else
+ cl_assert_equal_s(out, path);
+
+ /* find file using new path */
+ cl_git_pass(git_futils_find_global_file(temp, file));
+
+ /* reset path and confirm file not found */
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(temp, file));
+}
+
+void test_core_env__2(void)
+{
+ git_buf path = GIT_BUF_INIT, found = GIT_BUF_INIT;
+ char testfile[16], tidx = '0';
+ char **val;
+ const char *testname = "alternate";
+ size_t testlen = strlen(testname);
+
+ strncpy(testfile, testname, sizeof(testfile));
+ cl_assert_equal_s(testname, testfile);
+
+ for (val = home_values; *val != NULL; val++) {
+
+ /* if we can't make the directory, let's just assume
+ * we are on a filesystem that doesn't support the
+ * characters in question and skip this test...
+ */
+ if (p_mkdir(*val, 0777) != 0 && errno != EEXIST) {
+ *val = ""; /* mark as not created */
+ continue;
+ }
+
+ cl_git_pass(git_path_prettify(&path, *val, NULL));
+
+ /* vary testfile name so any sloppiness is resetting variables or
+ * deleting files won't accidentally make a test pass.
+ */
+ testfile[testlen] = tidx++;
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, testfile));
+ cl_git_mkfile(path.ptr, "find me");
+ git_buf_rtruncate_at_char(&path, '/');
+
+ /* default should be NOTFOUND */
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile));
+
+ /* try plain, append $PATH, and prepend $PATH */
+ check_global_searchpath(path.ptr, 0, testfile, &found);
+ check_global_searchpath(path.ptr, -1, testfile, &found);
+ check_global_searchpath(path.ptr, 1, testfile, &found);
+
+ /* cleanup */
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, testfile));
+ (void)p_unlink(path.ptr);
+ (void)p_rmdir(*val);
+ }
+
+ git_buf_free(&path);
+ git_buf_free(&found);
+}
diff --git a/tests/core/errors.c b/tests/core/errors.c
new file mode 100644
index 000000000..512a4134d
--- /dev/null
+++ b/tests/core/errors.c
@@ -0,0 +1,87 @@
+#include "clar_libgit2.h"
+
+void test_core_errors__public_api(void)
+{
+ char *str_in_error;
+
+ giterr_clear();
+ cl_assert(giterr_last() == NULL);
+
+ giterr_set_oom();
+
+ cl_assert(giterr_last() != NULL);
+ cl_assert(giterr_last()->klass == GITERR_NOMEMORY);
+ str_in_error = strstr(giterr_last()->message, "memory");
+ cl_assert(str_in_error != NULL);
+
+ giterr_clear();
+
+ giterr_set_str(GITERR_REPOSITORY, "This is a test");
+
+ cl_assert(giterr_last() != NULL);
+ str_in_error = strstr(giterr_last()->message, "This is a test");
+ cl_assert(str_in_error != NULL);
+
+ giterr_clear();
+ cl_assert(giterr_last() == NULL);
+}
+
+#include "common.h"
+#include "util.h"
+#include "posix.h"
+
+void test_core_errors__new_school(void)
+{
+ char *str_in_error;
+
+ giterr_clear();
+ cl_assert(giterr_last() == NULL);
+
+ giterr_set_oom(); /* internal fn */
+
+ cl_assert(giterr_last() != NULL);
+ cl_assert(giterr_last()->klass == GITERR_NOMEMORY);
+ str_in_error = strstr(giterr_last()->message, "memory");
+ cl_assert(str_in_error != NULL);
+
+ giterr_clear();
+
+ giterr_set(GITERR_REPOSITORY, "This is a test"); /* internal fn */
+
+ cl_assert(giterr_last() != NULL);
+ str_in_error = strstr(giterr_last()->message, "This is a test");
+ cl_assert(str_in_error != NULL);
+
+ giterr_clear();
+ cl_assert(giterr_last() == NULL);
+
+ do {
+ struct stat st;
+ memset(&st, 0, sizeof(st));
+ cl_assert(p_lstat("this_file_does_not_exist", &st) < 0);
+ GIT_UNUSED(st);
+ } while (false);
+ giterr_set(GITERR_OS, "stat failed"); /* internal fn */
+
+ cl_assert(giterr_last() != NULL);
+ str_in_error = strstr(giterr_last()->message, "stat failed");
+ cl_assert(str_in_error != NULL);
+ cl_assert(git__prefixcmp(str_in_error, "stat failed: ") == 0);
+ cl_assert(strlen(str_in_error) > strlen("stat failed: "));
+
+#ifdef GIT_WIN32
+ giterr_clear();
+
+ /* The MSDN docs use this to generate a sample error */
+ cl_assert(GetProcessId(NULL) == 0);
+ giterr_set(GITERR_OS, "GetProcessId failed"); /* internal fn */
+
+ cl_assert(giterr_last() != NULL);
+ str_in_error = strstr(giterr_last()->message, "GetProcessId failed");
+ cl_assert(str_in_error != NULL);
+ cl_assert(git__prefixcmp(str_in_error, "GetProcessId failed: ") == 0);
+ cl_assert(strlen(str_in_error) > strlen("GetProcessId failed: "));
+#endif
+
+ giterr_clear();
+}
diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c
new file mode 100644
index 000000000..5a3e7510f
--- /dev/null
+++ b/tests/core/filebuf.c
@@ -0,0 +1,126 @@
+#include "clar_libgit2.h"
+#include "filebuf.h"
+
+/* make sure git_filebuf_open doesn't delete an existing lock */
+void test_core_filebuf__0(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ int fd;
+ char test[] = "test", testlock[] = "test.lock";
+
+ fd = p_creat(testlock, 0744); //-V536
+
+ cl_must_pass(fd);
+ cl_must_pass(p_close(fd));
+
+ cl_git_fail(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(git_path_exists(testlock));
+
+ cl_must_pass(p_unlink(testlock));
+}
+
+
+/* make sure GIT_FILEBUF_APPEND works as expected */
+void test_core_filebuf__1(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+
+ cl_git_mkfile(test, "libgit2 rocks\n");
+
+ cl_git_pass(git_filebuf_open(&file, test, GIT_FILEBUF_APPEND, 0666));
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ cl_assert_equal_file("libgit2 rocks\nlibgit2 rocks\n", 0, test);
+
+ cl_must_pass(p_unlink(test));
+}
+
+
+/* make sure git_filebuf_write writes large buffer correctly */
+void test_core_filebuf__2(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+ unsigned char buf[4096 * 4]; /* 2 * WRITE_BUFFER_SIZE */
+
+ memset(buf, 0xfe, sizeof(buf));
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_git_pass(git_filebuf_write(&file, buf, sizeof(buf)));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ cl_assert_equal_file((char *)buf, sizeof(buf), test);
+
+ cl_must_pass(p_unlink(test));
+}
+
+/* make sure git_filebuf_cleanup clears the buffer */
+void test_core_filebuf__4(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+
+ cl_assert(file.buffer == NULL);
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(file.buffer != NULL);
+
+ git_filebuf_cleanup(&file);
+ cl_assert(file.buffer == NULL);
+}
+
+
+/* make sure git_filebuf_commit clears the buffer */
+void test_core_filebuf__5(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+
+ cl_assert(file.buffer == NULL);
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(file.buffer != NULL);
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+ cl_assert(file.buffer != NULL);
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert(file.buffer == NULL);
+
+ cl_must_pass(p_unlink(test));
+}
+
+
+/* make sure git_filebuf_commit takes umask into account */
+void test_core_filebuf__umask(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+ struct stat statbuf;
+ mode_t mask, os_mask;
+
+#ifdef GIT_WIN32
+ os_mask = 0600;
+#else
+ os_mask = 0777;
+#endif
+
+ p_umask(mask = p_umask(0));
+
+ cl_assert(file.buffer == NULL);
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(file.buffer != NULL);
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+ cl_assert(file.buffer != NULL);
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert(file.buffer == NULL);
+
+ cl_must_pass(p_stat("test", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (0666 & ~mask) & os_mask);
+
+ cl_must_pass(p_unlink(test));
+}
+
diff --git a/tests/core/hex.c b/tests/core/hex.c
new file mode 100644
index 000000000..930af1670
--- /dev/null
+++ b/tests/core/hex.c
@@ -0,0 +1,22 @@
+#include "clar_libgit2.h"
+#include "util.h"
+
+void test_core_hex__fromhex(void)
+{
+ /* Passing cases */
+ cl_assert(git__fromhex('0') == 0x0);
+ cl_assert(git__fromhex('1') == 0x1);
+ cl_assert(git__fromhex('3') == 0x3);
+ cl_assert(git__fromhex('9') == 0x9);
+ cl_assert(git__fromhex('A') == 0xa);
+ cl_assert(git__fromhex('C') == 0xc);
+ cl_assert(git__fromhex('F') == 0xf);
+ cl_assert(git__fromhex('a') == 0xa);
+ cl_assert(git__fromhex('c') == 0xc);
+ cl_assert(git__fromhex('f') == 0xf);
+
+ /* Failing cases */
+ cl_assert(git__fromhex('g') == -1);
+ cl_assert(git__fromhex('z') == -1);
+ cl_assert(git__fromhex('X') == -1);
+}
diff --git a/tests/core/iconv.c b/tests/core/iconv.c
new file mode 100644
index 000000000..8aedab206
--- /dev/null
+++ b/tests/core/iconv.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+#include "path.h"
+
+#ifdef GIT_USE_ICONV
+static git_path_iconv_t ic;
+static char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
+static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
+#endif
+
+void test_core_iconv__initialize(void)
+{
+#ifdef GIT_USE_ICONV
+ cl_git_pass(git_path_iconv_init_precompose(&ic));
+#endif
+}
+
+void test_core_iconv__cleanup(void)
+{
+#ifdef GIT_USE_ICONV
+ git_path_iconv_clear(&ic);
+#endif
+}
+
+void test_core_iconv__unchanged(void)
+{
+#ifdef GIT_USE_ICONV
+ char *data = "Ascii data", *original = data;
+ size_t datalen = strlen(data);
+
+ cl_git_pass(git_path_iconv(&ic, &data, &datalen));
+ GIT_UNUSED(datalen);
+
+ /* There are no high bits set, so this should leave data untouched */
+ cl_assert(data == original);
+#endif
+}
+
+void test_core_iconv__decomposed_to_precomposed(void)
+{
+#ifdef GIT_USE_ICONV
+ char *data = nfd;
+ size_t datalen = strlen(nfd);
+
+ cl_git_pass(git_path_iconv(&ic, &data, &datalen));
+ GIT_UNUSED(datalen);
+
+ /* The decomposed nfd string should be transformed to the nfc form
+ * (on platforms where iconv is enabled, of course).
+ */
+ cl_assert_equal_s(nfc, data);
+#endif
+}
+
+void test_core_iconv__precomposed_is_unmodified(void)
+{
+#ifdef GIT_USE_ICONV
+ char *data = nfc;
+ size_t datalen = strlen(nfc);
+
+ cl_git_pass(git_path_iconv(&ic, &data, &datalen));
+ GIT_UNUSED(datalen);
+
+ /* data is already in precomposed form, so even though some bytes have
+ * the high-bit set, the iconv transform should result in no change.
+ */
+ cl_assert_equal_s(nfc, data);
+#endif
+}
diff --git a/tests/core/mkdir.c b/tests/core/mkdir.c
new file mode 100644
index 000000000..a8c5b10ae
--- /dev/null
+++ b/tests/core/mkdir.c
@@ -0,0 +1,188 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "path.h"
+#include "posix.h"
+
+static void cleanup_basic_dirs(void *ref)
+{
+ GIT_UNUSED(ref);
+ git_futils_rmdir_r("d0", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d1", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d2", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d3", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d4", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+}
+
+void test_core_mkdir__basic(void)
+{
+ cl_set_cleanup(cleanup_basic_dirs, NULL);
+
+ /* make a directory */
+ cl_assert(!git_path_isdir("d0"));
+ cl_git_pass(git_futils_mkdir("d0", NULL, 0755, 0));
+ cl_assert(git_path_isdir("d0"));
+
+ /* make a path */
+ cl_assert(!git_path_isdir("d1"));
+ cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", NULL, 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir("d1"));
+ cl_assert(git_path_isdir("d1/d1.1"));
+ cl_assert(git_path_isdir("d1/d1.1/d1.2"));
+
+ /* make a dir exclusively */
+ cl_assert(!git_path_isdir("d2"));
+ cl_git_pass(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
+ cl_assert(git_path_isdir("d2"));
+
+ /* make exclusive failure */
+ cl_git_fail(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
+
+ /* make a path exclusively */
+ cl_assert(!git_path_isdir("d3"));
+ cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ cl_assert(git_path_isdir("d3"));
+ cl_assert(git_path_isdir("d3/d3.1/d3.2"));
+
+ /* make exclusive path failure */
+ cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ /* ??? Should EXCL only apply to the last item in the path? */
+
+ /* path with trailing slash? */
+ cl_assert(!git_path_isdir("d4"));
+ cl_git_pass(git_futils_mkdir("d4/d4.1/", NULL, 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir("d4/d4.1"));
+}
+
+static void cleanup_basedir(void *ref)
+{
+ GIT_UNUSED(ref);
+ git_futils_rmdir_r("base", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+}
+
+void test_core_mkdir__with_base(void)
+{
+#define BASEDIR "base/dir/here"
+
+ cl_set_cleanup(cleanup_basedir, NULL);
+
+ cl_git_pass(git_futils_mkdir(BASEDIR, NULL, 0755, GIT_MKDIR_PATH));
+
+ cl_git_pass(git_futils_mkdir("a", BASEDIR, 0755, 0));
+ cl_assert(git_path_isdir(BASEDIR "/a"));
+
+ cl_git_pass(git_futils_mkdir("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir(BASEDIR "/b/b1/b2"));
+
+ /* exclusive with existing base */
+ cl_git_pass(git_futils_mkdir("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* fail: exclusive with duplicated suffix */
+ cl_git_fail(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* fail: exclusive with any duplicated component */
+ cl_git_fail(git_futils_mkdir("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* success: exclusive without path */
+ cl_git_pass(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL));
+
+ /* path with shorter base and existing dirs */
+ cl_git_pass(git_futils_mkdir("dir/here/d/", "base", 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir("base/dir/here/d"));
+
+ /* fail: path with shorter base and existing dirs */
+ cl_git_fail(git_futils_mkdir("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* fail: base with missing components */
+ cl_git_fail(git_futils_mkdir("f/", "base/missing", 0755, GIT_MKDIR_PATH));
+
+ /* success: shift missing component to path */
+ cl_git_pass(git_futils_mkdir("missing/f/", "base/", 0755, GIT_MKDIR_PATH));
+}
+
+static void cleanup_chmod_root(void *ref)
+{
+ mode_t *mode = ref;
+
+ if (*mode != 0) {
+ (void)p_umask(*mode);
+ git__free(mode);
+ }
+
+ git_futils_rmdir_r("r", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+}
+
+#define check_mode(X,A) check_mode_at_line((X), (A), __FILE__, __LINE__)
+
+static void check_mode_at_line(
+ mode_t expected, mode_t actual, const char *file, int line)
+{
+ /* FAT filesystems don't support exec bit, nor group/world bits */
+ if (!cl_is_chmod_supported()) {
+ expected &= 0600;
+ actual &= 0600;
+ }
+
+ clar__assert_equal(
+ file, line, "expected_mode != actual_mode", 1,
+ "%07o", (int)expected, (int)(actual & 0777));
+}
+
+void test_core_mkdir__chmods(void)
+{
+ struct stat st;
+ mode_t *old = git__malloc(sizeof(mode_t));
+ *old = p_umask(022);
+
+ cl_set_cleanup(cleanup_chmod_root, old);
+
+ cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0));
+
+ cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH));
+
+ cl_git_pass(git_path_lstat("r/mode", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is/important", &st));
+ check_mode(0755, st.st_mode);
+
+ cl_git_pass(git_futils_mkdir("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
+
+ cl_git_pass(git_path_lstat("r/mode2", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2/important2", &st));
+ check_mode(0777, st.st_mode);
+
+ cl_git_pass(git_futils_mkdir("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
+
+ cl_git_pass(git_path_lstat("r/mode3", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode3/is3", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode3/is3/important3", &st));
+ check_mode(0777, st.st_mode);
+
+ /* test that we chmod existing dir */
+
+ cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
+
+ cl_git_pass(git_path_lstat("r/mode", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is/important", &st));
+ check_mode(0777, st.st_mode);
+
+ /* test that we chmod even existing dirs if CHMOD_PATH is set */
+
+ cl_git_pass(git_futils_mkdir("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
+
+ cl_git_pass(git_path_lstat("r/mode2", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2/important2.1", &st));
+ check_mode(0777, st.st_mode);
+}
diff --git a/tests/core/oid.c b/tests/core/oid.c
new file mode 100644
index 000000000..7ee6fb67d
--- /dev/null
+++ b/tests/core/oid.c
@@ -0,0 +1,70 @@
+#include "clar_libgit2.h"
+
+static git_oid id;
+static git_oid idp;
+static git_oid idm;
+const char *str_oid = "ae90f12eea699729ed24555e40b9fd669da12a12";
+const char *str_oid_p = "ae90f12eea699729ed";
+const char *str_oid_m = "ae90f12eea699729ed24555e40b9fd669da12a12THIS IS EXTRA TEXT THAT SHOULD GET IGNORED";
+
+void test_core_oid__initialize(void)
+{
+ cl_git_pass(git_oid_fromstr(&id, str_oid));
+ cl_git_pass(git_oid_fromstrp(&idp, str_oid_p));
+ cl_git_fail(git_oid_fromstrp(&idm, str_oid_m));
+}
+
+void test_core_oid__streq(void)
+{
+ cl_assert_equal_i(0, git_oid_streq(&id, str_oid));
+ cl_assert_equal_i(-1, git_oid_streq(&id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+
+ cl_assert_equal_i(-1, git_oid_streq(&id, "deadbeef"));
+ cl_assert_equal_i(-1, git_oid_streq(&id, "I'm not an oid.... :)"));
+
+ cl_assert_equal_i(0, git_oid_streq(&idp, "ae90f12eea699729ed0000000000000000000000"));
+ cl_assert_equal_i(0, git_oid_streq(&idp, "ae90f12eea699729ed"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp, "ae90f12eea699729ed1"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp, "ae90f12eea699729ec"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+
+ cl_assert_equal_i(-1, git_oid_streq(&idp, "deadbeef"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp, "I'm not an oid.... :)"));
+}
+
+void test_core_oid__strcmp(void)
+{
+ cl_assert_equal_i(0, git_oid_strcmp(&id, str_oid));
+ cl_assert(git_oid_strcmp(&id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0);
+
+ cl_assert(git_oid_strcmp(&id, "deadbeef") < 0);
+ cl_assert_equal_i(-1, git_oid_strcmp(&id, "I'm not an oid.... :)"));
+
+ cl_assert_equal_i(0, git_oid_strcmp(&idp, "ae90f12eea699729ed0000000000000000000000"));
+ cl_assert_equal_i(0, git_oid_strcmp(&idp, "ae90f12eea699729ed"));
+ cl_assert(git_oid_strcmp(&idp, "ae90f12eea699729ed1") < 0);
+ cl_assert(git_oid_strcmp(&idp, "ae90f12eea699729ec") > 0);
+ cl_assert(git_oid_strcmp(&idp, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0);
+
+ cl_assert(git_oid_strcmp(&idp, "deadbeef") < 0);
+ cl_assert_equal_i(-1, git_oid_strcmp(&idp, "I'm not an oid.... :)"));
+}
+
+void test_core_oid__ncmp(void)
+{
+ cl_assert(!git_oid_ncmp(&id, &idp, 0));
+ cl_assert(!git_oid_ncmp(&id, &idp, 1));
+ cl_assert(!git_oid_ncmp(&id, &idp, 2));
+ cl_assert(!git_oid_ncmp(&id, &idp, 17));
+ cl_assert(!git_oid_ncmp(&id, &idp, 18));
+ cl_assert(git_oid_ncmp(&id, &idp, 19));
+ cl_assert(git_oid_ncmp(&id, &idp, 40));
+ cl_assert(git_oid_ncmp(&id, &idp, 41));
+ cl_assert(git_oid_ncmp(&id, &idp, 42));
+
+ cl_assert(!git_oid_ncmp(&id, &id, 0));
+ cl_assert(!git_oid_ncmp(&id, &id, 1));
+ cl_assert(!git_oid_ncmp(&id, &id, 39));
+ cl_assert(!git_oid_ncmp(&id, &id, 40));
+ cl_assert(!git_oid_ncmp(&id, &id, 41));
+}
diff --git a/tests/core/oidmap.c b/tests/core/oidmap.c
new file mode 100644
index 000000000..ec4b5e775
--- /dev/null
+++ b/tests/core/oidmap.c
@@ -0,0 +1,110 @@
+#include "clar_libgit2.h"
+#include "oidmap.h"
+
+GIT__USE_OIDMAP;
+
+typedef struct {
+ git_oid oid;
+ size_t extra;
+} oidmap_item;
+
+#define NITEMS 0x0fff
+
+void test_core_oidmap__basic(void)
+{
+ git_oidmap *map;
+ oidmap_item items[NITEMS];
+ uint32_t i, j;
+
+ for (i = 0; i < NITEMS; ++i) {
+ items[i].extra = i;
+ for (j = 0; j < GIT_OID_RAWSZ / 4; ++j) {
+ items[i].oid.id[j * 4 ] = (unsigned char)i;
+ items[i].oid.id[j * 4 + 1] = (unsigned char)(i >> 8);
+ items[i].oid.id[j * 4 + 2] = (unsigned char)(i >> 16);
+ items[i].oid.id[j * 4 + 3] = (unsigned char)(i >> 24);
+ }
+ }
+
+ map = git_oidmap_alloc();
+ cl_assert(map != NULL);
+
+ for (i = 0; i < NITEMS; ++i) {
+ khiter_t pos;
+ int ret;
+
+ pos = kh_get(oid, map, &items[i].oid);
+ cl_assert(pos == kh_end(map));
+
+ pos = kh_put(oid, map, &items[i].oid, &ret);
+ cl_assert(ret != 0);
+
+ kh_val(map, pos) = &items[i];
+ }
+
+
+ for (i = 0; i < NITEMS; ++i) {
+ khiter_t pos;
+
+ pos = kh_get(oid, map, &items[i].oid);
+ cl_assert(pos != kh_end(map));
+
+ cl_assert_equal_p(kh_val(map, pos), &items[i]);
+ }
+
+ git_oidmap_free(map);
+}
+
+void test_core_oidmap__hash_collision(void)
+{
+ git_oidmap *map;
+ oidmap_item items[NITEMS];
+ uint32_t i, j;
+
+ for (i = 0; i < NITEMS; ++i) {
+ uint32_t segment = i / 8;
+ int modi = i - (segment * 8);
+
+ items[i].extra = i;
+
+ for (j = 0; j < GIT_OID_RAWSZ / 4; ++j) {
+ items[i].oid.id[j * 4 ] = (unsigned char)modi;
+ items[i].oid.id[j * 4 + 1] = (unsigned char)(modi >> 8);
+ items[i].oid.id[j * 4 + 2] = (unsigned char)(modi >> 16);
+ items[i].oid.id[j * 4 + 3] = (unsigned char)(modi >> 24);
+ }
+
+ items[i].oid.id[ 8] = (unsigned char)i;
+ items[i].oid.id[ 9] = (unsigned char)(i >> 8);
+ items[i].oid.id[10] = (unsigned char)(i >> 16);
+ items[i].oid.id[11] = (unsigned char)(i >> 24);
+ }
+
+ map = git_oidmap_alloc();
+ cl_assert(map != NULL);
+
+ for (i = 0; i < NITEMS; ++i) {
+ khiter_t pos;
+ int ret;
+
+ pos = kh_get(oid, map, &items[i].oid);
+ cl_assert(pos == kh_end(map));
+
+ pos = kh_put(oid, map, &items[i].oid, &ret);
+ cl_assert(ret != 0);
+
+ kh_val(map, pos) = &items[i];
+ }
+
+
+ for (i = 0; i < NITEMS; ++i) {
+ khiter_t pos;
+
+ pos = kh_get(oid, map, &items[i].oid);
+ cl_assert(pos != kh_end(map));
+
+ cl_assert_equal_p(kh_val(map, pos), &items[i]);
+ }
+
+ git_oidmap_free(map);
+}
diff --git a/tests/core/opts.c b/tests/core/opts.c
new file mode 100644
index 000000000..3173c648b
--- /dev/null
+++ b/tests/core/opts.c
@@ -0,0 +1,19 @@
+#include "clar_libgit2.h"
+#include "cache.h"
+
+void test_core_opts__readwrite(void)
+{
+ size_t old_val = 0;
+ size_t new_val = 0;
+
+ git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &old_val);
+ git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, (size_t)1234);
+ git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &new_val);
+
+ cl_assert(new_val == 1234);
+
+ git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, old_val);
+ git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &new_val);
+
+ cl_assert(new_val == old_val);
+}
diff --git a/tests/core/path.c b/tests/core/path.c
new file mode 100644
index 000000000..cf2d5e944
--- /dev/null
+++ b/tests/core/path.c
@@ -0,0 +1,583 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+
+static void
+check_dirname(const char *A, const char *B)
+{
+ git_buf dir = GIT_BUF_INIT;
+ char *dir2;
+
+ cl_assert(git_path_dirname_r(&dir, A) >= 0);
+ cl_assert_equal_s(B, dir.ptr);
+ git_buf_free(&dir);
+
+ cl_assert((dir2 = git_path_dirname(A)) != NULL);
+ cl_assert_equal_s(B, dir2);
+ git__free(dir2);
+}
+
+static void
+check_basename(const char *A, const char *B)
+{
+ git_buf base = GIT_BUF_INIT;
+ char *base2;
+
+ cl_assert(git_path_basename_r(&base, A) >= 0);
+ cl_assert_equal_s(B, base.ptr);
+ git_buf_free(&base);
+
+ cl_assert((base2 = git_path_basename(A)) != NULL);
+ cl_assert_equal_s(B, base2);
+ git__free(base2);
+}
+
+static void
+check_topdir(const char *A, const char *B)
+{
+ const char *dir;
+
+ cl_assert((dir = git_path_topdir(A)) != NULL);
+ cl_assert_equal_s(B, dir);
+}
+
+static void
+check_joinpath(const char *path_a, const char *path_b, const char *expected_path)
+{
+ git_buf joined_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&joined_path, path_a, path_b));
+ cl_assert_equal_s(expected_path, joined_path.ptr);
+
+ git_buf_free(&joined_path);
+}
+
+static void
+check_joinpath_n(
+ const char *path_a,
+ const char *path_b,
+ const char *path_c,
+ const char *path_d,
+ const char *expected_path)
+{
+ git_buf joined_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_join_n(&joined_path, '/', 4,
+ path_a, path_b, path_c, path_d));
+ cl_assert_equal_s(expected_path, joined_path.ptr);
+
+ git_buf_free(&joined_path);
+}
+
+
+/* get the dirname of a path */
+void test_core_path__00_dirname(void)
+{
+ check_dirname(NULL, ".");
+ check_dirname("", ".");
+ check_dirname("a", ".");
+ check_dirname("/", "/");
+ check_dirname("/usr", "/");
+ check_dirname("/usr/", "/");
+ check_dirname("/usr/lib", "/usr");
+ check_dirname("/usr/lib/", "/usr");
+ check_dirname("/usr/lib//", "/usr");
+ check_dirname("usr/lib", "usr");
+ check_dirname("usr/lib/", "usr");
+ check_dirname("usr/lib//", "usr");
+ check_dirname(".git/", ".");
+
+ check_dirname(REP16("/abc"), REP15("/abc"));
+
+#ifdef GIT_WIN32
+ check_dirname("C:/path/", "C:/");
+ check_dirname("C:/path", "C:/");
+ check_dirname("//computername/path/", "//computername/");
+ check_dirname("//computername/path", "//computername/");
+ check_dirname("//computername/sub/path/", "//computername/sub");
+ check_dirname("//computername/sub/path", "//computername/sub");
+#endif
+}
+
+/* get the base name of a path */
+void test_core_path__01_basename(void)
+{
+ check_basename(NULL, ".");
+ check_basename("", ".");
+ check_basename("a", "a");
+ check_basename("/", "/");
+ check_basename("/usr", "usr");
+ check_basename("/usr/", "usr");
+ check_basename("/usr/lib", "lib");
+ check_basename("/usr/lib//", "lib");
+ check_basename("usr/lib", "lib");
+
+ check_basename(REP16("/abc"), "abc");
+ check_basename(REP1024("/abc"), "abc");
+}
+
+/* get the latest component in a path */
+void test_core_path__02_topdir(void)
+{
+ check_topdir(".git/", ".git/");
+ check_topdir("/.git/", ".git/");
+ check_topdir("usr/local/.git/", ".git/");
+ check_topdir("./.git/", ".git/");
+ check_topdir("/usr/.git/", ".git/");
+ check_topdir("/", "/");
+ check_topdir("a/", "a/");
+
+ cl_assert(git_path_topdir("/usr/.git") == NULL);
+ cl_assert(git_path_topdir(".") == NULL);
+ cl_assert(git_path_topdir("") == NULL);
+ cl_assert(git_path_topdir("a") == NULL);
+}
+
+/* properly join path components */
+void test_core_path__05_joins(void)
+{
+ check_joinpath("", "", "");
+ check_joinpath("", "a", "a");
+ check_joinpath("", "/a", "/a");
+ check_joinpath("a", "", "a/");
+ check_joinpath("a", "/", "a/");
+ check_joinpath("a", "b", "a/b");
+ check_joinpath("/", "a", "/a");
+ check_joinpath("/", "", "/");
+ check_joinpath("/a", "/b", "/a/b");
+ check_joinpath("/a", "/b/", "/a/b/");
+ check_joinpath("/a/", "b/", "/a/b/");
+ check_joinpath("/a/", "/b/", "/a/b/");
+
+ check_joinpath("/abcd", "/defg", "/abcd/defg");
+ check_joinpath("/abcd", "/defg/", "/abcd/defg/");
+ check_joinpath("/abcd/", "defg/", "/abcd/defg/");
+ check_joinpath("/abcd/", "/defg/", "/abcd/defg/");
+
+ check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678");
+ check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/");
+ check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/");
+
+ check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/");
+ check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/"));
+ check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/");
+
+ check_joinpath(REP1024("aaaa"), REP1024("bbbb"),
+ REP1024("aaaa") "/" REP1024("bbbb"));
+ check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"),
+ REP1024("/aaaa") REP1024("/bbbb"));
+}
+
+/* properly join path components for more than one path */
+void test_core_path__06_long_joins(void)
+{
+ check_joinpath_n("", "", "", "", "");
+ check_joinpath_n("", "a", "", "", "a/");
+ check_joinpath_n("a", "", "", "", "a/");
+ check_joinpath_n("", "", "", "a", "a");
+ check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/");
+ check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d");
+ check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop");
+ check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/");
+ check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/");
+
+ check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"),
+ REP1024("a") "/" REP1024("b") "/"
+ REP1024("c") "/" REP1024("d"));
+ check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"),
+ REP1024("/a") REP1024("/b")
+ REP1024("/c") REP1024("/d"));
+}
+
+
+static void
+check_path_to_dir(
+ const char* path,
+ const char* expected)
+{
+ git_buf tgt = GIT_BUF_INIT;
+
+ git_buf_sets(&tgt, path);
+ cl_git_pass(git_path_to_dir(&tgt));
+ cl_assert_equal_s(expected, tgt.ptr);
+
+ git_buf_free(&tgt);
+}
+
+static void
+check_string_to_dir(
+ const char* path,
+ size_t maxlen,
+ const char* expected)
+{
+ size_t len = strlen(path);
+ char *buf = git__malloc(len + 2);
+ cl_assert(buf);
+
+ strncpy(buf, path, len + 2);
+
+ git_path_string_to_dir(buf, maxlen);
+
+ cl_assert_equal_s(expected, buf);
+
+ git__free(buf);
+}
+
+/* convert paths to dirs */
+void test_core_path__07_path_to_dir(void)
+{
+ check_path_to_dir("", "");
+ check_path_to_dir(".", "./");
+ check_path_to_dir("./", "./");
+ check_path_to_dir("a/", "a/");
+ check_path_to_dir("ab", "ab/");
+ /* make sure we try just under and just over an expansion that will
+ * require a realloc
+ */
+ check_path_to_dir("abcdef", "abcdef/");
+ check_path_to_dir("abcdefg", "abcdefg/");
+ check_path_to_dir("abcdefgh", "abcdefgh/");
+ check_path_to_dir("abcdefghi", "abcdefghi/");
+ check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/");
+ check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/");
+
+ check_string_to_dir("", 1, "");
+ check_string_to_dir(".", 1, ".");
+ check_string_to_dir(".", 2, "./");
+ check_string_to_dir(".", 3, "./");
+ check_string_to_dir("abcd", 3, "abcd");
+ check_string_to_dir("abcd", 4, "abcd");
+ check_string_to_dir("abcd", 5, "abcd/");
+ check_string_to_dir("abcd", 6, "abcd/");
+}
+
+/* join path to itself */
+void test_core_path__08_self_join(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ size_t asize = 0;
+
+ asize = path.asize;
+ cl_git_pass(git_buf_sets(&path, "/foo"));
+ cl_assert_equal_s(path.ptr, "/foo");
+ cl_assert(asize < path.asize);
+
+ asize = path.asize;
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, "this is a new string"));
+ cl_assert_equal_s(path.ptr, "/foo/this is a new string");
+ cl_assert(asize < path.asize);
+
+ asize = path.asize;
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer"));
+ cl_assert_equal_s(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer");
+ cl_assert(asize < path.asize);
+
+ git_buf_free(&path);
+ cl_git_pass(git_buf_sets(&path, "/foo/bar"));
+
+ cl_git_pass(git_buf_joinpath(&path, path.ptr + 4, "baz"));
+ cl_assert_equal_s(path.ptr, "/bar/baz");
+
+ asize = path.asize;
+ cl_git_pass(git_buf_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc"));
+ cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc");
+ cl_assert(asize < path.asize);
+
+ git_buf_free(&path);
+}
+
+static void check_percent_decoding(const char *expected_result, const char *input)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git__percent_decode(&buf, input));
+ cl_assert_equal_s(expected_result, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+void test_core_path__09_percent_decode(void)
+{
+ check_percent_decoding("abcd", "abcd");
+ check_percent_decoding("a2%", "a2%");
+ check_percent_decoding("a2%3", "a2%3");
+ check_percent_decoding("a2%%3", "a2%%3");
+ check_percent_decoding("a2%3z", "a2%3z");
+ check_percent_decoding("a,", "a%2c");
+ check_percent_decoding("a21", "a2%31");
+ check_percent_decoding("a2%1", "a2%%31");
+ check_percent_decoding("a bc ", "a%20bc%20");
+ check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED");
+}
+
+static void check_fromurl(const char *expected_result, const char *input, int should_fail)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ assert(should_fail || expected_result);
+
+ if (!should_fail) {
+ cl_git_pass(git_path_fromurl(&buf, input));
+ cl_assert_equal_s(expected_result, git_buf_cstr(&buf));
+ } else
+ cl_git_fail(git_path_fromurl(&buf, input));
+
+ git_buf_free(&buf);
+}
+
+#ifdef GIT_WIN32
+#define ABS_PATH_MARKER ""
+#else
+#define ABS_PATH_MARKER "/"
+#endif
+
+void test_core_path__10_fromurl(void)
+{
+ /* Failing cases */
+ check_fromurl(NULL, "a", 1);
+ check_fromurl(NULL, "http:///c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file://c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file:////c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file:///", 1);
+ check_fromurl(NULL, "file:////", 1);
+ check_fromurl(NULL, "file://servername/c:/Temp%20folder/note.txt", 1);
+
+ /* Passing cases */
+ check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file:///c:/Temp%20folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file://localhost/c:/Temp%20folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "c:/Temp+folder/note.txt", "file:///c:/Temp+folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0);
+}
+
+typedef struct {
+ int expect_idx;
+ char **expect;
+} check_walkup_info;
+
+static int check_one_walkup_step(void *ref, git_buf *path)
+{
+ check_walkup_info *info = (check_walkup_info *)ref;
+ cl_assert(info->expect[info->expect_idx] != NULL);
+ cl_assert_equal_s(info->expect[info->expect_idx], path->ptr);
+ info->expect_idx++;
+ return 0;
+}
+
+void test_core_path__11_walkup(void)
+{
+ git_buf p = GIT_BUF_INIT;
+ char *expect[] = {
+ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
+ "this is a path", NULL,
+ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL,
+ NULL
+ };
+ char *root[] = { NULL, NULL, "/", "", "/a/b", "/a/b/", NULL, NULL, NULL };
+ int i, j;
+ check_walkup_info info;
+
+ info.expect = expect;
+
+ for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
+
+ git_buf_sets(&p, expect[i]);
+
+ info.expect_idx = i;
+ cl_git_pass(
+ git_path_walk_up(&p, root[j], check_one_walkup_step, &info)
+ );
+
+ cl_assert_equal_s(p.ptr, expect[i]);
+
+ /* skip to next run of expectations */
+ while (expect[i] != NULL) i++;
+ }
+
+ git_buf_free(&p);
+}
+
+void test_core_path__12_offset_to_path_root(void)
+{
+ cl_assert(git_path_root("non/rooted/path") == -1);
+ cl_assert(git_path_root("/rooted/path") == 0);
+
+#ifdef GIT_WIN32
+ /* Windows specific tests */
+ cl_assert(git_path_root("C:non/rooted/path") == -1);
+ cl_assert(git_path_root("C:/rooted/path") == 2);
+ cl_assert(git_path_root("//computername/sharefolder/resource") == 14);
+ cl_assert(git_path_root("//computername/sharefolder") == 14);
+ cl_assert(git_path_root("//computername") == -1);
+#endif
+}
+
+#define NON_EXISTING_FILEPATH "i_hope_i_do_not_exist"
+
+void test_core_path__13_cannot_prettify_a_non_existing_file(void)
+{
+ git_buf p = GIT_BUF_INIT;
+
+ cl_must_pass(git_path_exists(NON_EXISTING_FILEPATH) == false);
+ cl_assert_equal_i(GIT_ENOTFOUND, git_path_prettify(&p, NON_EXISTING_FILEPATH, NULL));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL));
+
+ git_buf_free(&p);
+}
+
+void test_core_path__14_apply_relative(void)
+{
+ git_buf p = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_sets(&p, "/this/is/a/base"));
+
+ cl_git_pass(git_path_apply_relative(&p, "../test"));
+ cl_assert_equal_s("/this/is/a/test", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../../the/./end"));
+ cl_assert_equal_s("/this/is/the/end", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "./of/this/../the/string"));
+ cl_assert_equal_s("/this/is/the/end/of/the/string", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../../../../../.."));
+ cl_assert_equal_s("/this/", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../"));
+ cl_assert_equal_s("/", p.ptr);
+
+ cl_git_fail(git_path_apply_relative(&p, "../../.."));
+
+
+ cl_git_pass(git_buf_sets(&p, "d:/another/test"));
+
+ cl_git_pass(git_path_apply_relative(&p, "../.."));
+ cl_assert_equal_s("d:/", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "from/here/to/../and/./back/."));
+ cl_assert_equal_s("d:/from/here/and/back/", p.ptr);
+
+
+ cl_git_pass(git_buf_sets(&p, "https://my.url.com/test.git"));
+
+ cl_git_pass(git_path_apply_relative(&p, "../another.git"));
+ cl_assert_equal_s("https://my.url.com/another.git", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../full/path/url.patch"));
+ cl_assert_equal_s("https://my.url.com/full/path/url.patch", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, ".."));
+ cl_assert_equal_s("https://my.url.com/full/path/", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../../../"));
+ cl_assert_equal_s("https://", p.ptr);
+
+
+ cl_git_pass(git_buf_sets(&p, "../../this/is/relative"));
+
+ cl_git_pass(git_path_apply_relative(&p, "../../preserves/the/prefix"));
+ cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../../../../that"));
+ cl_assert_equal_s("../../that", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../there"));
+ cl_assert_equal_s("../../there", p.ptr);
+ git_buf_free(&p);
+}
+
+static void assert_resolve_relative(
+ git_buf *buf, const char *expected, const char *path)
+{
+ cl_git_pass(git_buf_sets(buf, path));
+ cl_git_pass(git_path_resolve_relative(buf, 0));
+ cl_assert_equal_s(expected, buf->ptr);
+}
+
+void test_core_path__15_resolve_relative(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ assert_resolve_relative(&buf, "", "");
+ assert_resolve_relative(&buf, "", ".");
+ assert_resolve_relative(&buf, "", "./");
+ assert_resolve_relative(&buf, "..", "..");
+ assert_resolve_relative(&buf, "../", "../");
+ assert_resolve_relative(&buf, "..", "./..");
+ assert_resolve_relative(&buf, "../", "./../");
+ assert_resolve_relative(&buf, "../", "../.");
+ assert_resolve_relative(&buf, "../", ".././");
+ assert_resolve_relative(&buf, "../..", "../..");
+ assert_resolve_relative(&buf, "../../", "../../");
+
+ assert_resolve_relative(&buf, "/", "/");
+ assert_resolve_relative(&buf, "/", "/.");
+
+ assert_resolve_relative(&buf, "", "a/..");
+ assert_resolve_relative(&buf, "", "a/../");
+ assert_resolve_relative(&buf, "", "a/../.");
+
+ assert_resolve_relative(&buf, "/a", "/a");
+ assert_resolve_relative(&buf, "/a/", "/a/.");
+ assert_resolve_relative(&buf, "/", "/a/../");
+ assert_resolve_relative(&buf, "/", "/a/../.");
+ assert_resolve_relative(&buf, "/", "/a/.././");
+
+ assert_resolve_relative(&buf, "a", "a");
+ assert_resolve_relative(&buf, "a/", "a/");
+ assert_resolve_relative(&buf, "a/", "a/.");
+ assert_resolve_relative(&buf, "a/", "a/./");
+
+ assert_resolve_relative(&buf, "a/b", "a//b");
+ assert_resolve_relative(&buf, "a/b/c", "a/b/c");
+ assert_resolve_relative(&buf, "b/c", "./b/c");
+ assert_resolve_relative(&buf, "a/c", "a/./c");
+ assert_resolve_relative(&buf, "a/b/", "a/b/.");
+
+ assert_resolve_relative(&buf, "/a/b/c", "///a/b/c");
+ assert_resolve_relative(&buf, "/", "////");
+ assert_resolve_relative(&buf, "/a", "///a");
+ assert_resolve_relative(&buf, "/", "///.");
+ assert_resolve_relative(&buf, "/", "///a/..");
+
+ assert_resolve_relative(&buf, "../../path", "../../test//../././path");
+ assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d");
+
+ cl_git_pass(git_buf_sets(&buf, "/.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/./.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/.//.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/../."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/../.././../a"));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "////.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ /* things that start with Windows network paths */
+#ifdef GIT_WIN32
+ assert_resolve_relative(&buf, "//a/b/c", "//a/b/c");
+ assert_resolve_relative(&buf, "//a/", "//a/b/..");
+ assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c");
+
+ cl_git_pass(git_buf_sets(&buf, "//a/b/../.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+#else
+ assert_resolve_relative(&buf, "/a/b/c", "//a/b/c");
+ assert_resolve_relative(&buf, "/a/", "//a/b/..");
+ assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c");
+ assert_resolve_relative(&buf, "/", "//a/b/../..");
+#endif
+
+ git_buf_free(&buf);
+}
diff --git a/tests/core/pool.c b/tests/core/pool.c
new file mode 100644
index 000000000..3073c4a45
--- /dev/null
+++ b/tests/core/pool.c
@@ -0,0 +1,145 @@
+#include "clar_libgit2.h"
+#include "pool.h"
+#include "git2/oid.h"
+
+void test_core_pool__0(void)
+{
+ int i;
+ git_pool p;
+ void *ptr;
+
+ cl_git_pass(git_pool_init(&p, 1, 4000));
+
+ for (i = 1; i < 10000; i *= 2) {
+ ptr = git_pool_malloc(&p, i);
+ cl_assert(ptr != NULL);
+ cl_assert(git_pool__ptr_in_pool(&p, ptr));
+ cl_assert(!git_pool__ptr_in_pool(&p, &i));
+ }
+
+ /* 1+2+4+8+16+32+64+128+256+512+1024 -> original block */
+ /* 2048 -> 1 block */
+ /* 4096 -> 1 block */
+ /* 8192 -> 1 block */
+
+ cl_assert(git_pool__open_pages(&p) + git_pool__full_pages(&p) == 4);
+
+ git_pool_clear(&p);
+}
+
+void test_core_pool__1(void)
+{
+ int i;
+ git_pool p;
+
+ cl_git_pass(git_pool_init(&p, 1, 4000));
+
+ for (i = 2010; i > 0; i--)
+ cl_assert(git_pool_malloc(&p, i) != NULL);
+
+ /* with fixed page size, allocation must end up with these values */
+ cl_assert(git_pool__open_pages(&p) == 1);
+ cl_assert(git_pool__full_pages(&p) == 505);
+
+ git_pool_clear(&p);
+
+ cl_git_pass(git_pool_init(&p, 1, 4100));
+
+ for (i = 2010; i > 0; i--)
+ cl_assert(git_pool_malloc(&p, i) != NULL);
+
+ /* with fixed page size, allocation must end up with these values */
+ cl_assert(git_pool__open_pages(&p) == 1);
+ cl_assert(git_pool__full_pages(&p) == 492);
+
+ git_pool_clear(&p);
+}
+
+static char to_hex[] = "0123456789abcdef";
+
+void test_core_pool__2(void)
+{
+ git_pool p;
+ char oid_hex[GIT_OID_HEXSZ];
+ git_oid *oid;
+ int i, j;
+
+ memset(oid_hex, '0', sizeof(oid_hex));
+
+ cl_git_pass(git_pool_init(&p, sizeof(git_oid), 100));
+
+ for (i = 1000; i < 10000; i++) {
+ oid = git_pool_malloc(&p, 1);
+ cl_assert(oid != NULL);
+
+ for (j = 0; j < 8; j++)
+ oid_hex[j] = to_hex[(i >> (4 * j)) & 0x0f];
+ cl_git_pass(git_oid_fromstr(oid, oid_hex));
+ }
+
+ /* with fixed page size, allocation must end up with these values */
+ cl_assert(git_pool__open_pages(&p) == 0);
+ cl_assert(git_pool__full_pages(&p) == 90);
+
+ git_pool_clear(&p);
+}
+
+void test_core_pool__free_list(void)
+{
+ int i;
+ git_pool p;
+ void *ptr, *ptrs[50];
+
+ cl_git_pass(git_pool_init(&p, 100, 100));
+
+ for (i = 0; i < 10; ++i) {
+ ptr = git_pool_malloc(&p, 1);
+ cl_assert(ptr != NULL);
+ }
+ cl_assert_equal_i(10, (int)p.items);
+
+ for (i = 0; i < 50; ++i) {
+ ptrs[i] = git_pool_malloc(&p, 1);
+ cl_assert(ptrs[i] != NULL);
+ }
+ cl_assert_equal_i(60, (int)p.items);
+
+ git_pool_free(&p, ptr);
+ cl_assert_equal_i(60, (int)p.items);
+
+ git_pool_free_array(&p, 50, ptrs);
+ cl_assert_equal_i(60, (int)p.items);
+
+ for (i = 0; i < 50; ++i) {
+ ptrs[i] = git_pool_malloc(&p, 1);
+ cl_assert(ptrs[i] != NULL);
+ }
+ cl_assert_equal_i(60, (int)p.items);
+
+ for (i = 0; i < 111; ++i) {
+ ptr = git_pool_malloc(&p, 1);
+ cl_assert(ptr != NULL);
+ }
+ cl_assert_equal_i(170, (int)p.items);
+
+ git_pool_free_array(&p, 50, ptrs);
+ cl_assert_equal_i(170, (int)p.items);
+
+ for (i = 0; i < 50; ++i) {
+ ptrs[i] = git_pool_malloc(&p, 1);
+ cl_assert(ptrs[i] != NULL);
+ }
+ cl_assert_equal_i(170, (int)p.items);
+
+ git_pool_clear(&p);
+}
+
+void test_core_pool__strndup_limit(void)
+{
+ git_pool p;
+
+ cl_git_pass(git_pool_init(&p, 1, 100));
+ cl_assert(git_pool_strndup(&p, "foo", -1) == NULL);
+ git_pool_clear(&p);
+}
+
diff --git a/tests/core/posix.c b/tests/core/posix.c
new file mode 100644
index 000000000..1cef937cd
--- /dev/null
+++ b/tests/core/posix.c
@@ -0,0 +1,99 @@
+#ifndef _WIN32
+# include <arpa/inet.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+#else
+# include <ws2tcpip.h>
+# ifdef _MSC_VER
+# pragma comment(lib, "ws2_32")
+# endif
+#endif
+
+#include "clar_libgit2.h"
+#include "posix.h"
+
+void test_core_posix__initialize(void)
+{
+#ifdef GIT_WIN32
+ /* on win32, the WSA context needs to be initialized
+ * before any socket calls can be performed */
+ WSADATA wsd;
+
+ cl_git_pass(WSAStartup(MAKEWORD(2,2), &wsd));
+ cl_assert(LOBYTE(wsd.wVersion) == 2 && HIBYTE(wsd.wVersion) == 2);
+#endif
+}
+
+static bool supports_ipv6(void)
+{
+#ifdef GIT_WIN32
+ /* IPv6 is supported on Vista and newer */
+ return git_has_win32_version(6, 0, 0);
+#else
+ return 1;
+#endif
+}
+
+void test_core_posix__inet_pton(void)
+{
+ struct in_addr addr;
+ struct in6_addr addr6;
+ size_t i;
+
+ struct in_addr_data {
+ const char *p;
+ const uint8_t n[4];
+ };
+
+ struct in6_addr_data {
+ const char *p;
+ const uint8_t n[16];
+ };
+
+ static struct in_addr_data in_addr_data[] = {
+ { "0.0.0.0", { 0, 0, 0, 0 } },
+ { "10.42.101.8", { 10, 42, 101, 8 } },
+ { "127.0.0.1", { 127, 0, 0, 1 } },
+ { "140.177.10.12", { 140, 177, 10, 12 } },
+ { "204.232.175.90", { 204, 232, 175, 90 } },
+ { "255.255.255.255", { 255, 255, 255, 255 } },
+ };
+
+ static struct in6_addr_data in6_addr_data[] = {
+ { "::", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { "::1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } },
+ { "0:0:0:0:0:0:0:1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } },
+ { "2001:db8:8714:3a90::12", { 0x20, 0x01, 0x0d, 0xb8, 0x87, 0x14, 0x3a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12 } },
+ { "fe80::f8ba:c2d6:86be:3645", { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xba, 0xc2, 0xd6, 0x86, 0xbe, 0x36, 0x45 } },
+ { "::ffff:204.152.189.116", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x98, 0xbd, 0x74 } },
+ };
+
+ /* Test some ipv4 addresses */
+ for (i = 0; i < 6; i++) {
+ cl_assert(p_inet_pton(AF_INET, in_addr_data[i].p, &addr) == 1);
+ cl_assert(memcmp(&addr, in_addr_data[i].n, sizeof(struct in_addr)) == 0);
+ }
+
+ /* Test some ipv6 addresses */
+ if (supports_ipv6())
+ {
+ for (i = 0; i < 6; i++) {
+ cl_assert(p_inet_pton(AF_INET6, in6_addr_data[i].p, &addr6) == 1);
+ cl_assert(memcmp(&addr6, in6_addr_data[i].n, sizeof(struct in6_addr)) == 0);
+ }
+ }
+
+ /* Test some invalid strings */
+ cl_assert(p_inet_pton(AF_INET, "", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, "foo", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, " 127.0.0.1", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, "bar", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, "10.foo.bar.1", &addr) == 0);
+
+ /* Test unsupported address families */
+ cl_git_fail(p_inet_pton(12, "52.472", NULL)); /* AF_DECnet */
+ cl_assert_equal_i(EAFNOSUPPORT, errno);
+
+ cl_git_fail(p_inet_pton(5, "315.124", NULL)); /* AF_CHAOS */
+ cl_assert_equal_i(EAFNOSUPPORT, errno);
+}
diff --git a/tests/core/rmdir.c b/tests/core/rmdir.c
new file mode 100644
index 000000000..f0b0bfa42
--- /dev/null
+++ b/tests/core/rmdir.c
@@ -0,0 +1,98 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+
+static const char *empty_tmp_dir = "test_gitfo_rmdir_recurs_test";
+
+void test_core_rmdir__initialize(void)
+{
+ git_buf path = GIT_BUF_INIT;
+
+ cl_must_pass(p_mkdir(empty_tmp_dir, 0777));
+
+ cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_one"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two/three"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/two"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ git_buf_free(&path);
+}
+
+/* make sure empty dir can be deleted recusively */
+void test_core_rmdir__delete_recursive(void)
+{
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+}
+
+/* make sure non-empty dir cannot be deleted recusively */
+void test_core_rmdir__fail_to_delete_non_empty_dir(void)
+{
+ git_buf file = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
+
+ cl_git_mkfile(git_buf_cstr(&file), "dummy");
+
+ cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+
+ cl_must_pass(p_unlink(file.ptr));
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+
+ git_buf_free(&file);
+}
+
+void test_core_rmdir__can_skip_non_empty_dir(void)
+{
+ git_buf file = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
+
+ cl_git_mkfile(git_buf_cstr(&file), "dummy");
+
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_SKIP_NONEMPTY));
+ cl_assert(git_path_exists(git_buf_cstr(&file)) == true);
+
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(git_path_exists(empty_tmp_dir) == false);
+
+ git_buf_free(&file);
+}
+
+void test_core_rmdir__can_remove_empty_parents(void)
+{
+ git_buf file = GIT_BUF_INIT;
+
+ cl_git_pass(
+ git_buf_joinpath(&file, empty_tmp_dir, "/one/two_two/three/file.txt"));
+ cl_git_mkfile(git_buf_cstr(&file), "dummy");
+ cl_assert(git_path_isfile(git_buf_cstr(&file)));
+
+ cl_git_pass(git_futils_rmdir_r("one/two_two/three/file.txt", empty_tmp_dir,
+ GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_EMPTY_PARENTS));
+
+ cl_assert(!git_path_exists(git_buf_cstr(&file)));
+
+ git_buf_rtruncate_at_char(&file, '/'); /* three (only contained file.txt) */
+ cl_assert(!git_path_exists(git_buf_cstr(&file)));
+
+ git_buf_rtruncate_at_char(&file, '/'); /* two_two (only contained three) */
+ cl_assert(!git_path_exists(git_buf_cstr(&file)));
+
+ git_buf_rtruncate_at_char(&file, '/'); /* one (contained two_one also) */
+ cl_assert(git_path_exists(git_buf_cstr(&file)));
+
+ cl_assert(git_path_exists(empty_tmp_dir) == true);
+
+ git_buf_free(&file);
+
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+}
diff --git a/tests/core/sortedcache.c b/tests/core/sortedcache.c
new file mode 100644
index 000000000..c1869bee0
--- /dev/null
+++ b/tests/core/sortedcache.c
@@ -0,0 +1,363 @@
+#include "clar_libgit2.h"
+#include "sortedcache.h"
+
+static int name_only_cmp(const void *a, const void *b)
+{
+ return strcmp(a, b);
+}
+
+void test_core_sortedcache__name_only(void)
+{
+ git_sortedcache *sc;
+ void *item;
+ size_t pos;
+
+ cl_git_pass(git_sortedcache_new(
+ &sc, 0, NULL, NULL, name_only_cmp, NULL));
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "aaa"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "bbb"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "zzz"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "mmm"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "iii"));
+ git_sortedcache_wunlock(sc);
+
+ cl_assert_equal_sz(5, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL);
+ cl_assert_equal_s("aaa", item);
+ cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL);
+ cl_assert_equal_s("mmm", item);
+ cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL);
+ cl_assert_equal_s("zzz", item);
+ cl_assert(git_sortedcache_lookup(sc, "qqq") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("aaa", item);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("bbb", item);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("iii", item);
+ cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+ cl_assert_equal_s("mmm", item);
+ cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL);
+ cl_assert_equal_s("zzz", item);
+ cl_assert(git_sortedcache_entry(sc, 5) == NULL);
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "iii"));
+ cl_assert_equal_sz(2, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz"));
+ cl_assert_equal_sz(4, pos);
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "abc"));
+
+ git_sortedcache_clear(sc, true);
+
+ cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+ cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+
+ git_sortedcache_free(sc);
+}
+
+typedef struct {
+ int value;
+ char smaller_value;
+ char path[GIT_FLEX_ARRAY];
+} sortedcache_test_struct;
+
+static int sortedcache_test_struct_cmp(const void *a_, const void *b_)
+{
+ const sortedcache_test_struct *a = a_, *b = b_;
+ return strcmp(a->path, b->path);
+}
+
+static void sortedcache_test_struct_free(void *payload, void *item_)
+{
+ sortedcache_test_struct *item = item_;
+ int *count = payload;
+ (*count)++;
+ item->smaller_value = 0;
+}
+
+void test_core_sortedcache__in_memory(void)
+{
+ git_sortedcache *sc;
+ sortedcache_test_struct *item;
+ int free_count = 0;
+
+ cl_git_pass(git_sortedcache_new(
+ &sc, offsetof(sortedcache_test_struct, path),
+ sortedcache_test_struct_free, &free_count,
+ sortedcache_test_struct_cmp, NULL));
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "aaa"));
+ item->value = 10;
+ item->smaller_value = 1;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "bbb"));
+ item->value = 20;
+ item->smaller_value = 2;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "zzz"));
+ item->value = 30;
+ item->smaller_value = 26;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "mmm"));
+ item->value = 40;
+ item->smaller_value = 14;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "iii"));
+ item->value = 50;
+ item->smaller_value = 9;
+ git_sortedcache_wunlock(sc);
+
+ cl_assert_equal_sz(5, git_sortedcache_entrycount(sc));
+
+ cl_git_pass(git_sortedcache_rlock(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL);
+ cl_assert_equal_s("aaa", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL);
+ cl_assert_equal_s("mmm", item->path);
+ cl_assert_equal_i(40, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL);
+ cl_assert_equal_s("zzz", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "abc") == NULL);
+
+ /* not on Windows:
+ * cl_git_pass(git_sortedcache_rlock(sc)); -- grab more than one
+ */
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("aaa", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("bbb", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("iii", item->path);
+ cl_assert_equal_i(50, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+ cl_assert_equal_s("mmm", item->path);
+ cl_assert_equal_i(40, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL);
+ cl_assert_equal_s("zzz", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_entry(sc, 5) == NULL);
+
+ git_sortedcache_runlock(sc);
+ /* git_sortedcache_runlock(sc); */
+
+ cl_assert_equal_i(0, free_count);
+
+ git_sortedcache_clear(sc, true);
+
+ cl_assert_equal_i(5, free_count);
+
+ cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+ cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+
+ free_count = 0;
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "testing"));
+ item->value = 10;
+ item->smaller_value = 3;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "again"));
+ item->value = 20;
+ item->smaller_value = 1;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "final"));
+ item->value = 30;
+ item->smaller_value = 2;
+ git_sortedcache_wunlock(sc);
+
+ cl_assert_equal_sz(3, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "testing")) != NULL);
+ cl_assert_equal_s("testing", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "again")) != NULL);
+ cl_assert_equal_s("again", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "zzz") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("again", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("testing", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert(git_sortedcache_entry(sc, 3) == NULL);
+
+ {
+ size_t pos;
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "again"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_remove(sc, pos));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "again"));
+
+ cl_assert_equal_sz(2, git_sortedcache_entrycount(sc));
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "testing"));
+ cl_assert_equal_sz(1, pos);
+ cl_git_pass(git_sortedcache_remove(sc, pos));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "testing"));
+
+ cl_assert_equal_sz(1, git_sortedcache_entrycount(sc));
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_remove(sc, pos));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "final"));
+
+ cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+
+ git_sortedcache_wunlock(sc);
+ }
+
+ git_sortedcache_free(sc);
+
+ cl_assert_equal_i(3, free_count);
+}
+
+static void sortedcache_test_reload(git_sortedcache *sc)
+{
+ int count = 0;
+ git_buf buf = GIT_BUF_INIT;
+ char *scan, *after;
+ sortedcache_test_struct *item;
+
+ cl_assert(git_sortedcache_lockandload(sc, &buf) > 0);
+
+ git_sortedcache_clear(sc, false); /* clear once we already have lock */
+
+ for (scan = buf.ptr; *scan; scan = after + 1) {
+ int val = strtol(scan, &after, 0);
+ cl_assert(after > scan);
+ scan = after;
+
+ for (scan = after; git__isspace(*scan); ++scan) /* find start */;
+ for (after = scan; *after && *after != '\n'; ++after) /* find eol */;
+ *after = '\0';
+
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, scan));
+
+ item->value = val;
+ item->smaller_value = (char)(count++);
+ }
+
+ git_sortedcache_wunlock(sc);
+
+ git_buf_free(&buf);
+}
+
+void test_core_sortedcache__on_disk(void)
+{
+ git_sortedcache *sc;
+ sortedcache_test_struct *item;
+ int free_count = 0;
+ size_t pos;
+
+ cl_git_mkfile("cacheitems.txt", "10 abc\n20 bcd\n30 cde\n");
+
+ cl_git_pass(git_sortedcache_new(
+ &sc, offsetof(sortedcache_test_struct, path),
+ sortedcache_test_struct_free, &free_count,
+ sortedcache_test_struct_cmp, "cacheitems.txt"));
+
+ /* should need to reload the first time */
+
+ sortedcache_test_reload(sc);
+
+ /* test what we loaded */
+
+ cl_assert_equal_sz(3, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL);
+ cl_assert_equal_s("abc", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "cde")) != NULL);
+ cl_assert_equal_s("cde", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("abc", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("bcd", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert(git_sortedcache_entry(sc, 3) == NULL);
+
+ /* should not need to reload this time */
+
+ cl_assert_equal_i(0, git_sortedcache_lockandload(sc, NULL));
+
+ /* rewrite ondisk file and reload */
+
+ cl_assert_equal_i(0, free_count);
+
+ cl_git_rewritefile(
+ "cacheitems.txt", "100 abc\n200 zzz\n500 aaa\n10 final\n");
+ sortedcache_test_reload(sc);
+
+ cl_assert_equal_i(3, free_count);
+
+ /* test what we loaded */
+
+ cl_assert_equal_sz(4, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL);
+ cl_assert_equal_s("abc", item->path);
+ cl_assert_equal_i(100, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "cde") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("aaa", item->path);
+ cl_assert_equal_i(500, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+ cl_assert_equal_s("zzz", item->path);
+ cl_assert_equal_i(200, item->value);
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "abc"));
+ cl_assert_equal_sz(1, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final"));
+ cl_assert_equal_sz(2, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz"));
+ cl_assert_equal_sz(3, pos);
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "missing"));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "cde"));
+
+ git_sortedcache_free(sc);
+
+ cl_assert_equal_i(7, free_count);
+}
+
diff --git a/tests/core/stat.c b/tests/core/stat.c
new file mode 100644
index 000000000..2e4abfb79
--- /dev/null
+++ b/tests/core/stat.c
@@ -0,0 +1,97 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "path.h"
+#include "posix.h"
+
+void test_core_stat__initialize(void)
+{
+ cl_git_pass(git_futils_mkdir("root/d1/d2", NULL, 0755, GIT_MKDIR_PATH));
+ cl_git_mkfile("root/file", "whatever\n");
+ cl_git_mkfile("root/d1/file", "whatever\n");
+}
+
+void test_core_stat__cleanup(void)
+{
+ git_futils_rmdir_r("root", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+#define cl_assert_error(val) \
+ do { err = errno; cl_assert_equal_i((val), err); } while (0)
+
+void test_core_stat__0(void)
+{
+ struct stat st;
+ int err;
+
+ cl_assert_equal_i(0, p_lstat("root", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/file", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/d1", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/d1/", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/d1/file", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert(p_lstat("root/missing", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat("root/missing/but/could/be/created", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat_posixly("root/missing/but/could/be/created", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat("root/d1/missing", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat("root/d1/missing/deeper/path", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat_posixly("root/d1/missing/deeper/path", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat_posixly("root/d1/file/deeper/path", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat("root/file/invalid", &st) < 0);
+#ifdef GIT_WIN32
+ cl_assert_error(ENOENT);
+#else
+ cl_assert_error(ENOTDIR);
+#endif
+
+ cl_assert(p_lstat_posixly("root/file/invalid", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat("root/file/invalid/deeper_path", &st) < 0);
+#ifdef GIT_WIN32
+ cl_assert_error(ENOENT);
+#else
+ cl_assert_error(ENOTDIR);
+#endif
+
+ cl_assert(p_lstat_posixly("root/file/invalid/deeper_path", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat_posixly("root/d1/file/extra", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat_posixly("root/d1/file/further/invalid/items", &st) < 0);
+ cl_assert_error(ENOTDIR);
+}
+
diff --git a/tests/core/string.c b/tests/core/string.c
new file mode 100644
index 000000000..ec9575685
--- /dev/null
+++ b/tests/core/string.c
@@ -0,0 +1,41 @@
+#include "clar_libgit2.h"
+
+/* compare prefixes */
+void test_core_string__0(void)
+{
+ cl_assert(git__prefixcmp("", "") == 0);
+ cl_assert(git__prefixcmp("a", "") == 0);
+ cl_assert(git__prefixcmp("", "a") < 0);
+ cl_assert(git__prefixcmp("a", "b") < 0);
+ cl_assert(git__prefixcmp("b", "a") > 0);
+ cl_assert(git__prefixcmp("ab", "a") == 0);
+ cl_assert(git__prefixcmp("ab", "ac") < 0);
+ cl_assert(git__prefixcmp("ab", "aa") > 0);
+}
+
+/* compare suffixes */
+void test_core_string__1(void)
+{
+ cl_assert(git__suffixcmp("", "") == 0);
+ cl_assert(git__suffixcmp("a", "") == 0);
+ cl_assert(git__suffixcmp("", "a") < 0);
+ cl_assert(git__suffixcmp("a", "b") < 0);
+ cl_assert(git__suffixcmp("b", "a") > 0);
+ cl_assert(git__suffixcmp("ba", "a") == 0);
+ cl_assert(git__suffixcmp("zaa", "ac") < 0);
+ cl_assert(git__suffixcmp("zaz", "ac") > 0);
+}
+
+/* compare icase sorting with case equality */
+void test_core_string__2(void)
+{
+ cl_assert(git__strcasesort_cmp("", "") == 0);
+ cl_assert(git__strcasesort_cmp("foo", "foo") == 0);
+ cl_assert(git__strcasesort_cmp("foo", "bar") > 0);
+ cl_assert(git__strcasesort_cmp("bar", "foo") < 0);
+ cl_assert(git__strcasesort_cmp("foo", "FOO") > 0);
+ cl_assert(git__strcasesort_cmp("FOO", "foo") < 0);
+ cl_assert(git__strcasesort_cmp("foo", "BAR") > 0);
+ cl_assert(git__strcasesort_cmp("BAR", "foo") < 0);
+ cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0);
+}
diff --git a/tests/core/strmap.c b/tests/core/strmap.c
new file mode 100644
index 000000000..f34a4f89f
--- /dev/null
+++ b/tests/core/strmap.c
@@ -0,0 +1,102 @@
+#include "clar_libgit2.h"
+#include "strmap.h"
+
+GIT__USE_STRMAP;
+
+void test_core_strmap__0(void)
+{
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+ cl_assert(git_strmap_num_entries(table) == 0);
+ git_strmap_free(table);
+}
+
+static void insert_strings(git_strmap *table, int count)
+{
+ int i, j, over, err;
+ char *str;
+
+ for (i = 0; i < count; ++i) {
+ str = malloc(10);
+ for (j = 0; j < 10; ++j)
+ str[j] = 'a' + (i % 26);
+ str[9] = '\0';
+
+ /* if > 26, then encode larger value in first letters */
+ for (j = 0, over = i / 26; over > 0; j++, over = over / 26)
+ str[j] = 'A' + (over % 26);
+
+ git_strmap_insert(table, str, str, err);
+ cl_assert(err >= 0);
+ }
+
+ cl_assert((int)git_strmap_num_entries(table) == count);
+}
+
+void test_core_strmap__1(void)
+{
+ int i;
+ char *str;
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+
+ insert_strings(table, 20);
+
+ cl_assert(git_strmap_exists(table, "aaaaaaaaa"));
+ cl_assert(git_strmap_exists(table, "ggggggggg"));
+ cl_assert(!git_strmap_exists(table, "aaaaaaaab"));
+ cl_assert(!git_strmap_exists(table, "abcdefghi"));
+
+ i = 0;
+ git_strmap_foreach_value(table, str, { i++; free(str); });
+ cl_assert(i == 20);
+
+ git_strmap_free(table);
+}
+
+void test_core_strmap__2(void)
+{
+ khiter_t pos;
+ int i;
+ char *str;
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+
+ insert_strings(table, 20);
+
+ cl_assert(git_strmap_exists(table, "aaaaaaaaa"));
+ cl_assert(git_strmap_exists(table, "ggggggggg"));
+ cl_assert(!git_strmap_exists(table, "aaaaaaaab"));
+ cl_assert(!git_strmap_exists(table, "abcdefghi"));
+
+ cl_assert(git_strmap_exists(table, "bbbbbbbbb"));
+ pos = git_strmap_lookup_index(table, "bbbbbbbbb");
+ cl_assert(git_strmap_valid_index(table, pos));
+ cl_assert_equal_s(git_strmap_value_at(table, pos), "bbbbbbbbb");
+ free(git_strmap_value_at(table, pos));
+ git_strmap_delete_at(table, pos);
+
+ cl_assert(!git_strmap_exists(table, "bbbbbbbbb"));
+
+ i = 0;
+ git_strmap_foreach_value(table, str, { i++; free(str); });
+ cl_assert(i == 19);
+
+ git_strmap_free(table);
+}
+
+void test_core_strmap__3(void)
+{
+ int i;
+ char *str;
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+
+ insert_strings(table, 10000);
+
+ i = 0;
+ git_strmap_foreach_value(table, str, { i++; free(str); });
+ cl_assert(i == 10000);
+
+ git_strmap_free(table);
+}
diff --git a/tests/core/strtol.c b/tests/core/strtol.c
new file mode 100644
index 000000000..8765e042b
--- /dev/null
+++ b/tests/core/strtol.c
@@ -0,0 +1,37 @@
+#include "clar_libgit2.h"
+
+void test_core_strtol__int32(void)
+{
+ int32_t i;
+
+ cl_git_pass(git__strtol32(&i, "123", NULL, 10));
+ cl_assert(i == 123);
+ cl_git_pass(git__strtol32(&i, " +123 ", NULL, 10));
+ cl_assert(i == 123);
+ cl_git_pass(git__strtol32(&i, " +2147483647 ", NULL, 10));
+ cl_assert(i == 2147483647);
+ cl_git_pass(git__strtol32(&i, " -2147483648 ", NULL, 10));
+ cl_assert(i == -2147483648LL);
+
+ cl_git_fail(git__strtol32(&i, " 2147483657 ", NULL, 10));
+ cl_git_fail(git__strtol32(&i, " -2147483657 ", NULL, 10));
+}
+
+void test_core_strtol__int64(void)
+{
+ int64_t i;
+
+ cl_git_pass(git__strtol64(&i, "123", NULL, 10));
+ cl_assert(i == 123);
+ cl_git_pass(git__strtol64(&i, " +123 ", NULL, 10));
+ cl_assert(i == 123);
+ cl_git_pass(git__strtol64(&i, " +2147483647 ", NULL, 10));
+ cl_assert(i == 2147483647);
+ cl_git_pass(git__strtol64(&i, " -2147483648 ", NULL, 10));
+ cl_assert(i == -2147483648LL);
+ cl_git_pass(git__strtol64(&i, " 2147483657 ", NULL, 10));
+ cl_assert(i == 2147483657LL);
+ cl_git_pass(git__strtol64(&i, " -2147483657 ", NULL, 10));
+ cl_assert(i == -2147483657LL);
+}
+
diff --git a/tests/core/vector.c b/tests/core/vector.c
new file mode 100644
index 000000000..db52c004f
--- /dev/null
+++ b/tests/core/vector.c
@@ -0,0 +1,275 @@
+#include "clar_libgit2.h"
+#include "vector.h"
+
+/* initial size of 1 would cause writing past array bounds */
+void test_core_vector__0(void)
+{
+ git_vector x;
+ int i;
+ git_vector_init(&x, 1, NULL);
+ for (i = 0; i < 10; ++i) {
+ git_vector_insert(&x, (void*) 0xabc);
+ }
+ git_vector_free(&x);
+}
+
+
+/* don't read past array bounds on remove() */
+void test_core_vector__1(void)
+{
+ git_vector x;
+ // make initial capacity exact for our insertions.
+ git_vector_init(&x, 3, NULL);
+ git_vector_insert(&x, (void*) 0xabc);
+ git_vector_insert(&x, (void*) 0xdef);
+ git_vector_insert(&x, (void*) 0x123);
+
+ git_vector_remove(&x, 0); // used to read past array bounds.
+ git_vector_free(&x);
+}
+
+
+static int test_cmp(const void *a, const void *b)
+{
+ return *(const int *)a - *(const int *)b;
+}
+
+/* remove duplicates */
+void test_core_vector__2(void)
+{
+ git_vector x;
+ int *ptrs[2];
+
+ ptrs[0] = git__malloc(sizeof(int));
+ ptrs[1] = git__malloc(sizeof(int));
+
+ *ptrs[0] = 2;
+ *ptrs[1] = 1;
+
+ cl_git_pass(git_vector_init(&x, 5, test_cmp));
+ cl_git_pass(git_vector_insert(&x, ptrs[0]));
+ cl_git_pass(git_vector_insert(&x, ptrs[1]));
+ cl_git_pass(git_vector_insert(&x, ptrs[1]));
+ cl_git_pass(git_vector_insert(&x, ptrs[0]));
+ cl_git_pass(git_vector_insert(&x, ptrs[1]));
+ cl_assert(x.length == 5);
+
+ git_vector_uniq(&x, NULL);
+ cl_assert(x.length == 2);
+
+ git_vector_free(&x);
+
+ git__free(ptrs[0]);
+ git__free(ptrs[1]);
+}
+
+
+static int compare_them(const void *a, const void *b)
+{
+ return (int)((long)a - (long)b);
+}
+
+/* insert_sorted */
+void test_core_vector__3(void)
+{
+ git_vector x;
+ long i;
+ git_vector_init(&x, 1, &compare_them);
+
+ for (i = 0; i < 10; i += 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 9; i > 0; i -= 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ cl_assert(x.length == 10);
+ for (i = 0; i < 10; ++i) {
+ cl_assert(git_vector_get(&x, i) == (void*)(i + 1));
+ }
+
+ git_vector_free(&x);
+}
+
+/* insert_sorted with duplicates */
+void test_core_vector__4(void)
+{
+ git_vector x;
+ long i;
+ git_vector_init(&x, 1, &compare_them);
+
+ for (i = 0; i < 10; i += 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 9; i > 0; i -= 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 0; i < 10; i += 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 9; i > 0; i -= 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ cl_assert(x.length == 20);
+ for (i = 0; i < 20; ++i) {
+ cl_assert(git_vector_get(&x, i) == (void*)(i / 2 + 1));
+ }
+
+ git_vector_free(&x);
+}
+
+typedef struct {
+ int content;
+ int count;
+} my_struct;
+
+static int _struct_count = 0;
+
+static int compare_structs(const void *a, const void *b)
+{
+ return ((const my_struct *)a)->content -
+ ((const my_struct *)b)->content;
+}
+
+static int merge_structs(void **old_raw, void *new)
+{
+ my_struct *old = *(my_struct **)old_raw;
+ cl_assert(((my_struct *)old)->content == ((my_struct *)new)->content);
+ ((my_struct *)old)->count += 1;
+ git__free(new);
+ _struct_count--;
+ return GIT_EEXISTS;
+}
+
+static my_struct *alloc_struct(int value)
+{
+ my_struct *st = git__malloc(sizeof(my_struct));
+ st->content = value;
+ st->count = 0;
+ _struct_count++;
+ return st;
+}
+
+/* insert_sorted with duplicates and special handling */
+void test_core_vector__5(void)
+{
+ git_vector x;
+ int i;
+
+ git_vector_init(&x, 1, &compare_structs);
+
+ for (i = 0; i < 10; i += 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ for (i = 9; i > 0; i -= 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ cl_assert(x.length == 10);
+ cl_assert(_struct_count == 10);
+
+ for (i = 0; i < 10; i += 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ for (i = 9; i > 0; i -= 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ cl_assert(x.length == 10);
+ cl_assert(_struct_count == 10);
+
+ for (i = 0; i < 10; ++i) {
+ cl_assert(((my_struct *)git_vector_get(&x, i))->content == i);
+ git__free(git_vector_get(&x, i));
+ _struct_count--;
+ }
+
+ git_vector_free(&x);
+}
+
+static int remove_ones(const git_vector *v, size_t idx)
+{
+ return (git_vector_get(v, idx) == (void *)0x001);
+}
+
+/* Test removal based on callback */
+void test_core_vector__remove_matching(void)
+{
+ git_vector x;
+ size_t i;
+ void *compare;
+
+ git_vector_init(&x, 1, NULL);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 1);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 0);
+
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 3);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 0);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x003);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x003);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 4);
+
+ git_vector_free(&x);
+}