summaryrefslogtreecommitdiff
path: root/tests/libgit2/object
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2021-11-16 23:29:22 -0500
committerEdward Thomson <ethomson@edwardthomson.com>2022-02-22 22:07:45 -0500
commit3344fddc97bbdea9c1b6ebb6f7fb6dbd70b41dfb (patch)
treefd6368a72944571c51627b40c592e7d58e0036e1 /tests/libgit2/object
parent91ba089663f5efc3bd4ba14a5099372cf5ce57a6 (diff)
downloadlibgit2-3344fddc97bbdea9c1b6ebb6f7fb6dbd70b41dfb.tar.gz
refactor: `tests` is now `tests/libgit2`
Like we want to separate libgit2 and utility source code, we want to separate libgit2 and utility tests. Start by moving all the tests into libgit2.
Diffstat (limited to 'tests/libgit2/object')
-rw-r--r--tests/libgit2/object/blob/filter.c149
-rw-r--r--tests/libgit2/object/blob/fromstream.c86
-rw-r--r--tests/libgit2/object/blob/write.c68
-rw-r--r--tests/libgit2/object/cache.c276
-rw-r--r--tests/libgit2/object/commit/commitstagedfile.c218
-rw-r--r--tests/libgit2/object/commit/parse.c232
-rw-r--r--tests/libgit2/object/lookup.c122
-rw-r--r--tests/libgit2/object/lookupbypath.c83
-rw-r--r--tests/libgit2/object/message.c197
-rw-r--r--tests/libgit2/object/peel.c118
-rw-r--r--tests/libgit2/object/raw/chars.c41
-rw-r--r--tests/libgit2/object/raw/compare.c123
-rw-r--r--tests/libgit2/object/raw/convert.c112
-rw-r--r--tests/libgit2/object/raw/data.h323
-rw-r--r--tests/libgit2/object/raw/fromstr.c30
-rw-r--r--tests/libgit2/object/raw/hash.c169
-rw-r--r--tests/libgit2/object/raw/short.c137
-rw-r--r--tests/libgit2/object/raw/size.c13
-rw-r--r--tests/libgit2/object/raw/type2string.c54
-rw-r--r--tests/libgit2/object/raw/write.c462
-rw-r--r--tests/libgit2/object/shortid.c51
-rw-r--r--tests/libgit2/object/tag/list.c117
-rw-r--r--tests/libgit2/object/tag/parse.c218
-rw-r--r--tests/libgit2/object/tag/peel.c61
-rw-r--r--tests/libgit2/object/tag/read.c179
-rw-r--r--tests/libgit2/object/tag/write.c260
-rw-r--r--tests/libgit2/object/tree/attributes.c118
-rw-r--r--tests/libgit2/object/tree/duplicateentries.c157
-rw-r--r--tests/libgit2/object/tree/frompath.c68
-rw-r--r--tests/libgit2/object/tree/parse.c164
-rw-r--r--tests/libgit2/object/tree/read.c119
-rw-r--r--tests/libgit2/object/tree/update.c302
-rw-r--r--tests/libgit2/object/tree/walk.c177
-rw-r--r--tests/libgit2/object/tree/write.c526
-rw-r--r--tests/libgit2/object/validate.c50
35 files changed, 5580 insertions, 0 deletions
diff --git a/tests/libgit2/object/blob/filter.c b/tests/libgit2/object/blob/filter.c
new file mode 100644
index 000000000..00b553e71
--- /dev/null
+++ b/tests/libgit2/object/blob/filter.c
@@ -0,0 +1,149 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+
+static git_repository *g_repo = NULL;
+
+#define CRLF_NUM_TEST_OBJECTS 9
+
+static const char *g_crlf_raw[CRLF_NUM_TEST_OBJECTS] = {
+ "",
+ "foo\nbar\n",
+ "foo\rbar\r",
+ "foo\r\nbar\r\n",
+ "foo\nbar\rboth\r\nreversed\n\ragain\nproblems\r",
+ "123\n\000\001\002\003\004abc\255\254\253\r\n",
+ "\xEF\xBB\xBFThis is UTF-8\n",
+ "\xEF\xBB\xBF\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\r\n\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\r\n",
+ "\xFE\xFF\x00T\x00h\x00i\x00s\x00!"
+};
+
+static off64_t g_crlf_raw_len[CRLF_NUM_TEST_OBJECTS] = {
+ -1, -1, -1, -1, -1, 17, -1, -1, 12
+};
+
+static git_oid g_crlf_oids[CRLF_NUM_TEST_OBJECTS];
+
+static git_str g_crlf_filtered[CRLF_NUM_TEST_OBJECTS] = {
+ { "", 0, 0 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\rbar\r", 0, 8 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\nbar\rboth\nreversed\n\ragain\nproblems\r", 0, 38 },
+ { "123\n\000\001\002\003\004abc\255\254\253\n", 0, 16 },
+ { "\xEF\xBB\xBFThis is UTF-8\n", 0, 17 },
+ { "\xEF\xBB\xBF\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\n\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\n", 0, 29 },
+ { "\xFE\xFF\x00T\x00h\x00i\x00s\x00!", 0, 12 }
+};
+
+static git_str_text_stats g_crlf_filtered_stats[CRLF_NUM_TEST_OBJECTS] = {
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 2, 0, 6, 0 },
+ { 0, 0, 2, 0, 0, 6, 0 },
+ { 0, 0, 2, 2, 2, 6, 0 },
+ { 0, 0, 4, 4, 1, 31, 0 },
+ { 0, 1, 1, 2, 1, 9, 5 },
+ { GIT_STR_BOM_UTF8, 0, 0, 1, 0, 16, 0 },
+ { GIT_STR_BOM_UTF8, 0, 2, 2, 2, 27, 0 },
+ { GIT_STR_BOM_UTF16_BE, 5, 0, 0, 0, 7, 5 },
+};
+
+void test_object_blob_filter__initialize(void)
+{
+ int i;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ if (g_crlf_raw_len[i] < 0)
+ g_crlf_raw_len[i] = strlen(g_crlf_raw[i]);
+
+ cl_git_pass(git_blob_create_from_buffer(
+ &g_crlf_oids[i], g_repo, g_crlf_raw[i], (size_t)g_crlf_raw_len[i]));
+ }
+}
+
+void test_object_blob_filter__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_blob_filter__unfiltered(void)
+{
+ int i;
+ git_blob *blob;
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ size_t raw_len = (size_t)g_crlf_raw_len[i];
+
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+
+ cl_assert_equal_sz(raw_len, (size_t)git_blob_rawsize(blob));
+ cl_assert_equal_i(
+ 0, memcmp(g_crlf_raw[i], git_blob_rawcontent(blob), raw_len));
+
+ git_blob_free(blob);
+ }
+}
+
+void test_object_blob_filter__stats(void)
+{
+ int i;
+ git_blob *blob;
+ git_str buf = GIT_STR_INIT;
+ git_str_text_stats stats;
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+ cl_git_pass(git_blob__getbuf(&buf, blob));
+ git_str_gather_text_stats(&stats, &buf, false);
+ cl_assert_equal_i(
+ 0, memcmp(&g_crlf_filtered_stats[i], &stats, sizeof(stats)));
+ git_blob_free(blob);
+ }
+
+ git_str_dispose(&buf);
+}
+
+void test_object_blob_filter__to_odb(void)
+{
+ git_filter_list *fl = NULL;
+ git_config *cfg;
+ int i;
+ git_blob *blob;
+ git_buf out = GIT_BUF_INIT, zeroed;
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_assert(cfg);
+
+ git_attr_cache_flush(g_repo);
+ cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "filename.txt", GIT_FILTER_TO_ODB, 0));
+ cl_assert(fl != NULL);
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+
+ /* try once with allocated blob */
+ cl_git_pass(git_filter_list_apply_to_blob(&out, fl, blob));
+ cl_assert_equal_sz(g_crlf_filtered[i].size, out.size);
+ cl_assert_equal_i(
+ 0, memcmp(out.ptr, g_crlf_filtered[i].ptr, out.size));
+
+ /* try again with zeroed blob */
+ memset(&zeroed, 0, sizeof(zeroed));
+ cl_git_pass(git_filter_list_apply_to_blob(&zeroed, fl, blob));
+ cl_assert_equal_sz(g_crlf_filtered[i].size, zeroed.size);
+ cl_assert_equal_i(
+ 0, memcmp(zeroed.ptr, g_crlf_filtered[i].ptr, zeroed.size));
+ git_buf_dispose(&zeroed);
+
+ git_blob_free(blob);
+ }
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/object/blob/fromstream.c b/tests/libgit2/object/blob/fromstream.c
new file mode 100644
index 000000000..60ff3991b
--- /dev/null
+++ b/tests/libgit2/object/blob/fromstream.c
@@ -0,0 +1,86 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "futils.h"
+
+static git_repository *repo;
+static char textual_content[] = "libgit2\n\r\n\0";
+
+void test_object_blob_fromstream__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_object_blob_fromstream__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_blob_fromstream__multiple_write(void)
+{
+ git_oid expected_id, id;
+ git_object *blob;
+ git_writestream *stream;
+ int i, howmany = 6;
+
+ cl_git_pass(git_oid_fromstr(&expected_id, "321cbdf08803c744082332332838df6bd160f8f9"));
+
+ cl_git_fail_with(GIT_ENOTFOUND,
+ git_object_lookup(&blob, repo, &expected_id, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_blob_create_from_stream(&stream, repo, NULL));
+
+ for (i = 0; i < howmany; i++)
+ cl_git_pass(stream->write(stream, textual_content, strlen(textual_content)));
+
+ cl_git_pass(git_blob_create_from_stream_commit(&id, stream));
+ cl_assert_equal_oid(&expected_id, &id);
+
+ cl_git_pass(git_object_lookup(&blob, repo, &expected_id, GIT_OBJECT_BLOB));
+
+ git_object_free(blob);
+}
+
+#define GITATTR "* text=auto\n" \
+ "*.txt text\n" \
+ "*.data binary\n"
+
+static void write_attributes(git_repository *repo)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&buf, git_repository_path(repo), "info"));
+ cl_git_pass(git_str_joinpath(&buf, git_str_cstr(&buf), "attributes"));
+
+ cl_git_pass(git_futils_mkpath2file(git_str_cstr(&buf), 0777));
+ cl_git_rewritefile(git_str_cstr(&buf), GITATTR);
+
+ git_str_dispose(&buf);
+}
+
+static void assert_named_chunked_blob(const char *expected_sha, const char *fake_name)
+{
+ git_oid expected_id, id;
+ git_writestream *stream;
+ int i, howmany = 6;
+
+ cl_git_pass(git_oid_fromstr(&expected_id, expected_sha));
+
+ cl_git_pass(git_blob_create_from_stream(&stream, repo, fake_name));
+
+ for (i = 0; i < howmany; i++)
+ cl_git_pass(stream->write(stream, textual_content, strlen(textual_content)));
+
+ cl_git_pass(git_blob_create_from_stream_commit(&id, stream));
+
+ cl_assert_equal_oid(&expected_id, &id);
+}
+
+void test_object_blob_fromstream__creating_a_blob_from_chunks_honors_the_attributes_directives(void)
+{
+ write_attributes(repo);
+
+ assert_named_chunked_blob("321cbdf08803c744082332332838df6bd160f8f9", "dummy.data");
+ assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.txt");
+ assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.dunno");
+}
diff --git a/tests/libgit2/object/blob/write.c b/tests/libgit2/object/blob/write.c
new file mode 100644
index 000000000..422258d63
--- /dev/null
+++ b/tests/libgit2/object/blob/write.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "futils.h"
+
+static git_repository *repo;
+
+#define WORKDIR "empty_standard_repo"
+#define BARE_REPO "testrepo.git"
+#define ELSEWHERE "elsewhere"
+
+typedef int (*blob_creator_fn)(
+ git_oid *,
+ git_repository *,
+ const char *);
+
+void test_object_blob_write__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_blob_creation(const char *path_to_file, const char *blob_from_path, blob_creator_fn creator)
+{
+ git_oid oid;
+ cl_git_mkfile(path_to_file, "1..2...3... Can you hear me?\n");
+
+ cl_must_pass(creator(&oid, repo, blob_from_path));
+ cl_assert(git_oid_streq(&oid, "da5e4f20c91c81b44a7e298f3d3fb3fe2f178e32") == 0);
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_standard_repo_from_a_file_located_in_the_working_directory(void)
+{
+ repo = cl_git_sandbox_init(WORKDIR);
+
+ assert_blob_creation(WORKDIR "/test.txt", "test.txt", &git_blob_create_from_workdir);
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_standard_repo_from_a_absolute_filepath_pointing_outside_of_the_working_directory(void)
+{
+ git_str full_path = GIT_STR_INIT;
+
+ repo = cl_git_sandbox_init(WORKDIR);
+
+ cl_must_pass(p_mkdir(ELSEWHERE, 0777));
+ cl_must_pass(git_fs_path_prettify_dir(&full_path, ELSEWHERE, NULL));
+ cl_must_pass(git_str_puts(&full_path, "test.txt"));
+
+ assert_blob_creation(ELSEWHERE "/test.txt", git_str_cstr(&full_path), &git_blob_create_from_disk);
+
+ git_str_dispose(&full_path);
+ cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_bare_repo_from_a_absolute_filepath(void)
+{
+ git_str full_path = GIT_STR_INIT;
+
+ repo = cl_git_sandbox_init(BARE_REPO);
+
+ cl_must_pass(p_mkdir(ELSEWHERE, 0777));
+ cl_must_pass(git_fs_path_prettify_dir(&full_path, ELSEWHERE, NULL));
+ cl_must_pass(git_str_puts(&full_path, "test.txt"));
+
+ assert_blob_creation(ELSEWHERE "/test.txt", git_str_cstr(&full_path), &git_blob_create_from_disk);
+
+ git_str_dispose(&full_path);
+ cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_RMDIR_REMOVE_FILES));
+}
diff --git a/tests/libgit2/object/cache.c b/tests/libgit2/object/cache.c
new file mode 100644
index 000000000..08bf03648
--- /dev/null
+++ b/tests/libgit2/object/cache.c
@@ -0,0 +1,276 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+
+static git_repository *g_repo;
+static size_t cache_limit;
+static int object_type;
+
+void test_object_cache__initialize_cache_no_blobs(void)
+{
+ g_repo = NULL;
+ object_type = GIT_OBJECT_BLOB;
+ cache_limit = 0;
+}
+
+void test_object_cache__initialize_cache_tiny_blobs(void)
+{
+ g_repo = NULL;
+ object_type = GIT_OBJECT_BLOB;
+ cache_limit = 10;
+}
+
+void test_object_cache__initialize_cache_all_blobs(void)
+{
+ g_repo = NULL;
+ object_type = GIT_OBJECT_BLOB;
+ cache_limit = 32767;
+}
+
+void test_object_cache__initialize_cache_no_trees(void)
+{
+ g_repo = NULL;
+ object_type = GIT_OBJECT_TREE;
+ cache_limit = 0;
+}
+
+void test_object_cache__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJECT_BLOB, (size_t)0);
+ git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJECT_TREE, (size_t)4096);
+ git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJECT_COMMIT, (size_t)4096);
+}
+
+static struct {
+ git_object_t type;
+ const char *sha;
+ size_t size;
+} g_data[] = {
+ /* HEAD */
+ { GIT_OBJECT_BLOB, "a8233120f6ad708f843d861ce2b7228ec4e3dec6", 10 }, /* README */
+ { GIT_OBJECT_BLOB, "3697d64be941a53d4ae8f6a271e4e3fa56b022cc", 8 }, /* branch_file.txt */
+ { GIT_OBJECT_BLOB, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd", 12 }, /* new.txt */
+
+ /* refs/heads/subtrees */
+ { GIT_OBJECT_BLOB, "1385f264afb75a56a5bec74243be9b367ba4ca08", 4 }, /* README */
+ { GIT_OBJECT_TREE, "f1425cef211cc08caa31e7b545ffb232acb098c3", 90 }, /* ab */
+ { GIT_OBJECT_BLOB, "d6c93164c249c8000205dd4ec5cbca1b516d487f", 6 }, /* ab/4.txt */
+ { GIT_OBJECT_TREE, "9a03079b8a8ee85a0bee58bf9be3da8b62414ed4", 33 }, /* ab/c */
+ { GIT_OBJECT_BLOB, "270b8ea76056d5cad83af921837702d3e3c2924d", 6 }, /* ab/c/3.txt */
+ { GIT_OBJECT_TREE, "b6361fc6a97178d8fc8639fdeed71c775ab52593", 63 }, /* ab/de */
+ { GIT_OBJECT_BLOB, "e7b4ad382349ff96dd8199000580b9b1e2042eb0", 6 }, /* ab/de/2.txt */
+ { GIT_OBJECT_TREE, "3259a6bd5b57fb9c1281bb7ed3167b50f224cb54", 33 }, /* ab/de/fgh */
+ { GIT_OBJECT_BLOB, "1f67fc4386b2d171e0d21be1c447e12660561f9b", 6 }, /* ab/de/fgh/1.txt */
+ { GIT_OBJECT_BLOB, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057", 3 }, /* branch_file.txt */
+ { GIT_OBJECT_BLOB, "fa49b077972391ad58037050f2a75f74e3671e92", 9 }, /* new.txt */
+
+ /* refs/heads/chomped */
+ { GIT_OBJECT_BLOB, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", 51 }, /* readme.txt */
+
+ { 0, NULL, 0 },
+ { 0, NULL, 0 }
+};
+
+void test_object_cache__cache_counts(void)
+{
+ int i, start, nonmatching = 0;
+ git_oid oid;
+ git_odb_object *odb_obj;
+ git_object *obj;
+ git_odb *odb;
+
+ git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, object_type, cache_limit);
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_odb(&odb, g_repo));
+
+ start = (int)git_cache_size(&g_repo->objects);
+
+ for (i = 0; g_data[i].sha != NULL; ++i) {
+ int count = (int)git_cache_size(&g_repo->objects);
+
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+
+ /* alternate between loading raw and parsed objects */
+ if ((i & 1) == 0) {
+ cl_git_pass(git_odb_read(&odb_obj, odb, &oid));
+ cl_assert(g_data[i].type == git_odb_object_type(odb_obj));
+ git_odb_object_free(odb_obj);
+ } else {
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+ }
+
+ if ((g_data[i].type == object_type && g_data[i].size >= cache_limit) ||
+ (g_data[i].type != object_type && g_data[i].type == GIT_OBJECT_BLOB))
+ cl_assert_equal_i(count, (int)git_cache_size(&g_repo->objects));
+ else {
+ cl_assert_equal_i(count + 1, (int)git_cache_size(&g_repo->objects));
+ nonmatching++;
+ }
+ }
+
+ cl_assert_equal_i(nonmatching, (int)git_cache_size(&g_repo->objects) - start);
+
+ for (i = 0; g_data[i].sha != NULL; ++i) {
+ int count = (int)git_cache_size(&g_repo->objects);
+
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+
+ cl_assert_equal_i(count, (int)git_cache_size(&g_repo->objects));
+ }
+
+ git_odb_free(odb);
+}
+
+static void *cache_parsed(void *arg)
+{
+ int i;
+ git_oid oid;
+ git_object *obj;
+
+ for (i = ((int *)arg)[1]; g_data[i].sha != NULL; i += 2) {
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+ }
+
+ for (i = 0; i < ((int *)arg)[1]; i += 2) {
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+ }
+
+ return arg;
+}
+
+static void *cache_raw(void *arg)
+{
+ int i;
+ git_oid oid;
+ git_odb *odb;
+ git_odb_object *odb_obj;
+
+ cl_git_pass(git_repository_odb(&odb, g_repo));
+
+ for (i = ((int *)arg)[1]; g_data[i].sha != NULL; i += 2) {
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+ cl_git_pass(git_odb_read(&odb_obj, odb, &oid));
+ cl_assert(g_data[i].type == git_odb_object_type(odb_obj));
+ git_odb_object_free(odb_obj);
+ }
+
+ for (i = 0; i < ((int *)arg)[1]; i += 2) {
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+ cl_git_pass(git_odb_read(&odb_obj, odb, &oid));
+ cl_assert(g_data[i].type == git_odb_object_type(odb_obj));
+ git_odb_object_free(odb_obj);
+ }
+
+ git_odb_free(odb);
+
+ return arg;
+}
+
+#define REPEAT 20
+#define THREADCOUNT 50
+
+void test_object_cache__threadmania(void)
+{
+ int try, th, max_i;
+ void *data;
+ void *(*fn)(void *);
+
+#ifdef GIT_THREADS
+ git_thread t[THREADCOUNT];
+#endif
+
+ for (max_i = 0; g_data[max_i].sha != NULL; ++max_i)
+ /* count up */;
+
+ for (try = 0; try < REPEAT; ++try) {
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+
+ for (th = 0; th < THREADCOUNT; ++th) {
+ data = git__malloc(2 * sizeof(int));
+
+ ((int *)data)[0] = th;
+ ((int *)data)[1] = th % max_i;
+
+ fn = (th & 1) ? cache_parsed : cache_raw;
+
+#ifdef GIT_THREADS
+ cl_git_pass(git_thread_create(&t[th], fn, data));
+#else
+ cl_assert(fn(data) == data);
+ git__free(data);
+#endif
+ }
+
+#ifdef GIT_THREADS
+ for (th = 0; th < THREADCOUNT; ++th) {
+ cl_git_pass(git_thread_join(&t[th], &data));
+ cl_assert_equal_i(th, ((int *)data)[0]);
+ git__free(data);
+ }
+#endif
+
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+}
+
+static void *cache_quick(void *arg)
+{
+ git_oid oid;
+ git_object *obj;
+
+ cl_git_pass(git_oid_fromstr(&oid, g_data[4].sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+ cl_assert(g_data[4].type == git_object_type(obj));
+ git_object_free(obj);
+
+ return arg;
+}
+
+void test_object_cache__fast_thread_rush(void)
+{
+ int try, th, data[THREADCOUNT];
+#ifdef GIT_THREADS
+ git_thread t[THREADCOUNT];
+#endif
+
+ for (try = 0; try < REPEAT; ++try) {
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+
+ for (th = 0; th < THREADCOUNT; ++th) {
+ data[th] = th;
+#ifdef GIT_THREADS
+ cl_git_pass(
+ git_thread_create(&t[th], cache_quick, &data[th]));
+#else
+ cl_assert(cache_quick(&data[th]) == &data[th]);
+#endif
+ }
+
+#ifdef GIT_THREADS
+ for (th = 0; th < THREADCOUNT; ++th) {
+ void *rval;
+ cl_git_pass(git_thread_join(&t[th], &rval));
+ cl_assert_equal_i(th, *((int *)rval));
+ }
+#endif
+
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+}
diff --git a/tests/libgit2/object/commit/commitstagedfile.c b/tests/libgit2/object/commit/commitstagedfile.c
new file mode 100644
index 000000000..7322a4e86
--- /dev/null
+++ b/tests/libgit2/object/commit/commitstagedfile.c
@@ -0,0 +1,218 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+static git_repository *repo;
+
+void test_object_commit_commitstagedfile__initialize(void)
+{
+ cl_fixture("treebuilder");
+ cl_git_pass(git_repository_init(&repo, "treebuilder/", 0));
+ cl_assert(repo != NULL);
+}
+
+void test_object_commit_commitstagedfile__cleanup(void)
+{
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("treebuilder");
+}
+
+void test_object_commit_commitstagedfile__generate_predictable_object_ids(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_oid expected_blob_oid, tree_oid, expected_tree_oid, commit_oid, expected_commit_oid;
+ git_signature *signature;
+ git_tree *tree;
+ git_buf buffer = GIT_BUF_INIT;
+
+ /*
+ * The test below replicates the following git scenario
+ *
+ * $ echo "test" > test.txt
+ * $ git hash-object test.txt
+ * 9daeafb9864cf43055ae93beb0afd6c7d144bfa4
+ *
+ * $ git add .
+ * $ git commit -m "Initial commit"
+ *
+ * $ git log
+ * commit 1fe3126578fc4eca68c193e4a3a0a14a0704624d
+ * Author: nulltoken <emeric.fermas@gmail.com>
+ * Date: Wed Dec 14 08:29:03 2011 +0100
+ *
+ * Initial commit
+ *
+ * $ git show 1fe3 --format=raw
+ * commit 1fe3126578fc4eca68c193e4a3a0a14a0704624d
+ * tree 2b297e643c551e76cfa1f93810c50811382f9117
+ * author nulltoken <emeric.fermas@gmail.com> 1323847743 +0100
+ * committer nulltoken <emeric.fermas@gmail.com> 1323847743 +0100
+ *
+ * Initial commit
+ *
+ * diff --git a/test.txt b/test.txt
+ * new file mode 100644
+ * index 0000000..9daeafb
+ * --- /dev/null
+ * +++ b/test.txt
+ * @@ -0,0 +1 @@
+ * +test
+ *
+ * $ git ls-tree 2b297
+ * 100644 blob 9daeafb9864cf43055ae93beb0afd6c7d144bfa4 test.txt
+ */
+
+ cl_git_pass(git_oid_fromstr(&expected_commit_oid, "1fe3126578fc4eca68c193e4a3a0a14a0704624d"));
+ cl_git_pass(git_oid_fromstr(&expected_tree_oid, "2b297e643c551e76cfa1f93810c50811382f9117"));
+ cl_git_pass(git_oid_fromstr(&expected_blob_oid, "9daeafb9864cf43055ae93beb0afd6c7d144bfa4"));
+
+ /*
+ * Add a new file to the index
+ */
+ cl_git_mkfile("treebuilder/test.txt", "test\n");
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ entry = git_index_get_byindex(index, 0);
+
+ cl_assert(git_oid_cmp(&expected_blob_oid, &entry->id) == 0);
+
+ /*
+ * Information about index entry should match test file
+ */
+ {
+ struct stat st;
+ cl_must_pass(p_lstat("treebuilder/test.txt", &st));
+ cl_assert(entry->file_size == st.st_size);
+#ifndef _WIN32
+ /*
+ * Windows doesn't populate these fields, and the signage is
+ * wrong in the Windows version of the struct, so lets avoid
+ * the "comparing signed and unsigned" compilation warning in
+ * that case.
+ */
+ cl_assert(entry->uid == st.st_uid);
+ cl_assert(entry->gid == st.st_gid);
+#endif
+ }
+
+ /*
+ * Build the tree from the index
+ */
+ cl_git_pass(git_index_write_tree(&tree_oid, index));
+
+ cl_assert(git_oid_cmp(&expected_tree_oid, &tree_oid) == 0);
+
+ /*
+ * Commit the staged file
+ */
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
+
+ cl_git_pass(git_message_prettify(&buffer, "Initial commit", 0, '#'));
+
+ cl_git_pass(git_commit_create_v(
+ &commit_oid,
+ repo,
+ "HEAD",
+ signature,
+ signature,
+ NULL,
+ buffer.ptr,
+ tree,
+ 0));
+
+ cl_assert(git_oid_cmp(&expected_commit_oid, &commit_oid) == 0);
+
+ git_buf_dispose(&buffer);
+ git_signature_free(signature);
+ git_tree_free(tree);
+ git_index_free(index);
+}
+
+static void assert_commit_tree_has_n_entries(git_commit *c, int count)
+{
+ git_tree *tree;
+ cl_git_pass(git_commit_tree(&tree, c));
+ cl_assert_equal_i(count, git_tree_entrycount(tree));
+ git_tree_free(tree);
+}
+
+static void assert_commit_is_head_(git_commit *c, const char *file, const char *func, int line)
+{
+ git_commit *head;
+ cl_git_pass(git_revparse_single((git_object **)&head, repo, "HEAD"));
+ clar__assert(git_oid_equal(git_commit_id(c), git_commit_id(head)), file, func, line, "Commit is not the HEAD", NULL, 1);
+ git_commit_free(head);
+}
+#define assert_commit_is_head(C) assert_commit_is_head_((C),__FILE__,__func__,__LINE__)
+
+void test_object_commit_commitstagedfile__amend_commit(void)
+{
+ git_index *index;
+ git_oid old_oid, new_oid, tree_oid;
+ git_commit *old_commit, *new_commit;
+ git_tree *tree;
+
+ /* make a commit */
+
+ cl_git_mkfile("treebuilder/myfile", "This is a file\n");
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "myfile"));
+ cl_repo_commit_from_index(&old_oid, repo, NULL, 0, "first commit");
+
+ cl_git_pass(git_commit_lookup(&old_commit, repo, &old_oid));
+
+ cl_assert_equal_i(0, git_commit_parentcount(old_commit));
+ assert_commit_tree_has_n_entries(old_commit, 1);
+ assert_commit_is_head(old_commit);
+
+ /* let's amend the message of the HEAD commit */
+
+ cl_git_pass(git_commit_amend(
+ &new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", NULL));
+
+ /* fail because the commit isn't the tip of the branch anymore */
+ cl_git_fail(git_commit_amend(
+ &new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", NULL));
+
+ cl_git_pass(git_commit_lookup(&new_commit, repo, &new_oid));
+
+ cl_assert_equal_i(0, git_commit_parentcount(new_commit));
+ assert_commit_tree_has_n_entries(new_commit, 1);
+ assert_commit_is_head(new_commit);
+
+ git_commit_free(old_commit);
+
+ old_commit = new_commit;
+
+ /* let's amend the tree of that last commit */
+
+ cl_git_mkfile("treebuilder/anotherfile", "This is another file\n");
+ cl_git_pass(git_index_add_bypath(index, "anotherfile"));
+ cl_git_pass(git_index_write_tree(&tree_oid, index));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
+ cl_assert_equal_i(2, git_tree_entrycount(tree));
+
+ /* fail to amend on a ref which does not exist */
+ cl_git_fail_with(GIT_ENOTFOUND, git_commit_amend(
+ &new_oid, old_commit, "refs/heads/nope", NULL, NULL, NULL, "Initial commit", tree));
+
+ cl_git_pass(git_commit_amend(
+ &new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", tree));
+ git_tree_free(tree);
+
+ cl_git_pass(git_commit_lookup(&new_commit, repo, &new_oid));
+
+ cl_assert_equal_i(0, git_commit_parentcount(new_commit));
+ assert_commit_tree_has_n_entries(new_commit, 2);
+ assert_commit_is_head(new_commit);
+
+ /* cleanup */
+
+ git_commit_free(old_commit);
+ git_commit_free(new_commit);
+ git_index_free(index);
+}
diff --git a/tests/libgit2/object/commit/parse.c b/tests/libgit2/object/commit/parse.c
new file mode 100644
index 000000000..4ff1ad62a
--- /dev/null
+++ b/tests/libgit2/object/commit/parse.c
@@ -0,0 +1,232 @@
+#include "clar_libgit2.h"
+#include "commit.h"
+#include "object.h"
+#include "signature.h"
+
+static void assert_commit_parses(const char *data, size_t datalen,
+ const char *expected_treeid,
+ const char *expected_author,
+ const char *expected_committer,
+ const char *expected_encoding,
+ const char *expected_message,
+ size_t expected_parents)
+{
+ git_commit *commit;
+ if (!datalen)
+ datalen = strlen(data);
+ cl_git_pass(git_object__from_raw((git_object **) &commit, data, datalen, GIT_OBJECT_COMMIT));
+
+ if (expected_author) {
+ git_signature *author;
+ cl_git_pass(git_signature_from_buffer(&author, expected_author));
+ cl_assert(git_signature__equal(author, commit->author));
+ cl_assert_equal_s(author->name, commit->author->name);
+ cl_assert_equal_s(author->email, commit->author->email);
+ cl_assert_equal_i(author->when.time, commit->author->when.time);
+ cl_assert_equal_i(author->when.offset, commit->author->when.offset);
+ cl_assert_equal_i(author->when.sign, commit->author->when.sign);
+ git_signature_free(author);
+ }
+
+ if (expected_committer) {
+ git_signature *committer;
+ cl_git_pass(git_signature_from_buffer(&committer, expected_committer));
+ cl_assert_equal_s(committer->name, commit->committer->name);
+ cl_assert_equal_s(committer->email, commit->committer->email);
+ cl_assert_equal_i(committer->when.time, commit->committer->when.time);
+ cl_assert_equal_i(committer->when.offset, commit->committer->when.offset);
+ cl_assert_equal_i(committer->when.sign, commit->committer->when.sign);
+ git_signature_free(committer);
+ }
+
+ if (expected_encoding)
+ cl_assert_equal_s(commit->message_encoding, expected_encoding);
+ else
+ cl_assert_equal_p(commit->message_encoding, NULL);
+
+ if (expected_message)
+ cl_assert_equal_s(commit->raw_message, expected_message);
+ else
+ cl_assert_equal_p(commit->message_encoding, NULL);
+
+ if (expected_treeid) {
+ git_oid tree_oid;
+ cl_git_pass(git_oid_fromstr(&tree_oid, expected_treeid));
+ cl_assert_equal_oid(&tree_oid, &commit->tree_id);
+ }
+
+ cl_assert_equal_i(commit->parent_ids.size, expected_parents);
+
+ git_object__free(&commit->object);
+}
+
+static void assert_commit_fails(const char *data, size_t datalen)
+{
+ git_object *object;
+ if (!datalen)
+ datalen = strlen(data);
+ cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_COMMIT));
+}
+
+void test_object_commit_parse__parsing_commit_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "encoding Encoding\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ "Encoding",
+ "Message", 0);
+}
+
+void test_object_commit_parse__parsing_commit_without_encoding_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "Message", 0);
+}
+
+void test_object_commit_parse__parsing_commit_with_multiple_authors_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author1 <author@example.com>\n"
+ "author Author2 <author@example.com>\n"
+ "author Author3 <author@example.com>\n"
+ "author Author4 <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author1 <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "Message", 0);
+}
+
+void test_object_commit_parse__parsing_commit_with_multiple_committers_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer1 <committer@example.com>\n"
+ "committer Committer2 <committer@example.com>\n"
+ "committer Committer3 <committer@example.com>\n"
+ "committer Committer4 <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author <author@example.com>",
+ "Committer1 <committer@example.com>",
+ NULL,
+ "Message", 0);
+}
+
+void test_object_commit_parse__parsing_commit_without_message_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n";
+ assert_commit_parses(commit, 0,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "", 0);
+}
+
+void test_object_commit_parse__parsing_commit_with_unknown_fields_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "foo bar\n"
+ "more garbage\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "Message", 0);
+}
+
+void test_object_commit_parse__parsing_commit_with_invalid_tree_fails(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1xxx5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0);
+}
+
+void test_object_commit_parse__parsing_commit_without_tree_fails(void)
+{
+ const char *commit =
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0);
+}
+
+void test_object_commit_parse__parsing_commit_without_author_fails(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0);
+}
+
+void test_object_commit_parse__parsing_commit_without_committer_fails(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0);
+}
+
+void test_object_commit_parse__parsing_encoding_will_not_cause_oob_read(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author <>\n"
+ "committer <>\n"
+ "encoding foo\n";
+ /*
+ * As we ignore unknown fields, the cut-off encoding field will be
+ * parsed just fine.
+ */
+ assert_commit_parses(commit, strlen(commit) - strlen("ncoding foo\n"),
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "<>",
+ "<>",
+ NULL,
+ "", 0);
+}
diff --git a/tests/libgit2/object/lookup.c b/tests/libgit2/object/lookup.c
new file mode 100644
index 000000000..a7b1ceeb4
--- /dev/null
+++ b/tests/libgit2/object/lookup.c
@@ -0,0 +1,122 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+static git_repository *g_repo;
+
+void test_object_lookup__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_object_lookup__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_lookup__lookup_wrong_type_returns_enotfound(void)
+{
+ const char *commit = "e90810b8df3e80c413d903f631643c716887138d";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid_fromstr(&oid, commit));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_TAG));
+}
+
+void test_object_lookup__lookup_nonexisting_returns_enotfound(void)
+{
+ const char *unknown = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid_fromstr(&oid, unknown));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_ANY));
+}
+
+void test_object_lookup__lookup_wrong_type_by_abbreviated_id_returns_enotfound(void)
+{
+ const char *commit = "e90810b";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid_fromstrn(&oid, commit, strlen(commit)));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup_prefix(&object, g_repo, &oid, strlen(commit), GIT_OBJECT_TAG));
+}
+
+void test_object_lookup__lookup_wrong_type_eventually_returns_enotfound(void)
+{
+ const char *commit = "e90810b8df3e80c413d903f631643c716887138d";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid_fromstr(&oid, commit));
+
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ git_object_free(object);
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_TAG));
+}
+
+void test_object_lookup__lookup_corrupt_object_returns_error(void)
+{
+ const char *commit = "8e73b769e97678d684b809b163bebdae2911720f",
+ *file = "objects/8e/73b769e97678d684b809b163bebdae2911720f";
+ git_str path = GIT_STR_INIT, contents = GIT_STR_INIT;
+ git_oid oid;
+ git_object *object;
+ size_t i;
+
+ cl_git_pass(git_oid_fromstr(&oid, commit));
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(g_repo), file));
+ cl_git_pass(git_futils_readbuffer(&contents, path.ptr));
+
+ /* Corrupt and try to read the object */
+ for (i = 0; i < contents.size; i++) {
+ contents.ptr[i] ^= 0x1;
+ cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+ cl_git_fail(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ contents.ptr[i] ^= 0x1;
+ }
+
+ /* Restore original content and assert we can read the object */
+ cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+
+ git_object_free(object);
+ git_str_dispose(&path);
+ git_str_dispose(&contents);
+}
+
+void test_object_lookup__lookup_object_with_wrong_hash_returns_error(void)
+{
+ const char *oldloose = "objects/8e/73b769e97678d684b809b163bebdae2911720f",
+ *newloose = "objects/8e/73b769e97678d684b809b163bebdae2911720e",
+ *commit = "8e73b769e97678d684b809b163bebdae2911720e";
+ git_str oldpath = GIT_STR_INIT, newpath = GIT_STR_INIT;
+ git_object *object;
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, commit));
+
+ /* Copy object to another location with wrong hash */
+ cl_git_pass(git_str_joinpath(&oldpath, git_repository_path(g_repo), oldloose));
+ cl_git_pass(git_str_joinpath(&newpath, git_repository_path(g_repo), newloose));
+ cl_git_pass(git_futils_cp(oldpath.ptr, newpath.ptr, 0644));
+
+ /* Verify that lookup fails due to a hashsum mismatch */
+ cl_git_fail_with(GIT_EMISMATCH, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+
+ /* Disable verification and try again */
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1));
+
+ git_object_free(object);
+ git_str_dispose(&oldpath);
+ git_str_dispose(&newpath);
+}
diff --git a/tests/libgit2/object/lookupbypath.c b/tests/libgit2/object/lookupbypath.c
new file mode 100644
index 000000000..f2257f556
--- /dev/null
+++ b/tests/libgit2/object/lookupbypath.c
@@ -0,0 +1,83 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+static git_repository *g_repo;
+static git_tree *g_root_tree;
+static git_commit *g_head_commit;
+static git_object *g_expectedobject,
+ *g_actualobject;
+
+void test_object_lookupbypath__initialize(void)
+{
+ git_reference *head;
+ git_tree_entry *tree_entry;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("attr/.gitted")));
+
+ cl_git_pass(git_repository_head(&head, g_repo));
+ cl_git_pass(git_reference_peel((git_object**)&g_head_commit, head, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_commit_tree(&g_root_tree, g_head_commit));
+ cl_git_pass(git_tree_entry_bypath(&tree_entry, g_root_tree, "subdir/subdir_test2.txt"));
+ cl_git_pass(git_object_lookup(&g_expectedobject, g_repo, git_tree_entry_id(tree_entry),
+ GIT_OBJECT_ANY));
+
+ git_tree_entry_free(tree_entry);
+ git_reference_free(head);
+
+ g_actualobject = NULL;
+}
+void test_object_lookupbypath__cleanup(void)
+{
+ git_object_free(g_actualobject);
+ git_object_free(g_expectedobject);
+ git_tree_free(g_root_tree);
+ git_commit_free(g_head_commit);
+ g_expectedobject = NULL;
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+void test_object_lookupbypath__errors(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+ "subdir/subdir_test2.txt", GIT_OBJECT_TREE)); /* It's not a tree */
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+ "file/doesnt/exist", GIT_OBJECT_ANY));
+}
+
+void test_object_lookupbypath__from_root_tree(void)
+{
+ cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+ "subdir/subdir_test2.txt", GIT_OBJECT_BLOB));
+ cl_assert_equal_oid(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject));
+}
+
+void test_object_lookupbypath__from_head_commit(void)
+{
+ cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_head_commit,
+ "subdir/subdir_test2.txt", GIT_OBJECT_BLOB));
+ cl_assert_equal_oid(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject));
+}
+
+void test_object_lookupbypath__from_subdir_tree(void)
+{
+ git_tree_entry *entry = NULL;
+ git_tree *tree = NULL;
+
+ cl_git_pass(git_tree_entry_bypath(&entry, g_root_tree, "subdir"));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, git_tree_entry_id(entry)));
+
+ cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)tree,
+ "subdir_test2.txt", GIT_OBJECT_BLOB));
+ cl_assert_equal_oid(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject));
+
+ git_tree_entry_free(entry);
+ git_tree_free(tree);
+}
+
diff --git a/tests/libgit2/object/message.c b/tests/libgit2/object/message.c
new file mode 100644
index 000000000..d87c8ef70
--- /dev/null
+++ b/tests/libgit2/object/message.c
@@ -0,0 +1,197 @@
+#include "clar_libgit2.h"
+
+static void assert_message_prettifying(char *expected_output, char *input, int strip_comments)
+{
+ git_buf prettified_message = GIT_BUF_INIT;
+
+ git_message_prettify(&prettified_message, input, strip_comments, '#');
+ cl_assert_equal_s(expected_output, prettified_message.ptr);
+
+ git_buf_dispose(&prettified_message);
+}
+
+#define t40 "A quick brown fox jumps over the lazy do"
+#define s40 " "
+#define sss s40 s40 s40 s40 s40 s40 s40 s40 s40 s40 /* # 400 */
+#define ttt t40 t40 t40 t40 t40 t40 t40 t40 t40 t40 /* # 400 */
+
+/* Ported from git.git */
+/* see https://github.com/git/git/blob/master/t/t0030-stripspace.sh */
+void test_object_message__long_lines_without_spaces_should_be_unchanged(void)
+{
+ assert_message_prettifying(ttt "\n", ttt, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", ttt ttt ttt ttt, 0);
+}
+
+void test_object_message__lines_with_spaces_at_the_beginning_should_be_unchanged(void)
+{
+ assert_message_prettifying(sss ttt "\n", sss ttt, 0);
+ assert_message_prettifying(sss sss ttt "\n", sss sss ttt, 0);
+ assert_message_prettifying(sss sss sss ttt "\n", sss sss sss ttt, 0);
+}
+
+void test_object_message__lines_with_intermediate_spaces_should_be_unchanged(void)
+{
+ assert_message_prettifying(ttt sss ttt "\n", ttt sss ttt, 0);
+ assert_message_prettifying(ttt sss sss ttt "\n", ttt sss sss ttt, 0);
+}
+
+void test_object_message__consecutive_blank_lines_should_be_unified(void)
+{
+ assert_message_prettifying(ttt "\n\n" ttt "\n", ttt "\n\n\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n\n" ttt "\n", ttt ttt "\n\n\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n\n" ttt "\n", ttt ttt ttt "\n\n\n\n\n" ttt "\n", 0);
+
+ assert_message_prettifying(ttt "\n\n" ttt ttt "\n", ttt "\n\n\n\n\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n\n" ttt ttt ttt "\n", ttt "\n\n\n\n\n" ttt ttt ttt "\n", 0);
+
+ assert_message_prettifying(ttt "\n\n" ttt "\n", ttt "\n\t\n \n\n \t\t\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n\n" ttt "\n", ttt ttt "\n\t\n \n\n \t\t\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n\n" ttt "\n", ttt ttt ttt "\n\t\n \n\n \t\t\n" ttt "\n", 0);
+
+ assert_message_prettifying(ttt "\n\n" ttt ttt "\n", ttt "\n\t\n \n\n \t\t\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n\n" ttt ttt ttt "\n", ttt "\n\t\n \n\n \t\t\n" ttt ttt ttt "\n", 0);
+}
+
+void test_object_message__only_consecutive_blank_lines_should_be_completely_removed(void)
+{
+ assert_message_prettifying("", "\n", 0);
+ assert_message_prettifying("", "\n\n\n", 0);
+ assert_message_prettifying("", sss "\n" sss "\n" sss "\n", 0);
+ assert_message_prettifying("", sss sss "\n" sss "\n\n", 0);
+}
+
+void test_object_message__consecutive_blank_lines_at_the_beginning_should_be_removed(void)
+{
+ assert_message_prettifying(ttt "\n", "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n", "\n\n\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n", "\n\n\n" ttt ttt ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", "\n\n\n" ttt ttt ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", sss "\n" sss "\n" sss "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n" sss "\n" sss sss "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", sss sss "\n" sss "\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", sss sss sss "\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n" sss sss sss "\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n\n" sss sss sss "\n" ttt "\n", 0);
+}
+
+void test_object_message__consecutive_blank_lines_at_the_end_should_be_removed(void)
+{
+ assert_message_prettifying(ttt "\n", ttt "\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", ttt ttt ttt ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n" sss "\n" sss "\n" sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n" sss "\n" sss sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n" sss sss "\n" sss "\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n" sss sss sss "\n\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n" sss sss sss "\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n\n" sss sss sss "\n\n", 0);
+}
+
+void test_object_message__text_without_newline_at_end_should_end_with_newline(void)
+{
+ assert_message_prettifying(ttt "\n", ttt, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", ttt ttt ttt ttt, 0);
+}
+
+void test_object_message__text_plus_spaces_without_newline_should_not_show_spaces_and_end_with_newline(void)
+{
+ assert_message_prettifying(ttt "\n", ttt sss, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss, 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt sss, 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss sss, 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss sss, 0);
+}
+
+void test_object_message__text_plus_spaces_ending_with_newline_should_be_cleaned_and_newline_must_remain(void){
+ assert_message_prettifying(ttt "\n", ttt sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss sss "\n", 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss "\n", 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss sss "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt sss "\n", 0);
+}
+
+void test_object_message__spaces_with_newline_at_end_should_be_replaced_with_empty_string(void)
+{
+ assert_message_prettifying("", sss "\n", 0);
+ assert_message_prettifying("", sss sss "\n", 0);
+ assert_message_prettifying("", sss sss sss "\n", 0);
+ assert_message_prettifying("", sss sss sss sss "\n", 0);
+}
+
+void test_object_message__spaces_without_newline_at_end_should_be_replaced_with_empty_string(void)
+{
+ assert_message_prettifying("", "", 0);
+ assert_message_prettifying("", sss sss, 0);
+ assert_message_prettifying("", sss sss sss, 0);
+ assert_message_prettifying("", sss sss sss sss, 0);
+}
+
+void test_object_message__consecutive_text_lines_should_be_unchanged(void)
+{
+ assert_message_prettifying(ttt ttt "\n" ttt "\n", ttt ttt "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt ttt "\n" ttt "\n", ttt "\n" ttt ttt "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt "\n" ttt "\n" ttt ttt "\n", ttt "\n" ttt "\n" ttt "\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt "\n\n" ttt ttt "\n" ttt "\n", ttt "\n" ttt "\n\n" ttt ttt "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n\n" ttt "\n" ttt ttt "\n", ttt ttt "\n\n" ttt "\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt ttt "\n\n" ttt "\n", ttt "\n" ttt ttt "\n\n" ttt "\n", 0);
+}
+
+void test_object_message__strip_comments(void)
+{
+ assert_message_prettifying("", "# comment", 1);
+ assert_message_prettifying("", "# comment\n", 1);
+ assert_message_prettifying("", "# comment \n", 1);
+
+ assert_message_prettifying(ttt "\n", ttt "\n" "# comment\n", 1);
+ assert_message_prettifying(ttt "\n", "# comment\n" ttt "\n", 1);
+ assert_message_prettifying(ttt "\n" ttt "\n", ttt "\n" "# comment\n" ttt "\n", 1);
+}
+
+void test_object_message__keep_comments(void)
+{
+ assert_message_prettifying("# comment\n", "# comment", 0);
+ assert_message_prettifying("# comment\n", "# comment\n", 0);
+ assert_message_prettifying("# comment\n", "# comment \n", 0);
+
+ assert_message_prettifying(ttt "\n" "# comment\n", ttt "\n" "# comment\n", 0);
+ assert_message_prettifying("# comment\n" ttt "\n", "# comment\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" "# comment\n" ttt "\n", ttt "\n" "# comment\n" ttt "\n", 0);
+}
+
+void test_object_message__message_prettify(void)
+{
+ git_buf buffer;
+
+ memset(&buffer, 0, sizeof(buffer));
+ cl_git_pass(git_message_prettify(&buffer, "", 0, '#'));
+ cl_assert_equal_s(buffer.ptr, "");
+ git_buf_dispose(&buffer);
+ cl_git_pass(git_message_prettify(&buffer, "", 1, '#'));
+ cl_assert_equal_s(buffer.ptr, "");
+ git_buf_dispose(&buffer);
+
+ cl_git_pass(git_message_prettify(&buffer, "Short", 0, '#'));
+ cl_assert_equal_s("Short\n", buffer.ptr);
+ git_buf_dispose(&buffer);
+ cl_git_pass(git_message_prettify(&buffer, "Short", 1, '#'));
+ cl_assert_equal_s("Short\n", buffer.ptr);
+ git_buf_dispose(&buffer);
+
+ cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 0, '#'));
+ cl_assert_equal_s(buffer.ptr, "This is longer\nAnd multiline\n# with some comments still in\n");
+ git_buf_dispose(&buffer);
+
+ cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 1, '#'));
+ cl_assert_equal_s(buffer.ptr, "This is longer\nAnd multiline\n");
+ git_buf_dispose(&buffer);
+}
diff --git a/tests/libgit2/object/peel.c b/tests/libgit2/object/peel.c
new file mode 100644
index 000000000..8be6e42d4
--- /dev/null
+++ b/tests/libgit2/object/peel.c
@@ -0,0 +1,118 @@
+#include "clar_libgit2.h"
+
+static git_repository *g_repo;
+
+void test_object_peel__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+}
+
+void test_object_peel__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+static void assert_peel(
+ const char *sha,
+ git_object_t requested_type,
+ const char* expected_sha,
+ git_object_t expected_type)
+{
+ git_oid oid, expected_oid;
+ git_object *obj;
+ git_object *peeled;
+
+ cl_git_pass(git_oid_fromstr(&oid, sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_object_peel(&peeled, obj, requested_type));
+
+ cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha));
+ cl_assert_equal_oid(&expected_oid, git_object_id(peeled));
+
+ cl_assert_equal_i(expected_type, git_object_type(peeled));
+
+ git_object_free(peeled);
+ git_object_free(obj);
+}
+
+static void assert_peel_error(int error, const char *sha, git_object_t requested_type)
+{
+ git_oid oid;
+ git_object *obj;
+ git_object *peeled;
+
+ cl_git_pass(git_oid_fromstr(&oid, sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_assert_equal_i(error, git_object_peel(&peeled, obj, requested_type));
+
+ git_object_free(obj);
+}
+
+void test_object_peel__peeling_an_object_into_its_own_type_returns_another_instance_of_it(void)
+{
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_TAG,
+ "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_TAG);
+ assert_peel("53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+ assert_peel("0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_BLOB,
+ "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_BLOB);
+}
+
+void test_object_peel__tag(void)
+{
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_COMMIT,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+ assert_peel_error(GIT_EPEEL, "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_BLOB);
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_ANY,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+}
+
+void test_object_peel__commit(void)
+{
+ assert_peel_error(GIT_EINVALIDSPEC, "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_BLOB);
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+ assert_peel_error(GIT_EINVALIDSPEC, "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_TAG);
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_ANY,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+}
+
+void test_object_peel__tree(void)
+{
+ assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_BLOB);
+ assert_peel("53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+ assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_COMMIT);
+ assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TAG);
+ assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_ANY);
+}
+
+void test_object_peel__blob(void)
+{
+ assert_peel("0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_BLOB,
+ "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_BLOB);
+ assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_TREE);
+ assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_COMMIT);
+ assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_TAG);
+ assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_ANY);
+}
+
+void test_object_peel__target_any_object_for_type_change(void)
+{
+ /* tag to commit */
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_ANY,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+
+ /* commit to tree */
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_ANY,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+}
diff --git a/tests/libgit2/object/raw/chars.c b/tests/libgit2/object/raw/chars.c
new file mode 100644
index 000000000..cde0bdbf6
--- /dev/null
+++ b/tests/libgit2/object/raw/chars.c
@@ -0,0 +1,41 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_chars__find_invalid_chars_in_oid(void)
+{
+ git_oid out;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ char in[] = "16a67770b7d8d72317c4b775213c23a8bd74f5e0";
+ unsigned int i;
+
+ for (i = 0; i < 256; i++) {
+ in[38] = (char)i;
+ if (git__fromhex(i) >= 0) {
+ exp[19] = (unsigned char)(git__fromhex(i) << 4);
+ cl_git_pass(git_oid_fromstr(&out, in));
+ cl_assert(memcmp(out.id, exp, sizeof(out.id)) == 0);
+ } else {
+ cl_git_fail(git_oid_fromstr(&out, in));
+ }
+ }
+}
+
+void test_object_raw_chars__build_valid_oid_from_raw_bytes(void)
+{
+ git_oid out;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ git_oid_fromraw(&out, exp);
+ cl_git_pass(memcmp(out.id, exp, sizeof(out.id)));
+}
diff --git a/tests/libgit2/object/raw/compare.c b/tests/libgit2/object/raw/compare.c
new file mode 100644
index 000000000..56c016b72
--- /dev/null
+++ b/tests/libgit2/object/raw/compare.c
@@ -0,0 +1,123 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_compare__succeed_on_copy_oid(void)
+{
+ git_oid a, b;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ memset(&b, 0, sizeof(b));
+ git_oid_fromraw(&a, exp);
+ git_oid_cpy(&b, &a);
+ cl_git_pass(memcmp(a.id, exp, sizeof(a.id)));
+}
+
+void test_object_raw_compare__succeed_on_oid_comparison_lesser(void)
+{
+ git_oid a, b;
+ unsigned char a_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ unsigned char b_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xf0,
+ };
+ git_oid_fromraw(&a, a_in);
+ git_oid_fromraw(&b, b_in);
+ cl_assert(git_oid_cmp(&a, &b) < 0);
+}
+
+void test_object_raw_compare__succeed_on_oid_comparison_equal(void)
+{
+ git_oid a, b;
+ unsigned char a_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ git_oid_fromraw(&a, a_in);
+ git_oid_fromraw(&b, a_in);
+ cl_assert(git_oid_cmp(&a, &b) == 0);
+}
+
+void test_object_raw_compare__succeed_on_oid_comparison_greater(void)
+{
+ git_oid a, b;
+ unsigned char a_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ unsigned char b_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xd0,
+ };
+ git_oid_fromraw(&a, a_in);
+ git_oid_fromraw(&b, b_in);
+ cl_assert(git_oid_cmp(&a, &b) > 0);
+}
+
+void test_object_raw_compare__compare_fmt_oids(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char out[GIT_OID_HEXSZ + 1];
+
+ cl_git_pass(git_oid_fromstr(&in, exp));
+
+ /* Format doesn't touch the last byte */
+ out[GIT_OID_HEXSZ] = 'Z';
+ git_oid_fmt(out, &in);
+ cl_assert(out[GIT_OID_HEXSZ] == 'Z');
+
+ /* Format produced the right result */
+ out[GIT_OID_HEXSZ] = '\0';
+ cl_assert_equal_s(exp, out);
+}
+
+void test_object_raw_compare__compare_static_oids(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char *out;
+
+ cl_git_pass(git_oid_fromstr(&in, exp));
+
+ out = git_oid_tostr_s(&in);
+ cl_assert(out);
+ cl_assert_equal_s(exp, out);
+}
+
+void test_object_raw_compare__compare_pathfmt_oids(void)
+{
+ const char *exp1 = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ const char *exp2 = "16/a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char out[GIT_OID_HEXSZ + 2];
+
+ cl_git_pass(git_oid_fromstr(&in, exp1));
+
+ /* Format doesn't touch the last byte */
+ out[GIT_OID_HEXSZ + 1] = 'Z';
+ git_oid_pathfmt(out, &in);
+ cl_assert(out[GIT_OID_HEXSZ + 1] == 'Z');
+
+ /* Format produced the right result */
+ out[GIT_OID_HEXSZ + 1] = '\0';
+ cl_assert_equal_s(exp2, out);
+}
diff --git a/tests/libgit2/object/raw/convert.c b/tests/libgit2/object/raw/convert.c
new file mode 100644
index 000000000..40a01ae09
--- /dev/null
+++ b/tests/libgit2/object/raw/convert.c
@@ -0,0 +1,112 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_convert__succeed_on_oid_to_string_conversion(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char out[GIT_OID_HEXSZ + 1];
+ char *str;
+ int i;
+
+ cl_git_pass(git_oid_fromstr(&in, exp));
+
+ /* NULL buffer pointer, returns static empty string */
+ str = git_oid_tostr(NULL, sizeof(out), &in);
+ cl_assert(str && *str == '\0' && str != out);
+
+ /* zero buffer size, returns static empty string */
+ str = git_oid_tostr(out, 0, &in);
+ cl_assert(str && *str == '\0' && str != out);
+
+ /* NULL oid pointer, sets existing buffer to empty string */
+ str = git_oid_tostr(out, sizeof(out), NULL);
+ cl_assert(str && *str == '\0' && str == out);
+
+ /* n == 1, returns out as an empty string */
+ str = git_oid_tostr(out, 1, &in);
+ cl_assert(str && *str == '\0' && str == out);
+
+ for (i = 1; i < GIT_OID_HEXSZ; i++) {
+ out[i+1] = 'Z';
+ str = git_oid_tostr(out, i+1, &in);
+ /* returns out containing c-string */
+ cl_assert(str && str == out);
+ /* must be '\0' terminated */
+ cl_assert(*(str+i) == '\0');
+ /* must not touch bytes past end of string */
+ cl_assert(*(str+(i+1)) == 'Z');
+ /* i == n-1 characters of string */
+ cl_git_pass(strncmp(exp, out, i));
+ }
+
+ /* returns out as hex formatted c-string */
+ str = git_oid_tostr(out, sizeof(out), &in);
+ cl_assert(str && str == out && *(str+GIT_OID_HEXSZ) == '\0');
+ cl_assert_equal_s(exp, out);
+}
+
+void test_object_raw_convert__succeed_on_oid_to_string_conversion_big(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char big[GIT_OID_HEXSZ + 1 + 3]; /* note + 4 => big buffer */
+ char *str;
+
+ cl_git_pass(git_oid_fromstr(&in, exp));
+
+ /* place some tail material */
+ big[GIT_OID_HEXSZ+0] = 'W'; /* should be '\0' afterwards */
+ big[GIT_OID_HEXSZ+1] = 'X'; /* should remain untouched */
+ big[GIT_OID_HEXSZ+2] = 'Y'; /* ditto */
+ big[GIT_OID_HEXSZ+3] = 'Z'; /* ditto */
+
+ /* returns big as hex formatted c-string */
+ str = git_oid_tostr(big, sizeof(big), &in);
+ cl_assert(str && str == big && *(str+GIT_OID_HEXSZ) == '\0');
+ cl_assert_equal_s(exp, big);
+
+ /* check tail material is untouched */
+ cl_assert(str && str == big && *(str+GIT_OID_HEXSZ+1) == 'X');
+ cl_assert(str && str == big && *(str+GIT_OID_HEXSZ+2) == 'Y');
+ cl_assert(str && str == big && *(str+GIT_OID_HEXSZ+3) == 'Z');
+}
+
+static void check_partial_oid(
+ char *buffer, size_t count, const git_oid *oid, const char *expected)
+{
+ git_oid_nfmt(buffer, count, oid);
+ buffer[count] = '\0';
+ cl_assert_equal_s(expected, buffer);
+}
+
+void test_object_raw_convert__convert_oid_partially(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char big[GIT_OID_HEXSZ + 1 + 3]; /* note + 4 => big buffer */
+
+ cl_git_pass(git_oid_fromstr(&in, exp));
+
+ git_oid_nfmt(big, sizeof(big), &in);
+ cl_assert_equal_s(exp, big);
+
+ git_oid_nfmt(big, GIT_OID_HEXSZ + 1, &in);
+ cl_assert_equal_s(exp, big);
+
+ check_partial_oid(big, 1, &in, "1");
+ check_partial_oid(big, 2, &in, "16");
+ check_partial_oid(big, 3, &in, "16a");
+ check_partial_oid(big, 4, &in, "16a0");
+ check_partial_oid(big, 5, &in, "16a01");
+
+ check_partial_oid(big, GIT_OID_HEXSZ, &in, exp);
+ check_partial_oid(
+ big, GIT_OID_HEXSZ - 1, &in, "16a0123456789abcdef4b775213c23a8bd74f5e");
+ check_partial_oid(
+ big, GIT_OID_HEXSZ - 2, &in, "16a0123456789abcdef4b775213c23a8bd74f5");
+ check_partial_oid(
+ big, GIT_OID_HEXSZ - 3, &in, "16a0123456789abcdef4b775213c23a8bd74f");
+}
diff --git a/tests/libgit2/object/raw/data.h b/tests/libgit2/object/raw/data.h
new file mode 100644
index 000000000..57431e70e
--- /dev/null
+++ b/tests/libgit2/object/raw/data.h
@@ -0,0 +1,323 @@
+
+/*
+ * Raw data
+ */
+static unsigned char commit_data[] = {
+ 0x74, 0x72, 0x65, 0x65, 0x20, 0x64, 0x66, 0x66,
+ 0x32, 0x64, 0x61, 0x39, 0x30, 0x62, 0x32, 0x35,
+ 0x34, 0x65, 0x31, 0x62, 0x65, 0x62, 0x38, 0x38,
+ 0x39, 0x64, 0x31, 0x66, 0x31, 0x66, 0x31, 0x32,
+ 0x38, 0x38, 0x62, 0x65, 0x31, 0x38, 0x30, 0x33,
+ 0x37, 0x38, 0x32, 0x64, 0x66, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
+ 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38,
+ 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30,
+ 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
+ 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
+ 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
+ 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
+ 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
+ 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x3e, 0x0a,
+};
+
+
+static unsigned char tree_data[] = {
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6f,
+ 0x6e, 0x65, 0x00, 0x8b, 0x13, 0x78, 0x91, 0x79,
+ 0x1f, 0xe9, 0x69, 0x27, 0xad, 0x78, 0xe6, 0x4b,
+ 0x0a, 0xad, 0x7b, 0xde, 0xd0, 0x8b, 0xdc, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x6f,
+ 0x6d, 0x65, 0x00, 0xfd, 0x84, 0x30, 0xbc, 0x86,
+ 0x4c, 0xfc, 0xd5, 0xf1, 0x0e, 0x55, 0x90, 0xf8,
+ 0xa4, 0x47, 0xe0, 0x1b, 0x94, 0x2b, 0xfe, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x77,
+ 0x6f, 0x00, 0x78, 0x98, 0x19, 0x22, 0x61, 0x3b,
+ 0x2a, 0xfb, 0x60, 0x25, 0x04, 0x2f, 0xf6, 0xbd,
+ 0x87, 0x8a, 0xc1, 0x99, 0x4e, 0x85, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x7a, 0x65, 0x72,
+ 0x6f, 0x00, 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1,
+ 0xd6, 0x43, 0x4b, 0x8b, 0x29, 0xae, 0x77, 0x5a,
+ 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91,
+};
+
+static unsigned char tag_data[] = {
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x33,
+ 0x64, 0x37, 0x66, 0x38, 0x61, 0x36, 0x61, 0x66,
+ 0x30, 0x37, 0x36, 0x63, 0x38, 0x63, 0x33, 0x66,
+ 0x32, 0x30, 0x30, 0x37, 0x31, 0x61, 0x38, 0x39,
+ 0x33, 0x35, 0x63, 0x64, 0x62, 0x65, 0x38, 0x32,
+ 0x32, 0x38, 0x35, 0x39, 0x34, 0x64, 0x31, 0x0a,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20,
+ 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74,
+ 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
+ 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
+ 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x0a,
+};
+
+/*
+ * Dummy data
+ */
+static unsigned char zero_data[] = {
+ 0x00,
+};
+
+static unsigned char one_data[] = {
+ 0x0a,
+};
+
+static unsigned char two_data[] = {
+ 0x61, 0x0a,
+};
+
+static unsigned char some_data[] = {
+ 0x2f, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20,
+ 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x3b, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61,
+ 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69,
+ 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72,
+ 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a,
+ 0x20, 0x2a, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e,
+ 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c,
+ 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x61,
+ 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73,
+ 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a,
+ 0x20, 0x2a, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64,
+ 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65,
+ 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e,
+ 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x2a, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65,
+ 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, 0x6e, 0x6c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20, 0x70,
+ 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e,
+ 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x20,
+ 0x2a, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69,
+ 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x74,
+ 0x68, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x67,
+ 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65,
+ 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e,
+ 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a,
+ 0x20, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20,
+ 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x2e, 0x20, 0x20, 0x28, 0x54, 0x68, 0x65,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x64, 0x6f, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79,
+ 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63,
+ 0x74, 0x73, 0x3b, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
+ 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x6f,
+ 0x76, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x6d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x6e,
+ 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x0a, 0x20,
+ 0x2a, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x65, 0x78, 0x65,
+ 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e,
+ 0x29, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c,
+ 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74,
+ 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x49, 0x54, 0x48,
+ 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20,
+ 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59,
+ 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75,
+ 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69,
+ 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61,
+ 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20,
+ 0x2a, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41,
+ 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54,
+ 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54,
+ 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52,
+ 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49,
+ 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55,
+ 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x20,
+ 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x47, 0x4e, 0x55, 0x0a, 0x20, 0x2a, 0x20, 0x47,
+ 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64,
+ 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a,
+ 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x59, 0x6f,
+ 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
+ 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65,
+ 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61,
+ 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
+ 0x61, 0x6d, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65,
+ 0x20, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47,
+ 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f,
+ 0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65,
+ 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x2a, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x35, 0x31, 0x20,
+ 0x46, 0x72, 0x61, 0x6e, 0x6b, 0x6c, 0x69, 0x6e,
+ 0x20, 0x53, 0x74, 0x72, 0x65, 0x65, 0x74, 0x2c,
+ 0x20, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, 0x46,
+ 0x6c, 0x6f, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x42, 0x6f, 0x73, 0x74, 0x6f, 0x6e, 0x2c,
+ 0x20, 0x4d, 0x41, 0x20, 0x30, 0x32, 0x31, 0x31,
+ 0x30, 0x2d, 0x31, 0x33, 0x30, 0x31, 0x2c, 0x20,
+ 0x55, 0x53, 0x41, 0x2e, 0x0a, 0x20, 0x2a, 0x2f,
+ 0x0a,
+};
+
+/*
+ * SHA1 Hashes
+ */
+static char *commit_id = "3d7f8a6af076c8c3f20071a8935cdbe8228594d1";
+static char *tree_id = "dff2da90b254e1beb889d1f1f1288be1803782df";
+static char *tag_id = "09d373e1dfdc16b129ceec6dd649739911541e05";
+static char *zero_id = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391";
+static char *one_id = "8b137891791fe96927ad78e64b0aad7bded08bdc";
+static char *two_id = "78981922613b2afb6025042ff6bd878ac1994e85";
+static char *some_id = "fd8430bc864cfcd5f10e5590f8a447e01b942bfe";
+
+/*
+ * In-memory objects
+ */
+static git_rawobj tree_obj = {
+ tree_data,
+ sizeof(tree_data),
+ GIT_OBJECT_TREE
+};
+
+static git_rawobj tag_obj = {
+ tag_data,
+ sizeof(tag_data),
+ GIT_OBJECT_TAG
+};
+
+static git_rawobj zero_obj = {
+ zero_data,
+ 0,
+ GIT_OBJECT_BLOB
+};
+
+static git_rawobj one_obj = {
+ one_data,
+ sizeof(one_data),
+ GIT_OBJECT_BLOB
+};
+
+static git_rawobj two_obj = {
+ two_data,
+ sizeof(two_data),
+ GIT_OBJECT_BLOB
+};
+
+static git_rawobj commit_obj = {
+ commit_data,
+ sizeof(commit_data),
+ GIT_OBJECT_COMMIT
+};
+
+static git_rawobj some_obj = {
+ some_data,
+ sizeof(some_data),
+ GIT_OBJECT_BLOB
+};
+
+static git_rawobj junk_obj = {
+ NULL,
+ 0,
+ GIT_OBJECT_INVALID
+};
diff --git a/tests/libgit2/object/raw/fromstr.c b/tests/libgit2/object/raw/fromstr.c
new file mode 100644
index 000000000..8c11c105f
--- /dev/null
+++ b/tests/libgit2/object/raw/fromstr.c
@@ -0,0 +1,30 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_fromstr__fail_on_invalid_oid_string(void)
+{
+ git_oid out;
+ cl_git_fail(git_oid_fromstr(&out, ""));
+ cl_git_fail(git_oid_fromstr(&out, "moo"));
+ cl_git_fail(git_oid_fromstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5ez"));
+}
+
+void test_object_raw_fromstr__succeed_on_valid_oid_string(void)
+{
+ git_oid out;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+
+ cl_git_pass(git_oid_fromstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5e0"));
+ cl_git_pass(memcmp(out.id, exp, sizeof(out.id)));
+
+ cl_git_pass(git_oid_fromstr(&out, "16A67770B7D8D72317C4b775213C23A8BD74F5E0"));
+ cl_git_pass(memcmp(out.id, exp, sizeof(out.id)));
+
+}
diff --git a/tests/libgit2/object/raw/hash.c b/tests/libgit2/object/raw/hash.c
new file mode 100644
index 000000000..5a3e81855
--- /dev/null
+++ b/tests/libgit2/object/raw/hash.c
@@ -0,0 +1,169 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+#include "hash.h"
+
+#include "data.h"
+
+static void hash_object_pass(git_oid *oid, git_rawobj *obj)
+{
+ cl_git_pass(git_odb_hash(oid, obj->data, obj->len, obj->type));
+}
+static void hash_object_fail(git_oid *oid, git_rawobj *obj)
+{
+ cl_git_fail(git_odb_hash(oid, obj->data, obj->len, obj->type));
+}
+
+static char *hello_id = "22596363b3de40b06f981fb85d82312e8c0ed511";
+static char *hello_text = "hello world\n";
+
+static char *bye_id = "ce08fe4884650f067bd5703b6a59a8b3b3c99a09";
+static char *bye_text = "bye world\n";
+
+void test_object_raw_hash__hash_by_blocks(void)
+{
+ git_hash_ctx ctx;
+ git_oid id1, id2;
+
+ cl_git_pass(git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA1));
+
+ /* should already be init'd */
+ cl_git_pass(git_hash_update(&ctx, hello_text, strlen(hello_text)));
+ cl_git_pass(git_hash_final(id2.id, &ctx));
+ cl_git_pass(git_oid_fromstr(&id1, hello_id));
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+
+ /* reinit should permit reuse */
+ cl_git_pass(git_hash_init(&ctx));
+ cl_git_pass(git_hash_update(&ctx, bye_text, strlen(bye_text)));
+ cl_git_pass(git_hash_final(id2.id, &ctx));
+ cl_git_pass(git_oid_fromstr(&id1, bye_id));
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+
+ git_hash_ctx_cleanup(&ctx);
+}
+
+void test_object_raw_hash__hash_buffer_in_single_call(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, hello_id));
+ git_hash_buf(id2.id, hello_text, strlen(hello_text), GIT_HASH_ALGORITHM_SHA1);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_vector(void)
+{
+ git_oid id1, id2;
+ git_str_vec vec[2];
+
+ cl_git_pass(git_oid_fromstr(&id1, hello_id));
+
+ vec[0].data = hello_text;
+ vec[0].len = 4;
+ vec[1].data = hello_text+4;
+ vec[1].len = strlen(hello_text)-4;
+
+ git_hash_vec(id2.id, vec, 2, GIT_HASH_ALGORITHM_SHA1);
+
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_junk_data(void)
+{
+ git_oid id, id_zero;
+
+ cl_git_pass(git_oid_fromstr(&id_zero, zero_id));
+
+ /* invalid types: */
+ junk_obj.data = some_data;
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = 0; /* EXT1 */
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = 5; /* EXT2 */
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = GIT_OBJECT_OFS_DELTA;
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = GIT_OBJECT_REF_DELTA;
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = 42;
+ hash_object_fail(&id, &junk_obj);
+
+ /* data can be NULL only if len is zero: */
+ junk_obj.type = GIT_OBJECT_BLOB;
+ junk_obj.data = NULL;
+ hash_object_pass(&id, &junk_obj);
+ cl_assert(git_oid_cmp(&id, &id_zero) == 0);
+
+ junk_obj.len = 1;
+ hash_object_fail(&id, &junk_obj);
+}
+
+void test_object_raw_hash__hash_commit_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, commit_id));
+ hash_object_pass(&id2, &commit_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_tree_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, tree_id));
+ hash_object_pass(&id2, &tree_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_tag_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, tag_id));
+ hash_object_pass(&id2, &tag_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_zero_length_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, zero_id));
+ hash_object_pass(&id2, &zero_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_one_byte_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, one_id));
+ hash_object_pass(&id2, &one_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_two_byte_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, two_id));
+ hash_object_pass(&id2, &two_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_multi_byte_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, some_id));
+ hash_object_pass(&id2, &some_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
diff --git a/tests/libgit2/object/raw/short.c b/tests/libgit2/object/raw/short.c
new file mode 100644
index 000000000..e8d2cf5a5
--- /dev/null
+++ b/tests/libgit2/object/raw/short.c
@@ -0,0 +1,137 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+#include "hash.h"
+
+void test_object_raw_short__oid_shortener_no_duplicates(void)
+{
+ git_oid_shorten *os;
+ int min_len;
+
+ os = git_oid_shorten_new(0);
+ cl_assert(os != NULL);
+
+ git_oid_shorten_add(os, "22596363b3de40b06f981fb85d82312e8c0ed511");
+ git_oid_shorten_add(os, "ce08fe4884650f067bd5703b6a59a8b3b3c99a09");
+ git_oid_shorten_add(os, "16a0123456789abcdef4b775213c23a8bd74f5e0");
+ min_len = git_oid_shorten_add(os, "ce08fe4884650f067bd5703b6a59a8b3b3c99a09");
+
+ cl_assert(min_len == GIT_OID_HEXSZ + 1);
+
+ git_oid_shorten_free(os);
+}
+
+static int insert_sequential_oids(
+ char ***out, git_oid_shorten *os, int n, int fail)
+{
+ int i, min_len = 0;
+ char numbuf[16];
+ git_oid oid;
+ char **oids = git__calloc(n, sizeof(char *));
+ cl_assert(oids != NULL);
+
+ for (i = 0; i < n; ++i) {
+ p_snprintf(numbuf, sizeof(numbuf), "%u", (unsigned int)i);
+ git_hash_buf(oid.id, numbuf, strlen(numbuf), GIT_HASH_ALGORITHM_SHA1);
+
+ oids[i] = git__malloc(GIT_OID_HEXSZ + 1);
+ cl_assert(oids[i]);
+ git_oid_nfmt(oids[i], GIT_OID_HEXSZ + 1, &oid);
+
+ min_len = git_oid_shorten_add(os, oids[i]);
+
+ /* After "fail", we expect git_oid_shorten_add to fail */
+ if (fail >= 0 && i >= fail)
+ cl_assert(min_len < 0);
+ else
+ cl_assert(min_len >= 0);
+ }
+
+ *out = oids;
+
+ return min_len;
+}
+
+static void free_oids(int n, char **oids)
+{
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ git__free(oids[i]);
+ }
+ git__free(oids);
+}
+
+void test_object_raw_short__oid_shortener_stresstest_git_oid_shorten(void)
+{
+#define MAX_OIDS 1000
+
+ git_oid_shorten *os;
+ size_t i, j;
+ int min_len = 0, found_collision;
+ char **oids;
+
+ os = git_oid_shorten_new(0);
+ cl_assert(os != NULL);
+
+ /*
+ * Insert in the shortener 1000 unique SHA1 ids
+ */
+ min_len = insert_sequential_oids(&oids, os, MAX_OIDS, MAX_OIDS);
+ cl_assert(min_len > 0);
+
+ /*
+ * Compare the first `min_char - 1` characters of each
+ * SHA1 OID. If the minimizer worked, we should find at
+ * least one collision
+ */
+ found_collision = 0;
+ for (i = 0; i < MAX_OIDS; ++i) {
+ for (j = i + 1; j < MAX_OIDS; ++j) {
+ if (memcmp(oids[i], oids[j], min_len - 1) == 0)
+ found_collision = 1;
+ }
+ }
+ cl_assert_equal_b(true, found_collision);
+
+ /*
+ * Compare the first `min_char` characters of each
+ * SHA1 OID. If the minimizer worked, every single preffix
+ * should be unique.
+ */
+ found_collision = 0;
+ for (i = 0; i < MAX_OIDS; ++i) {
+ for (j = i + 1; j < MAX_OIDS; ++j) {
+ if (memcmp(oids[i], oids[j], min_len) == 0)
+ found_collision = 1;
+ }
+ }
+ cl_assert_equal_b(false, found_collision);
+
+ /* cleanup */
+ free_oids(MAX_OIDS, oids);
+ git_oid_shorten_free(os);
+
+#undef MAX_OIDS
+}
+
+void test_object_raw_short__oid_shortener_too_much_oids(void)
+{
+ /* The magic number of oids at which an oid_shortener will fail.
+ * This was experimentally established. */
+#define MAX_OIDS 24556
+
+ git_oid_shorten *os;
+ char **oids;
+
+ os = git_oid_shorten_new(0);
+ cl_assert(os != NULL);
+
+ cl_assert(insert_sequential_oids(&oids, os, MAX_OIDS, MAX_OIDS - 1) < 0);
+
+ free_oids(MAX_OIDS, oids);
+ git_oid_shorten_free(os);
+
+#undef MAX_OIDS
+}
diff --git a/tests/libgit2/object/raw/size.c b/tests/libgit2/object/raw/size.c
new file mode 100644
index 000000000..930c6de23
--- /dev/null
+++ b/tests/libgit2/object/raw/size.c
@@ -0,0 +1,13 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_size__validate_oid_size(void)
+{
+ git_oid out;
+ cl_assert(20 == GIT_OID_RAWSZ);
+ cl_assert(40 == GIT_OID_HEXSZ);
+ cl_assert(sizeof(out) == GIT_OID_RAWSZ);
+ cl_assert(sizeof(out.id) == GIT_OID_RAWSZ);
+}
diff --git a/tests/libgit2/object/raw/type2string.c b/tests/libgit2/object/raw/type2string.c
new file mode 100644
index 000000000..ebd81f552
--- /dev/null
+++ b/tests/libgit2/object/raw/type2string.c
@@ -0,0 +1,54 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+#include "hash.h"
+
+void test_object_raw_type2string__convert_type_to_string(void)
+{
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_INVALID), "");
+ cl_assert_equal_s(git_object_type2string(0), ""); /* EXT1 */
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_COMMIT), "commit");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_TREE), "tree");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_BLOB), "blob");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_TAG), "tag");
+ cl_assert_equal_s(git_object_type2string(5), ""); /* EXT2 */
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_OFS_DELTA), "OFS_DELTA");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_REF_DELTA), "REF_DELTA");
+
+ cl_assert_equal_s(git_object_type2string(-2), "");
+ cl_assert_equal_s(git_object_type2string(8), "");
+ cl_assert_equal_s(git_object_type2string(1234), "");
+}
+
+void test_object_raw_type2string__convert_string_to_type(void)
+{
+ cl_assert(git_object_string2type(NULL) == GIT_OBJECT_INVALID);
+ cl_assert(git_object_string2type("") == GIT_OBJECT_INVALID);
+ cl_assert(git_object_string2type("commit") == GIT_OBJECT_COMMIT);
+ cl_assert(git_object_string2type("tree") == GIT_OBJECT_TREE);
+ cl_assert(git_object_string2type("blob") == GIT_OBJECT_BLOB);
+ cl_assert(git_object_string2type("tag") == GIT_OBJECT_TAG);
+ cl_assert(git_object_string2type("OFS_DELTA") == GIT_OBJECT_OFS_DELTA);
+ cl_assert(git_object_string2type("REF_DELTA") == GIT_OBJECT_REF_DELTA);
+
+ cl_assert(git_object_string2type("CoMmIt") == GIT_OBJECT_INVALID);
+ cl_assert(git_object_string2type("hohoho") == GIT_OBJECT_INVALID);
+}
+
+void test_object_raw_type2string__check_type_is_loose(void)
+{
+ cl_assert(git_object_typeisloose(GIT_OBJECT_INVALID) == 0);
+ cl_assert(git_object_typeisloose(0) == 0); /* EXT1 */
+ cl_assert(git_object_typeisloose(GIT_OBJECT_COMMIT) == 1);
+ cl_assert(git_object_typeisloose(GIT_OBJECT_TREE) == 1);
+ cl_assert(git_object_typeisloose(GIT_OBJECT_BLOB) == 1);
+ cl_assert(git_object_typeisloose(GIT_OBJECT_TAG) == 1);
+ cl_assert(git_object_typeisloose(5) == 0); /* EXT2 */
+ cl_assert(git_object_typeisloose(GIT_OBJECT_OFS_DELTA) == 0);
+ cl_assert(git_object_typeisloose(GIT_OBJECT_REF_DELTA) == 0);
+
+ cl_assert(git_object_typeisloose(-2) == 0);
+ cl_assert(git_object_typeisloose(8) == 0);
+ cl_assert(git_object_typeisloose(1234) == 0);
+}
diff --git a/tests/libgit2/object/raw/write.c b/tests/libgit2/object/raw/write.c
new file mode 100644
index 000000000..40e05f357
--- /dev/null
+++ b/tests/libgit2/object/raw/write.c
@@ -0,0 +1,462 @@
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+
+#include "futils.h"
+#include "odb.h"
+
+typedef struct object_data {
+ char *id; /* object id (sha1) */
+ char *dir; /* object store (fan-out) directory name */
+ char *file; /* object store filename */
+} object_data;
+
+static const char *odb_dir = "test-objects";
+
+void test_body(object_data *d, git_rawobj *o);
+
+
+
+/* Helpers */
+static void remove_object_files(object_data *d)
+{
+ cl_git_pass(p_unlink(d->file));
+ cl_git_pass(p_rmdir(d->dir));
+ cl_assert(errno != ENOTEMPTY);
+ cl_git_pass(p_rmdir(odb_dir) < 0);
+}
+
+static void streaming_write(git_oid *oid, git_odb *odb, git_rawobj *raw)
+{
+ git_odb_stream *stream;
+ int error;
+
+ cl_git_pass(git_odb_open_wstream(&stream, odb, raw->len, raw->type));
+ git_odb_stream_write(stream, raw->data, raw->len);
+ error = git_odb_stream_finalize_write(oid, stream);
+ git_odb_stream_free(stream);
+ cl_git_pass(error);
+}
+
+static void check_object_files(object_data *d)
+{
+ cl_assert(git_fs_path_exists(d->dir));
+ cl_assert(git_fs_path_exists(d->file));
+}
+
+static void cmp_objects(git_rawobj *o1, git_rawobj *o2)
+{
+ cl_assert(o1->type == o2->type);
+ cl_assert(o1->len == o2->len);
+ if (o1->len > 0)
+ cl_assert(memcmp(o1->data, o2->data, o1->len) == 0);
+}
+
+static void make_odb_dir(void)
+{
+ cl_git_pass(p_mkdir(odb_dir, GIT_OBJECT_DIR_MODE));
+}
+
+
+/* Standard test form */
+void test_body(object_data *d, git_rawobj *o)
+{
+ git_odb *db;
+ git_oid id1, id2;
+ git_odb_object *obj;
+ git_rawobj tmp;
+
+ make_odb_dir();
+ cl_git_pass(git_odb_open(&db, odb_dir));
+ cl_git_pass(git_oid_fromstr(&id1, d->id));
+
+ streaming_write(&id2, db, o);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+ check_object_files(d);
+
+ cl_git_pass(git_odb_read(&obj, db, &id1));
+
+ tmp.data = obj->buffer;
+ tmp.len = obj->cached.size;
+ tmp.type = obj->cached.type;
+
+ cmp_objects(&tmp, o);
+
+ git_odb_object_free(obj);
+ git_odb_free(db);
+ remove_object_files(d);
+}
+
+
+void test_object_raw_write__loose_object(void)
+{
+ object_data commit = {
+ "3d7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ "test-objects/3d",
+ "test-objects/3d/7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ };
+
+ unsigned char commit_data[] = {
+ 0x74, 0x72, 0x65, 0x65, 0x20, 0x64, 0x66, 0x66,
+ 0x32, 0x64, 0x61, 0x39, 0x30, 0x62, 0x32, 0x35,
+ 0x34, 0x65, 0x31, 0x62, 0x65, 0x62, 0x38, 0x38,
+ 0x39, 0x64, 0x31, 0x66, 0x31, 0x66, 0x31, 0x32,
+ 0x38, 0x38, 0x62, 0x65, 0x31, 0x38, 0x30, 0x33,
+ 0x37, 0x38, 0x32, 0x64, 0x66, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
+ 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38,
+ 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30,
+ 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
+ 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
+ 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
+ 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
+ 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
+ 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x3e, 0x0a,
+ };
+
+ git_rawobj commit_obj = {
+ commit_data,
+ sizeof(commit_data),
+ GIT_OBJECT_COMMIT
+ };
+
+ test_body(&commit, &commit_obj);
+}
+
+void test_object_raw_write__loose_tree(void)
+{
+ static object_data tree = {
+ "dff2da90b254e1beb889d1f1f1288be1803782df",
+ "test-objects/df",
+ "test-objects/df/f2da90b254e1beb889d1f1f1288be1803782df",
+ };
+
+ static unsigned char tree_data[] = {
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6f,
+ 0x6e, 0x65, 0x00, 0x8b, 0x13, 0x78, 0x91, 0x79,
+ 0x1f, 0xe9, 0x69, 0x27, 0xad, 0x78, 0xe6, 0x4b,
+ 0x0a, 0xad, 0x7b, 0xde, 0xd0, 0x8b, 0xdc, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x6f,
+ 0x6d, 0x65, 0x00, 0xfd, 0x84, 0x30, 0xbc, 0x86,
+ 0x4c, 0xfc, 0xd5, 0xf1, 0x0e, 0x55, 0x90, 0xf8,
+ 0xa4, 0x47, 0xe0, 0x1b, 0x94, 0x2b, 0xfe, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x77,
+ 0x6f, 0x00, 0x78, 0x98, 0x19, 0x22, 0x61, 0x3b,
+ 0x2a, 0xfb, 0x60, 0x25, 0x04, 0x2f, 0xf6, 0xbd,
+ 0x87, 0x8a, 0xc1, 0x99, 0x4e, 0x85, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x7a, 0x65, 0x72,
+ 0x6f, 0x00, 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1,
+ 0xd6, 0x43, 0x4b, 0x8b, 0x29, 0xae, 0x77, 0x5a,
+ 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91,
+ };
+
+ static git_rawobj tree_obj = {
+ tree_data,
+ sizeof(tree_data),
+ GIT_OBJECT_TREE
+ };
+
+ test_body(&tree, &tree_obj);
+}
+
+void test_object_raw_write__loose_tag(void)
+{
+ static object_data tag = {
+ "09d373e1dfdc16b129ceec6dd649739911541e05",
+ "test-objects/09",
+ "test-objects/09/d373e1dfdc16b129ceec6dd649739911541e05",
+ };
+
+ static unsigned char tag_data[] = {
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x33,
+ 0x64, 0x37, 0x66, 0x38, 0x61, 0x36, 0x61, 0x66,
+ 0x30, 0x37, 0x36, 0x63, 0x38, 0x63, 0x33, 0x66,
+ 0x32, 0x30, 0x30, 0x37, 0x31, 0x61, 0x38, 0x39,
+ 0x33, 0x35, 0x63, 0x64, 0x62, 0x65, 0x38, 0x32,
+ 0x32, 0x38, 0x35, 0x39, 0x34, 0x64, 0x31, 0x0a,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20,
+ 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74,
+ 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
+ 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
+ 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x0a,
+ };
+
+ static git_rawobj tag_obj = {
+ tag_data,
+ sizeof(tag_data),
+ GIT_OBJECT_TAG
+ };
+
+
+ test_body(&tag, &tag_obj);
+}
+
+void test_object_raw_write__zero_length(void)
+{
+ static object_data zero = {
+ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ "test-objects/e6",
+ "test-objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ };
+
+ static unsigned char zero_data[] = {
+ 0x00 /* dummy data */
+ };
+
+ static git_rawobj zero_obj = {
+ zero_data,
+ 0,
+ GIT_OBJECT_BLOB
+ };
+
+ test_body(&zero, &zero_obj);
+}
+
+void test_object_raw_write__one_byte(void)
+{
+ static object_data one = {
+ "8b137891791fe96927ad78e64b0aad7bded08bdc",
+ "test-objects/8b",
+ "test-objects/8b/137891791fe96927ad78e64b0aad7bded08bdc",
+ };
+
+ static unsigned char one_data[] = {
+ 0x0a,
+ };
+
+ static git_rawobj one_obj = {
+ one_data,
+ sizeof(one_data),
+ GIT_OBJECT_BLOB
+ };
+
+ test_body(&one, &one_obj);
+}
+
+void test_object_raw_write__two_byte(void)
+{
+ static object_data two = {
+ "78981922613b2afb6025042ff6bd878ac1994e85",
+ "test-objects/78",
+ "test-objects/78/981922613b2afb6025042ff6bd878ac1994e85",
+ };
+
+ static unsigned char two_data[] = {
+ 0x61, 0x0a,
+ };
+
+ static git_rawobj two_obj = {
+ two_data,
+ sizeof(two_data),
+ GIT_OBJECT_BLOB
+ };
+
+ test_body(&two, &two_obj);
+}
+
+void test_object_raw_write__several_bytes(void)
+{
+ static object_data some = {
+ "fd8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ "test-objects/fd",
+ "test-objects/fd/8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ };
+
+ static unsigned char some_data[] = {
+ 0x2f, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20,
+ 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x3b, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61,
+ 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69,
+ 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72,
+ 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a,
+ 0x20, 0x2a, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e,
+ 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c,
+ 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x61,
+ 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73,
+ 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a,
+ 0x20, 0x2a, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64,
+ 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65,
+ 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e,
+ 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x2a, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65,
+ 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, 0x6e, 0x6c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20, 0x70,
+ 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e,
+ 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x20,
+ 0x2a, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69,
+ 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x74,
+ 0x68, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x67,
+ 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65,
+ 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e,
+ 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a,
+ 0x20, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20,
+ 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x2e, 0x20, 0x20, 0x28, 0x54, 0x68, 0x65,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x64, 0x6f, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79,
+ 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63,
+ 0x74, 0x73, 0x3b, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
+ 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x6f,
+ 0x76, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x6d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x6e,
+ 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x0a, 0x20,
+ 0x2a, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x65, 0x78, 0x65,
+ 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e,
+ 0x29, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c,
+ 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74,
+ 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x49, 0x54, 0x48,
+ 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20,
+ 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59,
+ 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75,
+ 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69,
+ 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61,
+ 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20,
+ 0x2a, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41,
+ 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54,
+ 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54,
+ 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52,
+ 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49,
+ 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55,
+ 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x20,
+ 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x47, 0x4e, 0x55, 0x0a, 0x20, 0x2a, 0x20, 0x47,
+ 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64,
+ 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a,
+ 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x59, 0x6f,
+ 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
+ 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65,
+ 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61,
+ 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
+ 0x61, 0x6d, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65,
+ 0x20, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47,
+ 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f,
+ 0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65,
+ 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x2a, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x35, 0x31, 0x20,
+ 0x46, 0x72, 0x61, 0x6e, 0x6b, 0x6c, 0x69, 0x6e,
+ 0x20, 0x53, 0x74, 0x72, 0x65, 0x65, 0x74, 0x2c,
+ 0x20, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, 0x46,
+ 0x6c, 0x6f, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x42, 0x6f, 0x73, 0x74, 0x6f, 0x6e, 0x2c,
+ 0x20, 0x4d, 0x41, 0x20, 0x30, 0x32, 0x31, 0x31,
+ 0x30, 0x2d, 0x31, 0x33, 0x30, 0x31, 0x2c, 0x20,
+ 0x55, 0x53, 0x41, 0x2e, 0x0a, 0x20, 0x2a, 0x2f,
+ 0x0a,
+ };
+
+ static git_rawobj some_obj = {
+ some_data,
+ sizeof(some_data),
+ GIT_OBJECT_BLOB
+ };
+
+ test_body(&some, &some_obj);
+}
diff --git a/tests/libgit2/object/shortid.c b/tests/libgit2/object/shortid.c
new file mode 100644
index 000000000..9b673efeb
--- /dev/null
+++ b/tests/libgit2/object/shortid.c
@@ -0,0 +1,51 @@
+#include "clar_libgit2.h"
+
+git_repository *_repo;
+
+void test_object_shortid__initialize(void)
+{
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("duplicate.git")));
+}
+
+void test_object_shortid__cleanup(void)
+{
+ git_repository_free(_repo);
+ _repo = NULL;
+}
+
+void test_object_shortid__select(void)
+{
+ git_oid full;
+ git_object *obj;
+ git_buf shorty = {0};
+
+ git_oid_fromstr(&full, "ce013625030ba8dba906f756967f9e9ca394464a");
+ cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJECT_ANY));
+ cl_git_pass(git_object_short_id(&shorty, obj));
+ cl_assert_equal_i(7, shorty.size);
+ cl_assert_equal_s("ce01362", shorty.ptr);
+ git_object_free(obj);
+
+ git_oid_fromstr(&full, "038d718da6a1ebbc6a7780a96ed75a70cc2ad6e2");
+ cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJECT_ANY));
+ cl_git_pass(git_object_short_id(&shorty, obj));
+ cl_assert_equal_i(7, shorty.size);
+ cl_assert_equal_s("038d718", shorty.ptr);
+ git_object_free(obj);
+
+ git_oid_fromstr(&full, "dea509d097ce692e167dfc6a48a7a280cc5e877e");
+ cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJECT_ANY));
+ cl_git_pass(git_object_short_id(&shorty, obj));
+ cl_assert_equal_i(9, shorty.size);
+ cl_assert_equal_s("dea509d09", shorty.ptr);
+ git_object_free(obj);
+
+ git_oid_fromstr(&full, "dea509d0b3cb8ee0650f6ca210bc83f4678851ba");
+ cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJECT_ANY));
+ cl_git_pass(git_object_short_id(&shorty, obj));
+ cl_assert_equal_i(9, shorty.size);
+ cl_assert_equal_s("dea509d0b", shorty.ptr);
+ git_object_free(obj);
+
+ git_buf_dispose(&shorty);
+}
diff --git a/tests/libgit2/object/tag/list.c b/tests/libgit2/object/tag/list.c
new file mode 100644
index 000000000..d15f09205
--- /dev/null
+++ b/tests/libgit2/object/tag/list.c
@@ -0,0 +1,117 @@
+#include "clar_libgit2.h"
+
+#include "tag.h"
+
+static git_repository *g_repo;
+
+#define MAX_USED_TAGS 6
+
+struct pattern_match_t
+{
+ const char* pattern;
+ const size_t expected_matches;
+ const char* expected_results[MAX_USED_TAGS];
+};
+
+/* Helpers */
+static void ensure_tag_pattern_match(git_repository *repo,
+ const struct pattern_match_t* data)
+{
+ int already_found[MAX_USED_TAGS] = { 0 };
+ git_strarray tag_list;
+ int error = 0;
+ size_t successfully_found = 0;
+ size_t i, j;
+
+ cl_assert(data->expected_matches <= MAX_USED_TAGS);
+
+ if ((error = git_tag_list_match(&tag_list, data->pattern, repo)) < 0)
+ goto exit;
+
+ if (tag_list.count != data->expected_matches)
+ {
+ error = GIT_ERROR;
+ goto exit;
+ }
+
+ /* we have to be prepared that tags come in any order. */
+ for (i = 0; i < tag_list.count; i++)
+ {
+ for (j = 0; j < data->expected_matches; j++)
+ {
+ if (!already_found[j] && !strcmp(data->expected_results[j], tag_list.strings[i]))
+ {
+ already_found[j] = 1;
+ successfully_found++;
+ break;
+ }
+ }
+ }
+ cl_assert_equal_i((int)successfully_found, (int)data->expected_matches);
+
+exit:
+ git_strarray_dispose(&tag_list);
+ cl_git_pass(error);
+}
+
+/* Fixture setup and teardown */
+void test_object_tag_list__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tag_list__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tag_list__list_all(void)
+{
+ /* list all tag names from the repository */
+ git_strarray tag_list;
+
+ cl_git_pass(git_tag_list(&tag_list, g_repo));
+
+ cl_assert_equal_i((int)tag_list.count, 6);
+
+ git_strarray_dispose(&tag_list);
+}
+
+static const struct pattern_match_t matches[] = {
+ /* All tags, including a packed one and two namespaced ones. */
+ { "", 6, { "e90810b", "point_to_blob", "test", "packed-tag", "foo/bar", "foo/foo/bar" } },
+
+ /* beginning with */
+ { "t*", 1, { "test" } },
+
+ /* ending with */
+ { "*b", 2, { "e90810b", "point_to_blob" } },
+
+ /* exact match */
+ { "e", 0 },
+ { "e90810b", 1, { "e90810b" } },
+
+ /* either or */
+ { "e90810[ab]", 1, { "e90810b" } },
+
+ /* glob in the middle */
+ { "foo/*/bar", 1, { "foo/foo/bar" } },
+
+ /*
+ * The matching of '*' is based on plain string matching analog to the regular expression ".*"
+ * => a '/' in the tag name has no special meaning.
+ * Compare to `git tag -l "*bar"`
+ */
+ { "*bar", 2, { "foo/bar", "foo/foo/bar" } },
+
+ /* End of list */
+ { NULL }
+};
+
+void test_object_tag_list__list_by_pattern(void)
+{
+ /* list all tag names from the repository matching a specified pattern */
+ size_t i = 0;
+ while (matches[i].pattern)
+ ensure_tag_pattern_match(g_repo, &matches[i++]);
+}
diff --git a/tests/libgit2/object/tag/parse.c b/tests/libgit2/object/tag/parse.c
new file mode 100644
index 000000000..2c0635ae4
--- /dev/null
+++ b/tests/libgit2/object/tag/parse.c
@@ -0,0 +1,218 @@
+#include "clar_libgit2.h"
+#include "object.h"
+#include "signature.h"
+#include "tag.h"
+
+static void assert_tag_parses(const char *data, size_t datalen,
+ const char *expected_oid,
+ const char *expected_name,
+ const char *expected_tagger,
+ const char *expected_message)
+{
+ git_tag *tag;
+
+ if (!datalen)
+ datalen = strlen(data);
+
+ cl_git_pass(git_object__from_raw((git_object **) &tag, data, datalen, GIT_OBJECT_TAG));
+ cl_assert_equal_i(tag->type, GIT_OBJECT_TAG);
+
+ if (expected_oid) {
+ git_oid oid;
+ cl_git_pass(git_oid_fromstr(&oid, expected_oid));
+ cl_assert_equal_oid(&oid, &tag->target);
+ }
+
+ if (expected_name)
+ cl_assert_equal_s(expected_name, tag->tag_name);
+ else
+ cl_assert_equal_s(tag->message, NULL);
+
+ if (expected_tagger) {
+ git_signature *tagger;
+ cl_git_pass(git_signature_from_buffer(&tagger, expected_tagger));
+ cl_assert_equal_s(tagger->name, tag->tagger->name);
+ cl_assert_equal_s(tagger->email, tag->tagger->email);
+ cl_assert_equal_i(tagger->when.time, tag->tagger->when.time);
+ cl_assert_equal_i(tagger->when.offset, tag->tagger->when.offset);
+ cl_assert_equal_i(tagger->when.sign, tag->tagger->when.sign);
+ git_signature_free(tagger);
+ } else {
+ cl_assert_equal_s(tag->tagger, NULL);
+ }
+
+ if (expected_message)
+ cl_assert_equal_s(expected_message, tag->message);
+ else
+ cl_assert_equal_s(tag->message, NULL);
+
+ git_object__free(&tag->object);
+}
+
+static void assert_tag_fails(const char *data, size_t datalen)
+{
+ git_object *object;
+ if (!datalen)
+ datalen = strlen(data);
+ cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_TAG));
+}
+
+void test_object_tag_parse__valid_tag_parses(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_parses(tag, 0,
+ "a8d447f68076d1520f69649bb52629941be7031f",
+ "tagname",
+ "Taggy Mr. Taggart <taggy@taggart.com>",
+ "Message");
+}
+
+void test_object_tag_parse__missing_tagger_parses(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag tagname\n"
+ "\n"
+ "Message";
+ assert_tag_parses(tag, 0,
+ "a8d447f68076d1520f69649bb52629941be7031f",
+ "tagname",
+ NULL,
+ "Message");
+}
+
+void test_object_tag_parse__missing_message_parses(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n";
+ assert_tag_parses(tag, 0,
+ "a8d447f68076d1520f69649bb52629941be7031f",
+ "tagname",
+ "Taggy Mr. Taggart <taggy@taggart.com>",
+ NULL);
+}
+
+void test_object_tag_parse__unknown_field_parses(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "foo bar\n"
+ "frubble frabble\n"
+ "\n"
+ "Message";
+ assert_tag_parses(tag, 0,
+ "a8d447f68076d1520f69649bb52629941be7031f",
+ "tagname",
+ "Taggy Mr. Taggart <taggy@taggart.com>",
+ "Message");
+}
+
+void test_object_tag_parse__missing_object_fails(void)
+{
+ const char *tag =
+ "type tag\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__malformatted_object_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d15xxxxxxxxxxxxxxxx41be7031f\n"
+ "type tag\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__missing_type_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__invalid_type_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type garbage\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__missing_tagname_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__misformatted_tagger_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag Tag\n"
+ "tagger taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__missing_message_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag Tag\n"
+ "tagger taggy@taggart.com>\n";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__no_oob_read_when_searching_message(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag \n"
+ "tagger <>\n"
+ " \n\n"
+ "Message";
+ /*
+ * The OOB read previously resulted in an OOM error. We
+ * thus want to make sure that the resulting error is the
+ * expected one.
+ */
+ assert_tag_fails(tag, strlen(tag) - strlen("\n\nMessage"));
+ cl_assert(strstr(git_error_last()->message, "tag contains no message"));
+}
diff --git a/tests/libgit2/object/tag/peel.c b/tests/libgit2/object/tag/peel.c
new file mode 100644
index 000000000..7456a8e17
--- /dev/null
+++ b/tests/libgit2/object/tag/peel.c
@@ -0,0 +1,61 @@
+#include "clar_libgit2.h"
+#include "tag.h"
+
+static git_repository *repo;
+static git_tag *tag;
+static git_object *target;
+
+void test_object_tag_peel__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+}
+
+void test_object_tag_peel__cleanup(void)
+{
+ git_tag_free(tag);
+ tag = NULL;
+
+ git_object_free(target);
+ target = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static void retrieve_tag_from_oid(git_tag **tag_out, git_repository *repo, const char *sha)
+{
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, sha));
+ cl_git_pass(git_tag_lookup(tag_out, repo, &oid));
+}
+
+void test_object_tag_peel__can_peel_to_a_commit(void)
+{
+ retrieve_tag_from_oid(&tag, repo, "7b4384978d2493e851f9cca7858815fac9b10980");
+
+ cl_git_pass(git_tag_peel(&target, tag));
+ cl_assert(git_object_type(target) == GIT_OBJECT_COMMIT);
+ cl_git_pass(git_oid_streq(git_object_id(target), "e90810b8df3e80c413d903f631643c716887138d"));
+}
+
+void test_object_tag_peel__can_peel_several_nested_tags_to_a_commit(void)
+{
+ retrieve_tag_from_oid(&tag, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
+
+ cl_git_pass(git_tag_peel(&target, tag));
+ cl_assert(git_object_type(target) == GIT_OBJECT_COMMIT);
+ cl_git_pass(git_oid_streq(git_object_id(target), "e90810b8df3e80c413d903f631643c716887138d"));
+}
+
+void test_object_tag_peel__can_peel_to_a_non_commit(void)
+{
+ retrieve_tag_from_oid(&tag, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91");
+
+ cl_git_pass(git_tag_peel(&target, tag));
+ cl_assert(git_object_type(target) == GIT_OBJECT_BLOB);
+ cl_git_pass(git_oid_streq(git_object_id(target), "1385f264afb75a56a5bec74243be9b367ba4ca08"));
+}
diff --git a/tests/libgit2/object/tag/read.c b/tests/libgit2/object/tag/read.c
new file mode 100644
index 000000000..90ba58029
--- /dev/null
+++ b/tests/libgit2/object/tag/read.c
@@ -0,0 +1,179 @@
+#include "clar_libgit2.h"
+
+#include "tag.h"
+
+static const char *tag1_id = "b25fa35b38051e4ae45d4222e795f9df2e43f1d1";
+static const char *tag2_id = "7b4384978d2493e851f9cca7858815fac9b10980";
+static const char *tagged_commit = "e90810b8df3e80c413d903f631643c716887138d";
+static const char *bad_tag_id = "eda9f45a2a98d4c17a09d681d88569fa4ea91755";
+static const char *badly_tagged_commit = "e90810b8df3e80c413d903f631643c716887138d";
+static const char *short_tag_id = "5da7760512a953e3c7c4e47e4392c7a4338fb729";
+static const char *short_tagged_commit = "4a5ed60bafcf4638b7c8356bd4ce1916bfede93c";
+static const char *taggerless = "4a23e2e65ad4e31c4c9db7dc746650bfad082679";
+
+static git_repository *g_repo;
+
+/* Fixture setup and teardown */
+void test_object_tag_read__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tag_read__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_object_tag_read__parse(void)
+{
+ /* read and parse a tag from the repository */
+ git_tag *tag1, *tag2;
+ git_commit *commit;
+ git_oid id1, id2, id_commit;
+
+ git_oid_fromstr(&id1, tag1_id);
+ git_oid_fromstr(&id2, tag2_id);
+ git_oid_fromstr(&id_commit, tagged_commit);
+
+ cl_git_pass(git_tag_lookup(&tag1, g_repo, &id1));
+
+ cl_assert_equal_s(git_tag_name(tag1), "test");
+ cl_assert(git_tag_target_type(tag1) == GIT_OBJECT_TAG);
+
+ cl_git_pass(git_tag_target((git_object **)&tag2, tag1));
+ cl_assert(tag2 != NULL);
+
+ cl_assert(git_oid_cmp(&id2, git_tag_id(tag2)) == 0);
+
+ cl_git_pass(git_tag_target((git_object **)&commit, tag2));
+ cl_assert(commit != NULL);
+
+ cl_assert(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
+
+ git_tag_free(tag1);
+ git_tag_free(tag2);
+ git_commit_free(commit);
+}
+
+void test_object_tag_read__parse_without_tagger(void)
+{
+ /* read and parse a tag without a tagger field */
+ git_repository *bad_tag_repo;
+ git_tag *bad_tag;
+ git_commit *commit;
+ git_oid id, id_commit;
+
+ /* TODO: This is a little messy */
+ cl_git_pass(git_repository_open(&bad_tag_repo, cl_fixture("bad_tag.git")));
+
+ git_oid_fromstr(&id, bad_tag_id);
+ git_oid_fromstr(&id_commit, badly_tagged_commit);
+
+ cl_git_pass(git_tag_lookup(&bad_tag, bad_tag_repo, &id));
+ cl_assert(bad_tag != NULL);
+
+ cl_assert_equal_s(git_tag_name(bad_tag), "e90810b");
+ cl_assert(git_oid_cmp(&id, git_tag_id(bad_tag)) == 0);
+ cl_assert(bad_tag->tagger == NULL);
+
+ cl_git_pass(git_tag_target((git_object **)&commit, bad_tag));
+ cl_assert(commit != NULL);
+
+ cl_assert(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
+
+
+ git_tag_free(bad_tag);
+ git_commit_free(commit);
+ git_repository_free(bad_tag_repo);
+}
+
+void test_object_tag_read__parse_without_message(void)
+{
+ /* read and parse a tag without a message field */
+ git_repository *short_tag_repo;
+ git_tag *short_tag;
+ git_commit *commit;
+ git_oid id, id_commit;
+
+ /* TODO: This is a little messy */
+ cl_git_pass(git_repository_open(&short_tag_repo, cl_fixture("short_tag.git")));
+
+ git_oid_fromstr(&id, short_tag_id);
+ git_oid_fromstr(&id_commit, short_tagged_commit);
+
+ cl_git_pass(git_tag_lookup(&short_tag, short_tag_repo, &id));
+ cl_assert(short_tag != NULL);
+
+ cl_assert_equal_s(git_tag_name(short_tag), "no_description");
+ cl_assert(git_oid_cmp(&id, git_tag_id(short_tag)) == 0);
+ cl_assert(short_tag->message == NULL);
+
+ cl_git_pass(git_tag_target((git_object **)&commit, short_tag));
+ cl_assert(commit != NULL);
+
+ cl_assert(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
+
+ git_tag_free(short_tag);
+ git_commit_free(commit);
+ git_repository_free(short_tag_repo);
+}
+
+void test_object_tag_read__without_tagger_nor_message(void)
+{
+ git_tag *tag;
+ git_oid id;
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_oid_fromstr(&id, taggerless));
+
+ cl_git_pass(git_tag_lookup(&tag, repo, &id));
+
+ cl_assert_equal_s(git_tag_name(tag), "taggerless");
+ cl_assert(git_tag_target_type(tag) == GIT_OBJECT_COMMIT);
+
+ cl_assert(tag->message == NULL);
+ cl_assert(tag->tagger == NULL);
+
+ git_tag_free(tag);
+ git_repository_free(repo);
+}
+
+static const char *silly_tag = "object c054ccaefbf2da31c3b19178f9e3ef20a3867924\n\
+type commit\n\
+tag v1_0_1\n\
+tagger Jamis Buck <jamis@37signals.com> 1107717917\n\
+diff --git a/lib/sqlite3/version.rb b/lib/sqlite3/version.rb\n\
+index 0b3bf69..4ee8fc2 100644\n\
+--- a/lib/sqlite3/version.rb\n\
++++ b/lib/sqlite3/version.rb\n\
+@@ -36,7 +36,7 @@ module SQLite3\n\
+ \n\
+ MAJOR = 1\n\
+ MINOR = 0\n\
+- TINY = 0\n\
++ TINY = 1\n\
+ \n\
+ STRING = [ MAJOR, MINOR, TINY ].join( \".\" )\n\
+ \n\
+ -0600\n\
+\n\
+v1_0_1 release\n";
+
+void test_object_tag_read__extra_header_fields(void)
+{
+ git_tag *tag;
+ git_odb *odb;
+ git_oid id;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, g_repo));
+
+ cl_git_pass(git_odb_write(&id, odb, silly_tag, strlen(silly_tag), GIT_OBJECT_TAG));
+ cl_git_pass(git_tag_lookup(&tag, g_repo, &id));
+
+ cl_assert_equal_s("v1_0_1 release\n", git_tag_message(tag));
+
+ git_tag_free(tag);
+}
diff --git a/tests/libgit2/object/tag/write.c b/tests/libgit2/object/tag/write.c
new file mode 100644
index 000000000..3c1a98956
--- /dev/null
+++ b/tests/libgit2/object/tag/write.c
@@ -0,0 +1,260 @@
+#include "clar_libgit2.h"
+
+static const char* tagger_name = "Vicent Marti";
+static const char* tagger_email = "vicent@github.com";
+static const char* tagger_message = "This is my tag.\n\nThere are many tags, but this one is mine\n";
+
+static const char *tag2_id = "7b4384978d2493e851f9cca7858815fac9b10980";
+static const char *tagged_commit = "e90810b8df3e80c413d903f631643c716887138d";
+
+static git_repository *g_repo;
+
+/* Fixture setup and teardown */
+void test_object_tag_write__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tag_write__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tag_write__basic(void)
+{
+ /* write a tag to the repository and read it again */
+ git_tag *tag;
+ git_oid target_id, tag_id;
+ git_signature *tagger;
+ const git_signature *tagger1;
+ git_reference *ref_tag;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ /* create signature */
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_git_pass(
+ git_tag_create(&tag_id, g_repo,
+ "the-tag", target, tagger, tagger_message, 0)
+ );
+
+ git_object_free(target);
+ git_signature_free(tagger);
+
+ cl_git_pass(git_tag_lookup(&tag, g_repo, &tag_id));
+ cl_assert(git_oid_cmp(git_tag_target_id(tag), &target_id) == 0);
+
+ /* Check attributes were set correctly */
+ tagger1 = git_tag_tagger(tag);
+ cl_assert(tagger1 != NULL);
+ cl_assert_equal_s(tagger1->name, tagger_name);
+ cl_assert_equal_s(tagger1->email, tagger_email);
+ cl_assert(tagger1->when.time == 123456789);
+ cl_assert(tagger1->when.offset == 60);
+
+ cl_assert_equal_s(git_tag_message(tag), tagger_message);
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/the-tag"));
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &tag_id) == 0);
+ cl_git_pass(git_reference_delete(ref_tag));
+ git_reference_free(ref_tag);
+
+ git_tag_free(tag);
+}
+
+void test_object_tag_write__overwrite(void)
+{
+ /* Attempt to write a tag bearing the same name than an already existing tag */
+ git_oid target_id, tag_id;
+ git_signature *tagger;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ /* create signature */
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_tag_create(
+ &tag_id, /* out id */
+ g_repo,
+ "e90810b",
+ target,
+ tagger,
+ tagger_message,
+ 0));
+
+ git_object_free(target);
+ git_signature_free(tagger);
+}
+
+void test_object_tag_write__replace(void)
+{
+ /* Replace an already existing tag */
+ git_oid target_id, tag_id, old_tag_id;
+ git_signature *tagger;
+ git_reference *ref_tag;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/e90810b"));
+ git_oid_cpy(&old_tag_id, git_reference_target(ref_tag));
+ git_reference_free(ref_tag);
+
+ /* create signature */
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_git_pass(git_tag_create(
+ &tag_id, /* out id */
+ g_repo,
+ "e90810b",
+ target,
+ tagger,
+ tagger_message,
+ 1));
+
+ git_object_free(target);
+ git_signature_free(tagger);
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/e90810b"));
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &tag_id) == 0);
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &old_tag_id) != 0);
+
+ git_reference_free(ref_tag);
+}
+
+void test_object_tag_write__lightweight(void)
+{
+ /* write a lightweight tag to the repository and read it again */
+ git_oid target_id, object_id;
+ git_reference *ref_tag;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_tag_create_lightweight(
+ &object_id,
+ g_repo,
+ "light-tag",
+ target,
+ 0));
+
+ git_object_free(target);
+
+ cl_assert(git_oid_cmp(&object_id, &target_id) == 0);
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/light-tag"));
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &target_id) == 0);
+
+ cl_git_pass(git_tag_delete(g_repo, "light-tag"));
+
+ git_reference_free(ref_tag);
+}
+
+void test_object_tag_write__lightweight_over_existing(void)
+{
+ /* Attempt to write a lightweight tag bearing the same name than an already existing tag */
+ git_oid target_id, object_id, existing_object_id;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_tag_create_lightweight(
+ &object_id,
+ g_repo,
+ "e90810b",
+ target,
+ 0));
+
+ git_oid_fromstr(&existing_object_id, tag2_id);
+ cl_assert(git_oid_cmp(&object_id, &existing_object_id) == 0);
+
+ git_object_free(target);
+}
+
+void test_object_tag_write__delete(void)
+{
+ /* Delete an already existing tag */
+ git_reference *ref_tag;
+
+ cl_git_pass(git_tag_delete(g_repo, "e90810b"));
+
+ cl_git_fail(git_reference_lookup(&ref_tag, g_repo, "refs/tags/e90810b"));
+
+ git_reference_free(ref_tag);
+}
+
+void test_object_tag_write__creating_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_oid target_id, tag_id;
+ git_signature *tagger;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_tag_create(&tag_id, g_repo,
+ "Inv@{id", target, tagger, tagger_message, 0)
+ );
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_tag_create_lightweight(&tag_id, g_repo,
+ "Inv@{id", target, 0)
+ );
+
+ git_object_free(target);
+ git_signature_free(tagger);
+}
+
+void test_object_tag_write__deleting_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_tag_delete(g_repo, "Inv@{id"));
+}
+
+static void create_annotation(git_oid *tag_id, const char *name)
+{
+ git_object *target;
+ git_oid target_id;
+ git_signature *tagger;
+
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_tag_annotation_create(tag_id, g_repo, name, target, tagger, "boom!"));
+ git_object_free(target);
+ git_signature_free(tagger);
+}
+
+void test_object_tag_write__creating_an_annotation_stores_the_new_object_in_the_odb(void)
+{
+ git_oid tag_id;
+ git_tag *tag;
+
+ create_annotation(&tag_id, "new_tag");
+
+ cl_git_pass(git_tag_lookup(&tag, g_repo, &tag_id));
+ cl_assert_equal_s("new_tag", git_tag_name(tag));
+
+ git_tag_free(tag);
+}
+
+void test_object_tag_write__creating_an_annotation_does_not_create_a_reference(void)
+{
+ git_oid tag_id;
+ git_reference *tag_ref;
+
+ create_annotation(&tag_id, "new_tag");
+ cl_git_fail_with(git_reference_lookup(&tag_ref, g_repo, "refs/tags/new_tag"), GIT_ENOTFOUND);
+}
diff --git a/tests/libgit2/object/tree/attributes.c b/tests/libgit2/object/tree/attributes.c
new file mode 100644
index 000000000..8654dfa31
--- /dev/null
+++ b/tests/libgit2/object/tree/attributes.c
@@ -0,0 +1,118 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+
+static git_repository *repo;
+
+static const char *blob_oid = "3d0970ec547fc41ef8a5882dde99c6adce65b021";
+static const char *tree_oid = "1b05fdaa881ee45b48cbaa5e9b037d667a47745e";
+
+void test_object_tree_attributes__initialize(void)
+{
+ repo = cl_git_sandbox_init("deprecated-mode.git");
+}
+
+void test_object_tree_attributes__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(void)
+{
+ git_treebuilder *builder;
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, blob_oid));
+
+ cl_git_pass(git_treebuilder_new(&builder, repo, NULL));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0777777));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0100666));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0000001));
+
+ git_treebuilder_free(builder);
+}
+
+void test_object_tree_attributes__group_writable_tree_entries_created_with_an_antique_git_version_can_still_be_accessed(void)
+{
+ git_oid tid;
+ git_tree *tree;
+ const git_tree_entry *entry;
+
+
+ cl_git_pass(git_oid_fromstr(&tid, tree_oid));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tid));
+
+ entry = git_tree_entry_byname(tree, "old_mode.txt");
+ cl_assert_equal_i(
+ GIT_FILEMODE_BLOB,
+ git_tree_entry_filemode(entry));
+
+ git_tree_free(tree);
+}
+
+void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
+{
+ git_treebuilder *builder;
+ git_oid bid;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_oid_fromstr(&bid, blob_oid));
+ cl_git_pass(git_treebuilder_new(&builder, repo, NULL));
+
+ cl_git_fail(git_treebuilder_insert(
+ &entry,
+ builder,
+ "normalized.txt",
+ &bid,
+ GIT_FILEMODE_BLOB_GROUP_WRITABLE));
+
+ git_treebuilder_free(builder);
+}
+
+void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from_an_existing_one(void)
+{
+ git_treebuilder *builder;
+ git_oid tid, tid2;
+ git_tree *tree;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_oid_fromstr(&tid, tree_oid));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tid));
+
+ cl_git_pass(git_treebuilder_new(&builder, repo, tree));
+
+ entry = git_treebuilder_get(builder, "old_mode.txt");
+ cl_assert(entry != NULL);
+ cl_assert_equal_i(
+ GIT_FILEMODE_BLOB,
+ git_tree_entry_filemode(entry));
+
+ cl_git_pass(git_treebuilder_write(&tid2, builder));
+ git_treebuilder_free(builder);
+ git_tree_free(tree);
+
+ cl_git_pass(git_tree_lookup(&tree, repo, &tid2));
+ entry = git_tree_entry_byname(tree, "old_mode.txt");
+ cl_assert(entry != NULL);
+ cl_assert_equal_i(
+ GIT_FILEMODE_BLOB,
+ git_tree_entry_filemode(entry));
+
+ git_tree_free(tree);
+}
+
+void test_object_tree_attributes__normalize_600(void)
+{
+ git_oid id;
+ git_tree *tree;
+ const git_tree_entry *entry;
+
+ git_oid_fromstr(&id, "0810fb7818088ff5ac41ee49199b51473b1bd6c7");
+ cl_git_pass(git_tree_lookup(&tree, repo, &id));
+
+ entry = git_tree_entry_byname(tree, "ListaTeste.xml");
+ cl_assert_equal_i(git_tree_entry_filemode(entry), GIT_FILEMODE_BLOB);
+ cl_assert_equal_i(git_tree_entry_filemode_raw(entry), 0100600);
+
+ git_tree_free(tree);
+}
diff --git a/tests/libgit2/object/tree/duplicateentries.c b/tests/libgit2/object/tree/duplicateentries.c
new file mode 100644
index 000000000..c11ae0d3d
--- /dev/null
+++ b/tests/libgit2/object/tree/duplicateentries.c
@@ -0,0 +1,157 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+
+static git_repository *_repo;
+
+void test_object_tree_duplicateentries__initialize(void) {
+ _repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_duplicateentries__cleanup(void) {
+ cl_git_sandbox_cleanup();
+}
+
+/*
+ * $ git show --format=raw refs/heads/dir
+ * commit 144344043ba4d4a405da03de3844aa829ae8be0e
+ * tree d52a8fe84ceedf260afe4f0287bbfca04a117e83
+ * parent cf80f8de9f1185bf3a05f993f6121880dd0cfbc9
+ * author Ben Straub <bstraub@github.com> 1343755506 -0700
+ * committer Ben Straub <bstraub@github.com> 1343755506 -0700
+ *
+ * Change a file mode
+ *
+ * diff --git a/a/b.txt b/a/b.txt
+ * old mode 100644
+ * new mode 100755
+ *
+ * $ git ls-tree d52a8fe84ceedf260afe4f0287bbfca04a117e83
+ * 100644 blob a8233120f6ad708f843d861ce2b7228ec4e3dec6 README
+ * 040000 tree 4e0883eeeeebc1fb1735161cea82f7cb5fab7e63 a
+ * 100644 blob 45b983be36b73c0788dc9cbcb76cbb80fc7bb057 branch_file.txt
+ * 100644 blob a71586c1dfe8a71c6cbf6c129f404c5642ff31bd new.txt
+ */
+
+static void tree_checker(
+ git_oid *tid,
+ const char *expected_sha,
+ git_filemode_t expected_filemode)
+{
+ git_tree *tree;
+ const git_tree_entry *entry;
+ git_oid oid;
+
+ cl_git_pass(git_tree_lookup(&tree, _repo, tid));
+ cl_assert_equal_i(1, (int)git_tree_entrycount(tree));
+ entry = git_tree_entry_byindex(tree, 0);
+
+ cl_git_pass(git_oid_fromstr(&oid, expected_sha));
+
+ cl_assert_equal_i(0, git_oid_cmp(&oid, git_tree_entry_id(entry)));
+ cl_assert_equal_i(expected_filemode, git_tree_entry_filemode(entry));
+
+ git_tree_free(tree);
+}
+
+static void tree_creator(git_oid *out, void (*fn)(git_treebuilder *))
+{
+ git_treebuilder *builder;
+
+ cl_git_pass(git_treebuilder_new(&builder, _repo, NULL));
+
+ fn(builder);
+
+ cl_git_pass(git_treebuilder_write(out, builder));
+ git_treebuilder_free(builder);
+}
+
+static void two_blobs(git_treebuilder *bld)
+{
+ git_oid oid;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "a8233120f6ad708f843d861ce2b7228ec4e3dec6")); /* blob oid (README) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd")); /* blob oid (new.txt) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_BLOB));
+}
+
+static void one_blob_and_one_tree(git_treebuilder *bld)
+{
+ git_oid oid;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "a8233120f6ad708f843d861ce2b7228ec4e3dec6")); /* blob oid (README) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "4e0883eeeeebc1fb1735161cea82f7cb5fab7e63")); /* tree oid (a) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_TREE));
+}
+
+void test_object_tree_duplicateentries__cannot_create_a_duplicate_entry_through_the_treebuilder(void)
+{
+ git_oid tid;
+
+ tree_creator(&tid, two_blobs);
+ tree_checker(&tid, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd", GIT_FILEMODE_BLOB);
+
+ tree_creator(&tid, one_blob_and_one_tree);
+ tree_checker(&tid, "4e0883eeeeebc1fb1735161cea82f7cb5fab7e63", GIT_FILEMODE_TREE);
+}
+
+static void add_fake_conflicts(git_index *index)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "duplicate";
+ ancestor_entry.mode = GIT_FILEMODE_BLOB;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, 1);
+ git_oid_fromstr(&ancestor_entry.id, "a8233120f6ad708f843d861ce2b7228ec4e3dec6");
+
+ our_entry.path = "duplicate";
+ our_entry.mode = GIT_FILEMODE_BLOB;
+ GIT_INDEX_ENTRY_STAGE_SET(&our_entry, 2);
+ git_oid_fromstr(&our_entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
+
+ their_entry.path = "duplicate";
+ their_entry.mode = GIT_FILEMODE_BLOB;
+ GIT_INDEX_ENTRY_STAGE_SET(&their_entry, 3);
+ git_oid_fromstr(&their_entry.id, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd");
+
+ cl_git_pass(git_index_conflict_add(index, &ancestor_entry, &our_entry, &their_entry));
+}
+
+void test_object_tree_duplicateentries__cannot_create_a_duplicate_entry_building_a_tree_from_a_index_with_conflicts(void)
+{
+ git_index *index;
+ git_oid tid;
+
+ cl_git_pass(git_repository_index(&index, _repo));
+
+ add_fake_conflicts(index);
+
+ cl_assert_equal_i(GIT_EUNMERGED, git_index_write_tree(&tid, index));
+
+ git_index_free(index);
+}
diff --git a/tests/libgit2/object/tree/frompath.c b/tests/libgit2/object/tree/frompath.c
new file mode 100644
index 000000000..86ca47e94
--- /dev/null
+++ b/tests/libgit2/object/tree/frompath.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+
+static git_repository *repo;
+static git_tree *tree;
+
+void test_object_tree_frompath__initialize(void)
+{
+ git_oid id;
+ const char *tree_with_subtrees_oid = "ae90f12eea699729ed24555e40b9fd669da12a12";
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_assert(repo != NULL);
+
+ cl_git_pass(git_oid_fromstr(&id, tree_with_subtrees_oid));
+ cl_git_pass(git_tree_lookup(&tree, repo, &id));
+ cl_assert(tree != NULL);
+}
+
+void test_object_tree_frompath__cleanup(void)
+{
+ git_tree_free(tree);
+ tree = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+static void assert_tree_from_path(
+ git_tree *root,
+ const char *path,
+ const char *expected_entry_name)
+{
+ git_tree_entry *entry;
+
+ cl_git_pass(git_tree_entry_bypath(&entry, root, path));
+ cl_assert_equal_s(git_tree_entry_name(entry), expected_entry_name);
+ git_tree_entry_free(entry);
+}
+
+void test_object_tree_frompath__retrieve_tree_from_path_to_treeentry(void)
+{
+ git_tree_entry *e;
+
+ assert_tree_from_path(tree, "README", "README");
+ assert_tree_from_path(tree, "ab/de/fgh/1.txt", "1.txt");
+ assert_tree_from_path(tree, "ab/de/fgh", "fgh");
+ assert_tree_from_path(tree, "ab/de/fgh/", "fgh");
+ assert_tree_from_path(tree, "ab/de", "de");
+ assert_tree_from_path(tree, "ab/", "ab");
+ assert_tree_from_path(tree, "ab/de/", "de");
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "i-do-not-exist.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "README/"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "ab/de/fgh/i-do-not-exist.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "nope/de/fgh/1.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "ab/me-neither/fgh/2.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "ab/me-neither/fgh/2.txt/"));
+}
+
+void test_object_tree_frompath__fail_when_processing_an_invalid_path(void)
+{
+ git_tree_entry *e;
+
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "/"));
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "/ab"));
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "/ab/de"));
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "ab//de"));
+}
diff --git a/tests/libgit2/object/tree/parse.c b/tests/libgit2/object/tree/parse.c
new file mode 100644
index 000000000..9d76a74f0
--- /dev/null
+++ b/tests/libgit2/object/tree/parse.c
@@ -0,0 +1,164 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+#include "object.h"
+
+#define OID1_HEX \
+ "\xae\x90\xf1\x2e\xea\x69\x97\x29\xed\x24" \
+ "\x55\x5e\x40\xb9\xfd\x66\x9d\xa1\x2a\x12"
+#define OID1_STR "ae90f12eea699729ed24555e40b9fd669da12a12"
+
+#define OID2_HEX \
+ "\xe8\xbf\xe5\xaf\x39\x57\x9a\x7e\x48\x98" \
+ "\xbb\x23\xf3\xa7\x6a\x72\xc3\x68\xce\xe6"
+#define OID2_STR "e8bfe5af39579a7e4898bb23f3a76a72c368cee6"
+
+typedef struct {
+ const char *filename;
+ uint16_t attr;
+ const char *oid;
+} expected_entry;
+
+static void assert_tree_parses(const char *data, size_t datalen,
+ expected_entry *expected_entries, size_t expected_nentries)
+{
+ git_tree *tree;
+ size_t n;
+
+ if (!datalen)
+ datalen = strlen(data);
+ cl_git_pass(git_object__from_raw((git_object **) &tree, data, datalen, GIT_OBJECT_TREE));
+
+ cl_assert_equal_i(git_tree_entrycount(tree), expected_nentries);
+
+ for (n = 0; n < expected_nentries; n++) {
+ expected_entry *expected = expected_entries + n;
+ const git_tree_entry *entry;
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, expected->oid));
+
+ cl_assert(entry = git_tree_entry_byname(tree, expected->filename));
+ cl_assert_equal_s(expected->filename, entry->filename);
+ cl_assert_equal_i(expected->attr, entry->attr);
+ cl_assert_equal_oid(&oid, entry->oid);
+ }
+
+ git_object_free(&tree->object);
+}
+
+static void assert_tree_fails(const char *data, size_t datalen)
+{
+ git_object *object;
+ if (!datalen)
+ datalen = strlen(data);
+ cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_TREE));
+}
+
+void test_object_tree_parse__single_blob_parses(void)
+{
+ expected_entry entries[] = {
+ { "foo", 0100644, OID1_STR },
+ };
+ const char data[] = "100644 foo\x00" OID1_HEX;
+
+ assert_tree_parses(data, ARRAY_SIZE(data) - 1, entries, ARRAY_SIZE(entries));
+}
+
+void test_object_tree_parse__single_tree_parses(void)
+{
+ expected_entry entries[] = {
+ { "foo", 040000, OID1_STR },
+ };
+ const char data[] = "040000 foo\x00" OID1_HEX;
+
+ assert_tree_parses(data, ARRAY_SIZE(data) - 1, entries, ARRAY_SIZE(entries));
+}
+
+void test_object_tree_parse__leading_filename_spaces_parse(void)
+{
+ expected_entry entries[] = {
+ { " bar", 0100644, OID1_STR },
+ };
+ const char data[] = "100644 bar\x00" OID1_HEX;
+
+ assert_tree_parses(data, ARRAY_SIZE(data) - 1, entries, ARRAY_SIZE(entries));
+}
+
+void test_object_tree_parse__multiple_entries_parse(void)
+{
+ expected_entry entries[] = {
+ { "bar", 0100644, OID1_STR },
+ { "foo", 040000, OID2_STR },
+ };
+ const char data[] =
+ "100644 bar\x00" OID1_HEX
+ "040000 foo\x00" OID2_HEX;
+
+ assert_tree_parses(data, ARRAY_SIZE(data) - 1, entries, ARRAY_SIZE(entries));
+}
+
+void test_object_tree_parse__invalid_mode_fails(void)
+{
+ const char data[] = "10x644 bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__missing_mode_fails(void)
+{
+ const char data[] = " bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__mode_doesnt_cause_oob_read(void)
+{
+ const char data[] = "100644 bar\x00" OID1_HEX;
+ assert_tree_fails(data, 2);
+ /*
+ * An oob-read would correctly parse the filename and
+ * later fail to parse the OID with a different error
+ * message
+ */
+ cl_assert_equal_s(git_error_last()->message, "failed to parse tree: missing space after filemode");
+}
+
+void test_object_tree_parse__unreasonably_large_mode_fails(void)
+{
+ const char data[] = "10000000000000000000000000 bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__missing_filename_separator_fails(void)
+{
+ const char data[] = "100644bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__missing_filename_terminator_fails(void)
+{
+ const char data[] = "100644 bar" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__empty_filename_fails(void)
+{
+ const char data[] = "100644 \x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__trailing_garbage_fails(void)
+{
+ const char data[] = "100644 bar\x00" OID1_HEX "x";
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__leading_space_fails(void)
+{
+ const char data[] = " 100644 bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__truncated_oid_fails(void)
+{
+ const char data[] = " 100644 bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 2);
+}
diff --git a/tests/libgit2/object/tree/read.c b/tests/libgit2/object/tree/read.c
new file mode 100644
index 000000000..95a2e70fb
--- /dev/null
+++ b/tests/libgit2/object/tree/read.c
@@ -0,0 +1,119 @@
+#include "clar_libgit2.h"
+
+#include "tree.h"
+
+static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
+
+static git_repository *g_repo;
+
+/* Fixture setup and teardown */
+void test_object_tree_read__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_read__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_object_tree_read__loaded(void)
+{
+ /* access randomly the entries on a loaded tree */
+ git_oid id;
+ git_tree *tree;
+
+ git_oid_fromstr(&id, tree_oid);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ cl_assert(git_tree_entry_byname(tree, "README") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "NOTEXISTS") == NULL);
+ cl_assert(git_tree_entry_byname(tree, "") == NULL);
+ cl_assert(git_tree_entry_byindex(tree, 0) != NULL);
+ cl_assert(git_tree_entry_byindex(tree, 2) != NULL);
+ cl_assert(git_tree_entry_byindex(tree, 3) == NULL);
+ cl_assert(git_tree_entry_byindex(tree, (unsigned int)-1) == NULL);
+
+ git_tree_free(tree);
+}
+
+void test_object_tree_read__two(void)
+{
+ /* read a tree from the repository */
+ git_oid id;
+ git_tree *tree;
+ const git_tree_entry *entry;
+ git_object *obj;
+
+ git_oid_fromstr(&id, tree_oid);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ cl_assert(git_tree_entrycount(tree) == 3);
+
+ /* GH-86: git_object_lookup() should also check the type if the object comes from the cache */
+ cl_assert(git_object_lookup(&obj, g_repo, &id, GIT_OBJECT_TREE) == 0);
+ cl_assert(obj != NULL);
+ git_object_free(obj);
+ obj = NULL;
+ cl_git_fail(git_object_lookup(&obj, g_repo, &id, GIT_OBJECT_BLOB));
+ cl_assert(obj == NULL);
+
+ entry = git_tree_entry_byname(tree, "README");
+ cl_assert(entry != NULL);
+
+ cl_assert_equal_s(git_tree_entry_name(entry), "README");
+
+ cl_git_pass(git_tree_entry_to_object(&obj, g_repo, entry));
+ cl_assert(obj != NULL);
+
+ git_object_free(obj);
+ git_tree_free(tree);
+}
+
+#define BIGFILE "bigfile"
+
+#ifdef GIT_ARCH_64
+#define BIGFILE_SIZE (off_t)4294967296
+#else
+# define BIGFILE_SIZE SIZE_MAX
+#endif
+
+void test_object_tree_read__largefile(void)
+{
+ const git_tree_entry *entry;
+ git_index_entry ie;
+ git_commit *commit;
+ git_object *object;
+ git_index *index;
+ git_tree *tree;
+ git_oid oid;
+ char *buf;
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE"))
+ cl_skip();
+
+ cl_assert(buf = git__calloc(1, BIGFILE_SIZE));
+
+ memset(&ie, 0, sizeof(ie));
+ ie.mode = GIT_FILEMODE_BLOB;
+ ie.path = BIGFILE;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_from_buffer(index, &ie, buf, BIGFILE_SIZE));
+ cl_repo_commit_from_index(&oid, g_repo, NULL, 0, BIGFILE);
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_git_pass(git_commit_tree(&tree, commit));
+ cl_assert(entry = git_tree_entry_byname(tree, BIGFILE));
+ cl_git_pass(git_tree_entry_to_object(&object, g_repo, entry));
+
+ git_object_free(object);
+ git_tree_free(tree);
+ git_index_free(index);
+ git_commit_free(commit);
+ git__free(buf);
+}
diff --git a/tests/libgit2/object/tree/update.c b/tests/libgit2/object/tree/update.c
new file mode 100644
index 000000000..41b50f3e9
--- /dev/null
+++ b/tests/libgit2/object/tree/update.c
@@ -0,0 +1,302 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+
+static git_repository *g_repo;
+
+void test_object_tree_update__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo2");
+}
+
+void test_object_tree_update__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tree_update__remove_blob(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ const char *path = "README";
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_REMOVE, {{0}}, GIT_FILEMODE_BLOB /* ignored */, path},
+ };
+
+ cl_git_pass(git_oid_fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b"));
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+
+ /* Create it with an index */
+ cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+ cl_git_pass(git_index_remove(idx, path, 0));
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operation via the tree updater */
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 1, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__remove_blob_deeper(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ const char *path = "subdir/README";
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_REMOVE, {{0}}, GIT_FILEMODE_BLOB /* ignored */, path},
+ };
+
+ cl_git_pass(git_oid_fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b"));
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+
+ /* Create it with an index */
+ cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+ cl_git_pass(git_index_remove(idx, path, 0));
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operation via the tree updater */
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 1, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__remove_all_entries(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ const char *path1 = "subdir/subdir2/README";
+ const char *path2 = "subdir/subdir2/new.txt";
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_REMOVE, {{0}}, GIT_FILEMODE_BLOB /* ignored */, path1},
+ { GIT_TREE_UPDATE_REMOVE, {{0}}, GIT_FILEMODE_BLOB /* ignored */, path2},
+ };
+
+ cl_git_pass(git_oid_fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b"));
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+
+ /* Create it with an index */
+ cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+ cl_git_pass(git_index_remove(idx, path1, 0));
+ cl_git_pass(git_index_remove(idx, path2, 0));
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operation via the tree updater */
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 2, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__replace_blob(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ const char *path = "README";
+ git_index_entry entry = { {0} };
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, path},
+ };
+
+ cl_git_pass(git_oid_fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b"));
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+
+ /* Create it with an index */
+ cl_git_pass(git_index_new(&idx));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+
+ entry.path = path;
+ cl_git_pass(git_oid_fromstr(&entry.id, "fa49b077972391ad58037050f2a75f74e3671e92"));
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(idx, &entry));
+
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operation via the tree updater */
+ cl_git_pass(git_oid_fromstr(&updates[0].id, "fa49b077972391ad58037050f2a75f74e3671e92"));
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 1, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__add_blobs(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ git_index_entry entry = { {0} };
+ int i;
+ const char *paths[] = {
+ "some/deep/path",
+ "some/other/path",
+ "a/path/elsewhere",
+ };
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, paths[0]},
+ { GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, paths[1]},
+ { GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, paths[2]},
+ };
+
+ cl_git_pass(git_oid_fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b"));
+
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid_fromstr(&entry.id, "fa49b077972391ad58037050f2a75f74e3671e92"));
+
+ for (i = 0; i < 3; i++) {
+ cl_git_pass(git_oid_fromstr(&updates[i].id, "fa49b077972391ad58037050f2a75f74e3671e92"));
+ }
+
+ for (i = 0; i < 2; i++) {
+ int j;
+
+ /* Create it with an index */
+ cl_git_pass(git_index_new(&idx));
+
+ base_tree = NULL;
+ if (i == 1) {
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+ }
+
+ for (j = 0; j < 3; j++) {
+ entry.path = paths[j];
+ cl_git_pass(git_index_add(idx, &entry));
+ }
+
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operations via the tree updater */
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 3, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+ }
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__add_blobs_unsorted(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ git_index_entry entry = { {0} };
+ int i;
+ const char *paths[] = {
+ "some/deep/path",
+ "a/path/elsewhere",
+ "some/other/path",
+ };
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, paths[0]},
+ { GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, paths[1]},
+ { GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, paths[2]},
+ };
+
+ cl_git_pass(git_oid_fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b"));
+
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid_fromstr(&entry.id, "fa49b077972391ad58037050f2a75f74e3671e92"));
+
+ for (i = 0; i < 3; i++) {
+ cl_git_pass(git_oid_fromstr(&updates[i].id, "fa49b077972391ad58037050f2a75f74e3671e92"));
+ }
+
+ for (i = 0; i < 2; i++) {
+ int j;
+
+ /* Create it with an index */
+ cl_git_pass(git_index_new(&idx));
+
+ base_tree = NULL;
+ if (i == 1) {
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+ }
+
+ for (j = 0; j < 3; j++) {
+ entry.path = paths[j];
+ cl_git_pass(git_index_add(idx, &entry));
+ }
+
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operations via the tree updater */
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 3, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+ }
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__add_conflict(void)
+{
+ int i;
+ git_oid tree_updater_id;
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, "a/dir/blob"},
+ { GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, "a/dir"},
+ };
+
+ for (i = 0; i < 2; i++) {
+ cl_git_pass(git_oid_fromstr(&updates[i].id, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"));
+ }
+
+ cl_git_fail(git_tree_create_updated(&tree_updater_id, g_repo, NULL, 2, updates));
+}
+
+void test_object_tree_update__add_conflict2(void)
+{
+ int i;
+ git_oid tree_updater_id;
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_BLOB, "a/dir/blob"},
+ { GIT_TREE_UPDATE_UPSERT, {{0}}, GIT_FILEMODE_TREE, "a/dir/blob"},
+ };
+
+ for (i = 0; i < 2; i++) {
+ cl_git_pass(git_oid_fromstr(&updates[i].id, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"));
+ }
+
+ cl_git_fail(git_tree_create_updated(&tree_updater_id, g_repo, NULL, 2, updates));
+}
+
+void test_object_tree_update__remove_invalid_submodule(void)
+{
+ git_tree *baseline;
+ git_oid updated_tree_id, baseline_id;
+ git_tree_update updates[] = {
+ {GIT_TREE_UPDATE_REMOVE, {{0}}, GIT_FILEMODE_BLOB, "submodule"},
+ };
+
+ /* This tree contains a submodule with an all-zero commit for a submodule named 'submodule' */
+ cl_git_pass(git_oid_fromstr(&baseline_id, "396c7f1adb7925f51ba13a75f48252f44c5a14a2"));
+ cl_git_pass(git_tree_lookup(&baseline, g_repo, &baseline_id));
+ cl_git_pass(git_tree_create_updated(&updated_tree_id, g_repo, baseline, 1, updates));
+
+ git_tree_free(baseline);
+}
diff --git a/tests/libgit2/object/tree/walk.c b/tests/libgit2/object/tree/walk.c
new file mode 100644
index 000000000..d1fdaa3a0
--- /dev/null
+++ b/tests/libgit2/object/tree/walk.c
@@ -0,0 +1,177 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+
+static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
+static git_repository *g_repo;
+
+void test_object_tree_walk__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_walk__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int treewalk_count_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ int *count = payload;
+
+ GIT_UNUSED(root);
+ GIT_UNUSED(entry);
+
+ (*count) += 1;
+
+ return 0;
+}
+
+void test_object_tree_walk__0(void)
+{
+ git_oid id;
+ git_tree *tree;
+ int ct;
+
+ git_oid_fromstr(&id, tree_oid);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ ct = 0;
+ cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_count_cb, &ct));
+ cl_assert_equal_i(3, ct);
+
+ ct = 0;
+ cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_count_cb, &ct));
+ cl_assert_equal_i(3, ct);
+
+ git_tree_free(tree);
+}
+
+
+static int treewalk_stop_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ int *count = payload;
+
+ GIT_UNUSED(root);
+ GIT_UNUSED(entry);
+
+ (*count) += 1;
+
+ return (*count == 2) ? -123 : 0;
+}
+
+static int treewalk_stop_immediately_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ GIT_UNUSED(root);
+ GIT_UNUSED(entry);
+ GIT_UNUSED(payload);
+ return -100;
+}
+
+void test_object_tree_walk__1(void)
+{
+ git_oid id;
+ git_tree *tree;
+ int ct;
+
+ git_oid_fromstr(&id, tree_oid);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ ct = 0;
+ cl_assert_equal_i(
+ -123, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct));
+ cl_assert_equal_i(2, ct);
+
+ ct = 0;
+ cl_assert_equal_i(
+ -123, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct));
+ cl_assert_equal_i(2, ct);
+
+ cl_assert_equal_i(
+ -100, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_stop_immediately_cb, NULL));
+
+ cl_assert_equal_i(
+ -100, git_tree_walk(
+ tree, GIT_TREEWALK_POST, treewalk_stop_immediately_cb, NULL));
+
+ git_tree_free(tree);
+}
+
+
+struct treewalk_skip_data {
+ int files;
+ int dirs;
+ const char *skip;
+ const char *stop;
+};
+
+static int treewalk_skip_de_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ struct treewalk_skip_data *data = payload;
+ const char *name = git_tree_entry_name(entry);
+
+ GIT_UNUSED(root);
+
+ if (git_tree_entry_type(entry) == GIT_OBJECT_TREE)
+ data->dirs++;
+ else
+ data->files++;
+
+ if (data->skip && !strcmp(name, data->skip))
+ return 1;
+ else if (data->stop && !strcmp(name, data->stop))
+ return -1;
+ else
+ return 0;
+}
+
+void test_object_tree_walk__2(void)
+{
+ git_oid id;
+ git_tree *tree;
+ struct treewalk_skip_data data;
+
+ /* look up a deep tree */
+ git_oid_fromstr(&id, "ae90f12eea699729ed24555e40b9fd669da12a12");
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ memset(&data, 0, sizeof(data));
+ data.skip = "de";
+
+ cl_assert_equal_i(0, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(5, data.files);
+ cl_assert_equal_i(3, data.dirs);
+
+ memset(&data, 0, sizeof(data));
+ data.stop = "3.txt";
+
+ cl_assert_equal_i(-1, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(3, data.files);
+ cl_assert_equal_i(2, data.dirs);
+
+ memset(&data, 0, sizeof(data));
+ data.skip = "new.txt";
+
+ cl_assert_equal_i(0, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(7, data.files);
+ cl_assert_equal_i(4, data.dirs);
+
+ memset(&data, 0, sizeof(data));
+ data.stop = "new.txt";
+
+ cl_assert_equal_i(-1, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(7, data.files);
+ cl_assert_equal_i(4, data.dirs);
+
+ git_tree_free(tree);
+}
diff --git a/tests/libgit2/object/tree/write.c b/tests/libgit2/object/tree/write.c
new file mode 100644
index 000000000..a4ceb35b6
--- /dev/null
+++ b/tests/libgit2/object/tree/write.c
@@ -0,0 +1,526 @@
+#include "clar_libgit2.h"
+
+#include "tree.h"
+
+static const char *blob_oid = "fa49b077972391ad58037050f2a75f74e3671e92";
+static const char *first_tree = "181037049a54a1eb5fab404658a3a250b44335d7";
+static const char *second_tree = "f60079018b664e4e79329a7ef9559c8d9e0378d1";
+static const char *third_tree = "eb86d8b81d6adbd5290a935d6c9976882de98488";
+
+static git_repository *g_repo;
+
+/* Fixture setup and teardown */
+void test_object_tree_write__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_write__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
+}
+
+void test_object_tree_write__from_memory(void)
+{
+ /* write a tree from a memory */
+ git_treebuilder *builder;
+ git_tree *tree;
+ git_oid id, bid, rid, id2;
+
+ git_oid_fromstr(&id, first_tree);
+ git_oid_fromstr(&id2, second_tree);
+ git_oid_fromstr(&bid, blob_oid);
+
+ /* create a second tree from first tree using `git_treebuilder_insert`
+ * on REPOSITORY_FOLDER.
+ */
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, tree));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "/",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "..",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "folder/new.txt",
+ &bid, GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_treebuilder_write(&rid, builder));
+
+ cl_assert(git_oid_cmp(&rid, &id2) == 0);
+
+ git_treebuilder_free(builder);
+ git_tree_free(tree);
+}
+
+void test_object_tree_write__subtree(void)
+{
+ /* write a hierarchical tree from a memory */
+ git_treebuilder *builder;
+ git_tree *tree;
+ git_oid id, bid, subtree_id, id2, id3;
+ git_oid id_hiearar;
+
+ git_oid_fromstr(&id, first_tree);
+ git_oid_fromstr(&id2, second_tree);
+ git_oid_fromstr(&id3, third_tree);
+ git_oid_fromstr(&bid, blob_oid);
+
+ /* create subtree */
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); /* -V536 */
+ cl_git_pass(git_treebuilder_write(&subtree_id, builder));
+ git_treebuilder_free(builder);
+
+ /* create parent tree */
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, tree));
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "new", &subtree_id, GIT_FILEMODE_TREE)); /* -V536 */
+ cl_git_pass(git_treebuilder_write(&id_hiearar, builder));
+ git_treebuilder_free(builder);
+ git_tree_free(tree);
+
+ cl_assert(git_oid_cmp(&id_hiearar, &id3) == 0);
+
+ /* check data is correct */
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id_hiearar));
+ cl_assert(2 == git_tree_entrycount(tree));
+ git_tree_free(tree);
+}
+
+/*
+ * And the Lord said: Is this tree properly sorted?
+ */
+void test_object_tree_write__sorted_subtrees(void)
+{
+ git_treebuilder *builder;
+ git_tree *tree;
+ unsigned int i;
+ int position_c = -1, position_cake = -1, position_config = -1;
+
+ struct {
+ unsigned int attr;
+ const char *filename;
+ } entries[] = {
+ { GIT_FILEMODE_BLOB, ".gitattributes" },
+ { GIT_FILEMODE_BLOB, ".gitignore" },
+ { GIT_FILEMODE_BLOB, ".htaccess" },
+ { GIT_FILEMODE_BLOB, "Capfile" },
+ { GIT_FILEMODE_BLOB, "Makefile"},
+ { GIT_FILEMODE_BLOB, "README"},
+ { GIT_FILEMODE_TREE, "app"},
+ { GIT_FILEMODE_TREE, "cake"},
+ { GIT_FILEMODE_TREE, "config"},
+ { GIT_FILEMODE_BLOB, "c"},
+ { GIT_FILEMODE_BLOB, "git_test.txt"},
+ { GIT_FILEMODE_BLOB, "htaccess.htaccess"},
+ { GIT_FILEMODE_BLOB, "index.php"},
+ { GIT_FILEMODE_TREE, "plugins"},
+ { GIT_FILEMODE_TREE, "schemas"},
+ { GIT_FILEMODE_TREE, "ssl-certs"},
+ { GIT_FILEMODE_TREE, "vendors"}
+ };
+
+ git_oid bid, tid, tree_oid;
+
+ cl_git_pass(git_oid_fromstr(&bid, blob_oid));
+ cl_git_pass(git_oid_fromstr(&tid, first_tree));
+
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+ git_oid *id = entries[i].attr == GIT_FILEMODE_TREE ? &tid : &bid;
+
+ cl_git_pass(git_treebuilder_insert(NULL,
+ builder, entries[i].filename, id, entries[i].attr));
+ }
+
+ cl_git_pass(git_treebuilder_write(&tree_oid, builder));
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
+ for (i = 0; i < git_tree_entrycount(tree); i++) {
+ const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
+
+ if (strcmp(entry->filename, "c") == 0)
+ position_c = i;
+
+ if (strcmp(entry->filename, "cake") == 0)
+ position_cake = i;
+
+ if (strcmp(entry->filename, "config") == 0)
+ position_config = i;
+ }
+
+ git_tree_free(tree);
+
+ cl_assert(position_c != -1);
+ cl_assert(position_cake != -1);
+ cl_assert(position_config != -1);
+
+ cl_assert(position_c < position_cake);
+ cl_assert(position_cake < position_config);
+
+ git_treebuilder_free(builder);
+}
+
+static struct {
+ unsigned int attr;
+ const char *filename;
+} _entries[] = {
+ { GIT_FILEMODE_BLOB, "aardvark" },
+ { GIT_FILEMODE_BLOB, ".first" },
+ { GIT_FILEMODE_BLOB, "apple" },
+ { GIT_FILEMODE_BLOB, "last"},
+ { GIT_FILEMODE_BLOB, "apple_after"},
+ { GIT_FILEMODE_BLOB, "after_aardvark"},
+ { 0, NULL },
+};
+
+void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
+{
+ git_treebuilder *builder;
+ int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i;
+ git_oid entry_oid, tree_oid;
+ git_tree *tree;
+
+ cl_git_pass(git_oid_fromstr(&entry_oid, blob_oid));
+
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder));
+
+ for (i = 0; _entries[i].filename; ++i)
+ cl_git_pass(git_treebuilder_insert(NULL,
+ builder, _entries[i].filename, &entry_oid, _entries[i].attr));
+
+ cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_remove(builder, "apple"));
+ cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_remove(builder, "apple_after"));
+ cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "before_last", &entry_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder));
+
+ /* reinsert apple_after */
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "apple_after", &entry_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_remove(builder, "last"));
+ cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder));
+
+ /* reinsert last */
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "last", &entry_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "apple_extra", &entry_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_write(&tree_oid, builder));
+
+ git_treebuilder_free(builder);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
+
+ cl_assert_equal_i(7, (int)git_tree_entrycount(tree));
+
+ cl_assert(git_tree_entry_byname(tree, ".first") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "apple") == NULL);
+ cl_assert(git_tree_entry_byname(tree, "apple_after") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "apple_extra") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "last") != NULL);
+
+ aardvark_i = apple_i = apple_after_i = apple_extra_i = last_i = -1;
+
+ for (i = 0; i < 7; ++i) {
+ const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
+
+ if (!strcmp(entry->filename, "aardvark"))
+ aardvark_i = i;
+ else if (!strcmp(entry->filename, "apple"))
+ apple_i = i;
+ else if (!strcmp(entry->filename, "apple_after"))
+ apple_after_i = i;
+ else if (!strcmp(entry->filename, "apple_extra"))
+ apple_extra_i = i;
+ else if (!strcmp(entry->filename, "last"))
+ last_i = i;
+ }
+
+ cl_assert_equal_i(-1, apple_i);
+ cl_assert_equal_i(6, last_i);
+ cl_assert(aardvark_i < apple_after_i);
+ cl_assert(apple_after_i < apple_extra_i);
+
+ git_tree_free(tree);
+}
+
+static int treebuilder_filter_prefixed(
+ const git_tree_entry *entry, void *payload)
+{
+ return !git__prefixcmp(git_tree_entry_name(entry), payload);
+}
+
+void test_object_tree_write__filtering(void)
+{
+ git_treebuilder *builder;
+ int i;
+ git_oid entry_oid, tree_oid;
+ git_tree *tree;
+
+ git_oid_fromstr(&entry_oid, blob_oid);
+
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ for (i = 0; _entries[i].filename; ++i)
+ cl_git_pass(git_treebuilder_insert(NULL,
+ builder, _entries[i].filename, &entry_oid, _entries[i].attr));
+
+ cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
+
+ cl_assert(git_treebuilder_get(builder, "apple") != NULL);
+ cl_assert(git_treebuilder_get(builder, "aardvark") != NULL);
+ cl_assert(git_treebuilder_get(builder, "last") != NULL);
+
+ git_treebuilder_filter(builder, treebuilder_filter_prefixed, "apple");
+
+ cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder));
+
+ cl_assert(git_treebuilder_get(builder, "apple") == NULL);
+ cl_assert(git_treebuilder_get(builder, "aardvark") != NULL);
+ cl_assert(git_treebuilder_get(builder, "last") != NULL);
+
+ git_treebuilder_filter(builder, treebuilder_filter_prefixed, "a");
+
+ cl_assert_equal_i(2, (int)git_treebuilder_entrycount(builder));
+
+ cl_assert(git_treebuilder_get(builder, "aardvark") == NULL);
+ cl_assert(git_treebuilder_get(builder, "last") != NULL);
+
+ cl_git_pass(git_treebuilder_write(&tree_oid, builder));
+
+ git_treebuilder_free(builder);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
+
+ cl_assert_equal_i(2, (int)git_tree_entrycount(tree));
+
+ git_tree_free(tree);
+}
+
+void test_object_tree_write__cruel_paths(void)
+{
+ static const char *the_paths[] = {
+ "C:\\",
+ " : * ? \" \n < > |",
+ "a\\b",
+ "\\\\b\a",
+ ":\\",
+ "COM1",
+ "foo.aux",
+ REP1024("1234"), /* 4096 char string */
+ REP1024("12345678"), /* 8192 char string */
+ "\xC5\xAA\x6E\xC4\xAD\x63\xC5\x8D\x64\x65\xCC\xBD", /* Ūnĭcōde̽ */
+ NULL
+ };
+ git_treebuilder *builder;
+ git_tree *tree;
+ git_oid id, bid, subid;
+ const char **scan;
+ int count = 0, i, j;
+ git_tree_entry *te;
+
+ git_oid_fromstr(&bid, blob_oid);
+
+ /* create tree */
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+ for (scan = the_paths; *scan; ++scan) {
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, *scan, &bid, GIT_FILEMODE_BLOB));
+ count++;
+ }
+ cl_git_pass(git_treebuilder_write(&id, builder));
+ git_treebuilder_free(builder);
+
+ /* check data is correct */
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ cl_assert_equal_i(count, git_tree_entrycount(tree));
+
+ for (scan = the_paths; *scan; ++scan) {
+ const git_tree_entry *cte = git_tree_entry_byname(tree, *scan);
+ cl_assert(cte != NULL);
+ cl_assert_equal_s(*scan, git_tree_entry_name(cte));
+ }
+ for (scan = the_paths; *scan; ++scan) {
+ cl_git_pass(git_tree_entry_bypath(&te, tree, *scan));
+ cl_assert_equal_s(*scan, git_tree_entry_name(te));
+ git_tree_entry_free(te);
+ }
+
+ git_tree_free(tree);
+
+ /* let's try longer paths */
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+ for (scan = the_paths; *scan; ++scan) {
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, *scan, &id, GIT_FILEMODE_TREE));
+ }
+ cl_git_pass(git_treebuilder_write(&subid, builder));
+ git_treebuilder_free(builder);
+
+ /* check data is correct */
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &subid));
+
+ cl_assert_equal_i(count, git_tree_entrycount(tree));
+
+ for (i = 0; i < count; ++i) {
+ for (j = 0; j < count; ++j) {
+ git_str b = GIT_STR_INIT;
+ cl_git_pass(git_str_joinpath(&b, the_paths[i], the_paths[j]));
+ cl_git_pass(git_tree_entry_bypath(&te, tree, b.ptr));
+ cl_assert_equal_s(the_paths[j], git_tree_entry_name(te));
+ git_tree_entry_free(te);
+ git_str_dispose(&b);
+ }
+ }
+
+ git_tree_free(tree);
+}
+
+void test_object_tree_write__protect_filesystems(void)
+{
+ git_treebuilder *builder;
+ git_oid bid;
+
+ cl_git_pass(git_oid_fromstr(&bid, "fa49b077972391ad58037050f2a75f74e3671e92"));
+
+ /* Ensure that (by default) we can write objects with funny names on
+ * platforms that are not affected.
+ */
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB));
+
+#ifndef __APPLE__
+ cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB));
+#endif
+
+ git_treebuilder_free(builder);
+
+ /* Now turn on core.protectHFS and core.protectNTFS and validate that these
+ * paths are rejected.
+ */
+
+ cl_repo_set_bool(g_repo, "core.protectHFS", true);
+ cl_repo_set_bool(g_repo, "core.protectNTFS", true);
+
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git::$INDEX_ALLOCATION/dummy-file", &bid, GIT_FILEMODE_BLOB));
+
+ git_treebuilder_free(builder);
+}
+
+static void test_invalid_objects(bool should_allow_invalid)
+{
+ git_treebuilder *builder;
+ git_oid valid_blob_id, invalid_blob_id, valid_tree_id, invalid_tree_id;
+
+#define assert_allowed(expr) \
+ clar__assert(!(expr) == should_allow_invalid, \
+ __FILE__, __func__, __LINE__, \
+ (should_allow_invalid ? \
+ "Expected function call to succeed: " #expr : \
+ "Expected function call to fail: " #expr), \
+ NULL, 1)
+
+ cl_git_pass(git_oid_fromstr(&valid_blob_id, blob_oid));
+ cl_git_pass(git_oid_fromstr(&invalid_blob_id,
+ "1234567890123456789012345678901234567890"));
+ cl_git_pass(git_oid_fromstr(&valid_tree_id, first_tree));
+ cl_git_pass(git_oid_fromstr(&invalid_tree_id,
+ "0000000000111111111122222222223333333333"));
+
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ /* test valid blobs and trees (these should always pass) */
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "file.txt", &valid_blob_id, GIT_FILEMODE_BLOB));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "folder", &valid_tree_id, GIT_FILEMODE_TREE));
+
+ /* replace valid files and folders with invalid ones */
+ assert_allowed(git_treebuilder_insert(NULL, builder, "file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB));
+ assert_allowed(git_treebuilder_insert(NULL, builder, "folder", &invalid_blob_id, GIT_FILEMODE_BLOB));
+
+ /* insert new invalid files and folders */
+ assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB));
+ assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_folder", &invalid_blob_id, GIT_FILEMODE_BLOB));
+
+ /* insert valid blobs as trees and trees as blobs */
+ assert_allowed(git_treebuilder_insert(NULL, builder, "file_as_folder", &valid_blob_id, GIT_FILEMODE_TREE));
+ assert_allowed(git_treebuilder_insert(NULL, builder, "folder_as_file.txt", &valid_tree_id, GIT_FILEMODE_BLOB));
+
+#undef assert_allowed
+
+ git_treebuilder_free(builder);
+}
+
+static void test_inserting_submodule(void)
+{
+ git_treebuilder *bld;
+ git_oid sm_id;
+
+ cl_git_pass(git_oid_fromstr(&sm_id, "da39a3ee5e6b4b0d3255bfef95601890afd80709"));
+ cl_git_pass(git_treebuilder_new(&bld, g_repo, NULL));
+ cl_git_pass(git_treebuilder_insert(NULL, bld, "sm", &sm_id, GIT_FILEMODE_COMMIT));
+ git_treebuilder_free(bld);
+}
+
+void test_object_tree_write__object_validity(void)
+{
+ /* Ensure that we cannot add invalid objects by default */
+ test_invalid_objects(false);
+ test_inserting_submodule();
+
+ /* Ensure that we can turn off validation */
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
+ test_invalid_objects(true);
+ test_inserting_submodule();
+}
+
+void test_object_tree_write__invalid_null_oid(void)
+{
+ git_treebuilder *bld;
+ git_oid null_oid = {{0}};
+
+ cl_git_pass(git_treebuilder_new(&bld, g_repo, NULL));
+ cl_git_fail(git_treebuilder_insert(NULL, bld, "null_oid_file", &null_oid, GIT_FILEMODE_BLOB));
+ cl_assert(git_error_last() && strstr(git_error_last()->message, "null OID") != NULL);
+
+ git_treebuilder_free(bld);
+}
diff --git a/tests/libgit2/object/validate.c b/tests/libgit2/object/validate.c
new file mode 100644
index 000000000..87193deb6
--- /dev/null
+++ b/tests/libgit2/object/validate.c
@@ -0,0 +1,50 @@
+#include "clar_libgit2.h"
+
+#define VALID_COMMIT "tree bdd24e358576f1baa275df98cdcaf3ac9a3f4233\n" \
+ "parent d6d956f1d66210bfcd0484166befab33b5987a39\n" \
+ "author Edward Thomson <ethomson@edwardthomson.com> 1638286404 -0500\n" \
+ "committer Edward Thomson <ethomson@edwardthomson.com> 1638324642 -0500\n" \
+ "\n" \
+ "commit go here.\n"
+#define VALID_TREE "100644 HEADER\0\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+#define INVALID_COMMIT "tree bdd24e358576f1baa275df98cdcaf3ac9a3f4233\n" \
+ "parent d6d956f1d66210bfcd0484166befab33b5987a39\n" \
+ "committer Edward Thomson <ethomson@edwardthomson.com> 1638324642 -0500\n" \
+ "\n" \
+ "commit go here.\n"
+#define INVALID_TREE "100644 HEADER \x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+void test_object_validate__valid(void)
+{
+ int valid;
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_BLOB));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_BLOB));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_COMMIT, CONST_STRLEN(VALID_COMMIT), GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_TREE, CONST_STRLEN(VALID_TREE), GIT_OBJECT_TREE));
+ cl_assert_equal_i(1, valid);
+}
+
+void test_object_validate__invalid(void)
+{
+ int valid;
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_COMMIT, CONST_STRLEN(INVALID_COMMIT), GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_TREE, CONST_STRLEN(INVALID_TREE), GIT_OBJECT_TREE));
+ cl_assert_equal_i(0, valid);
+}