#include "unit-test.h" #include "hex.h" #include "object-file.h" #include "odb/source-inmemory.h" #include "odb/streaming.h" #include "oidset.h" #include "repository.h" #include "strbuf.h" #define RANDOM_OID "da39a3ee5e6b4b0d3255bfef95601890afd80709" #define FOOBAR_OID "f6ea0495187600e7b2288c8ac19c5886383a4632" static struct repository repo = { .hash_algo = &hash_algos[GIT_HASH_SHA1], }; static struct object_database *odb; static void cl_assert_object_info(struct odb_source_inmemory *source, const struct object_id *oid, enum object_type expected_type, const char *expected_content) { enum object_type actual_type; size_t actual_size; void *actual_content; struct object_info oi = { .typep = &actual_type, .sizep = &actual_size, .contentp = &actual_content, }; cl_must_pass(odb_source_read_object_info(&source->base, oid, &oi, 0, NULL)); cl_assert_equal_u(actual_size, strlen(expected_content)); cl_assert_equal_u(actual_type, expected_type); cl_assert_equal_s((char *) actual_content, expected_content); free(actual_content); } static void cl_assert_write_object(struct odb_source_inmemory *source, const char *content, enum object_type type, struct object_id *oid) { size_t content_len = strlen(content); hash_object_file(repo.hash_algo, content, content_len, type, oid); cl_must_pass(odb_source_write_object(&source->base, content, content_len, type, oid, NULL, NULL, 0)); } void test_odb_inmemory__initialize(void) { odb = odb_new(&repo, 0); } void test_odb_inmemory__cleanup(void) { odb_free(odb); } void test_odb_inmemory__new(void) { struct odb_source_inmemory *source = odb_source_inmemory_new(odb); cl_assert_equal_i(source->base.type, ODB_SOURCE_INMEMORY); odb_source_free(&source->base); } void test_odb_inmemory__read_missing_object(void) { struct odb_source_inmemory *source = odb_source_inmemory_new(odb); struct object_id oid; const char *end; cl_must_pass(parse_oid_hex_algop(RANDOM_OID, &oid, &end, repo.hash_algo)); cl_assert_equal_i(odb_source_read_object_info(&source->base, &oid, NULL, 0, NULL), ODB_READ_NOT_FOUND); odb_source_free(&source->base); } void test_odb_inmemory__read_empty_tree(void) { struct odb_source_inmemory *source = odb_source_inmemory_new(odb); cl_assert_object_info(source, repo.hash_algo->empty_tree, OBJ_TREE, ""); odb_source_free(&source->base); } void test_odb_inmemory__read_written_object(void) { struct odb_source_inmemory *source = odb_source_inmemory_new(odb); const char data[] = "foobar"; struct object_id written_oid; cl_assert_write_object(source, data, OBJ_BLOB, &written_oid); cl_assert_equal_s(oid_to_hex(&written_oid), FOOBAR_OID); cl_assert_object_info(source, &written_oid, OBJ_BLOB, "foobar"); odb_source_free(&source->base); } void test_odb_inmemory__read_stream_object(void) { struct odb_source_inmemory *source = odb_source_inmemory_new(odb); struct odb_stream *stream; struct object_id written_oid; const char data[] = "foobar"; char buf[3] = { 0 }; cl_assert_write_object(source, data, OBJ_BLOB, &written_oid); cl_must_pass(odb_source_read_object_stream(&stream, &source->base, &written_oid)); cl_assert_equal_i(stream->type, OBJ_BLOB); cl_assert_equal_u(stream->size, 6); cl_assert_equal_i(odb_stream_read(stream, buf, 2), 2); cl_assert_equal_s(buf, "fo"); cl_assert_equal_i(odb_stream_read(stream, buf, 2), 2); cl_assert_equal_s(buf, "ob"); cl_assert_equal_i(odb_stream_read(stream, buf, 2), 2); cl_assert_equal_s(buf, "ar"); cl_assert_equal_i(odb_stream_read(stream, buf, 2), 0); odb_stream_close(stream); odb_source_free(&source->base); } static int add_one_object(const struct object_id *oid, struct object_info *oi UNUSED, void *payload) { struct oidset *actual_oids = payload; cl_must_pass(oidset_insert(actual_oids, oid)); return 0; } void test_odb_inmemory__for_each_object(void) { struct odb_source_inmemory *source = odb_source_inmemory_new(odb); struct odb_for_each_object_options opts = { 0 }; struct oidset expected_oids = OIDSET_INIT; struct oidset actual_oids = OIDSET_INIT; struct strbuf buf = STRBUF_INIT; cl_must_pass(odb_source_for_each_object(&source->base, NULL, add_one_object, &actual_oids, &opts)); cl_assert_equal_u(oidset_size(&actual_oids), 0); for (int i = 0; i < 10; i++) { struct object_id written_oid; strbuf_reset(&buf); strbuf_addf(&buf, "%d", i); cl_assert_write_object(source, buf.buf, OBJ_BLOB, &written_oid); cl_must_pass(oidset_insert(&expected_oids, &written_oid)); } cl_must_pass(odb_source_for_each_object(&source->base, NULL, add_one_object, &actual_oids, &opts)); cl_assert_equal_b(oidset_equal(&expected_oids, &actual_oids), true); odb_source_free(&source->base); oidset_clear(&expected_oids); oidset_clear(&actual_oids); strbuf_release(&buf); } static int abort_after_two_objects(const struct object_id *oid UNUSED, struct object_info *oi UNUSED, void *payload) { unsigned *counter = payload; (*counter)++; if (*counter == 2) return 123; return 0; } void test_odb_inmemory__for_each_object_can_abort_iteration(void) { struct odb_source_inmemory *source = odb_source_inmemory_new(odb); struct odb_for_each_object_options opts = { 0 }; struct object_id written_oid; unsigned counter = 0; cl_assert_write_object(source, "1", OBJ_BLOB, &written_oid); cl_assert_write_object(source, "2", OBJ_BLOB, &written_oid); cl_assert_write_object(source, "3", OBJ_BLOB, &written_oid); cl_assert_equal_i(odb_source_for_each_object(&source->base, NULL, abort_after_two_objects, &counter, &opts), 123); cl_assert_equal_u(counter, 2); odb_source_free(&source->base); } void test_odb_inmemory__count_objects(void) { struct odb_source_inmemory *source = odb_source_inmemory_new(odb); struct object_id written_oid; unsigned long count; cl_must_pass(odb_source_count_objects(&source->base, 0, &count)); cl_assert_equal_u(count, 0); cl_assert_write_object(source, "1", OBJ_BLOB, &written_oid); cl_assert_write_object(source, "2", OBJ_BLOB, &written_oid); cl_assert_write_object(source, "3", OBJ_BLOB, &written_oid); cl_must_pass(odb_source_count_objects(&source->base, 0, &count)); cl_assert_equal_u(count, 3); odb_source_free(&source->base); } void test_odb_inmemory__find_abbrev_len(void) { struct odb_source_inmemory *source = odb_source_inmemory_new(odb); struct object_id oid1, oid2; unsigned abbrev_len; /* * The two blobs we're about to write share the first 10 hex characters * of their object IDs ("a09f43dc45"), so at least 11 characters are * needed to tell them apart: * * "368317" -> a09f43dc4562d45115583f5094640ae237df55f7 * "514796" -> a09f43dc45fef837235eb7e6b1a6ca5e169a3981 * * With only one blob written we expect a length of 4. */ cl_assert_write_object(source, "368317", OBJ_BLOB, &oid1); cl_must_pass(odb_source_find_abbrev_len(&source->base, &oid1, 4, &abbrev_len)); cl_assert_equal_u(abbrev_len, 4); /* * With both objects present, the shared 10-character prefix means we * need at least 11 characters to uniquely identify either object. */ cl_assert_write_object(source, "514796", OBJ_BLOB, &oid2); cl_must_pass(odb_source_find_abbrev_len(&source->base, &oid1, 4, &abbrev_len)); cl_assert_equal_u(abbrev_len, 11); odb_source_free(&source->base); } void test_odb_inmemory__freshen_object(void) { struct odb_source_inmemory *source = odb_source_inmemory_new(odb); struct object_id written_oid; struct object_id oid; const char *end; cl_must_pass(parse_oid_hex_algop(RANDOM_OID, &oid, &end, repo.hash_algo)); cl_assert_equal_i(odb_source_freshen_object(&source->base, &oid, NULL), 0); cl_assert_write_object(source, "foobar", OBJ_BLOB, &written_oid); cl_assert_equal_i(odb_source_freshen_object(&source->base, &written_oid, NULL), 1); odb_source_free(&source->base); } struct membuf_write_stream { struct odb_stream base; const char *buf; size_t offset; }; static ssize_t membuf_write_stream_read(struct odb_stream *stream, char *buf, size_t len) { struct membuf_write_stream *s = container_of(stream, struct membuf_write_stream, base); size_t chunk_size = 2; if (s->offset == s->base.size) return 0; if (chunk_size > len) chunk_size = len; if (chunk_size > s->base.size - s->offset) chunk_size = s->base.size - s->offset; memcpy(buf, s->buf + s->offset, chunk_size); s->offset += chunk_size; return chunk_size; } void test_odb_inmemory__write_object_stream(void) { struct odb_source_inmemory *source = odb_source_inmemory_new(odb); const char data[] = "foobar"; struct membuf_write_stream stream = { .base = { .read = membuf_write_stream_read, .size = strlen(data), .type = OBJ_BLOB, }, .buf = data, }; struct object_id written_oid; cl_must_pass(odb_source_write_object_stream(&source->base, &stream.base, &written_oid)); cl_assert_equal_s(oid_to_hex(&written_oid), FOOBAR_OID); cl_assert_object_info(source, &written_oid, OBJ_BLOB, "foobar"); odb_source_free(&source->base); }