| #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); | |
| } |