diff options
| author | Edward Thomson <ethomson@edwardthomson.com> | 2021-11-16 23:29:22 -0500 |
|---|---|---|
| committer | Edward Thomson <ethomson@edwardthomson.com> | 2022-02-22 22:07:45 -0500 |
| commit | 3344fddc97bbdea9c1b6ebb6f7fb6dbd70b41dfb (patch) | |
| tree | fd6368a72944571c51627b40c592e7d58e0036e1 /tests/libgit2/object | |
| parent | 91ba089663f5efc3bd4ba14a5099372cf5ce57a6 (diff) | |
| download | libgit2-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')
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); +} |
