| #include "unit-test.h" |
| #include "hex.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; |
| unsigned long 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)); |
| 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); |
| } |
| |
| void test_odb_inmemory__initialize(void) |
| { |
| odb = odb_new(&repo, "", ""); |
| } |
| |
| 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_must_fail(odb_source_read_object_info(&source->base, &oid, NULL, 0)); |
| |
| 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_must_pass(odb_source_write_object(&source->base, data, strlen(data), |
| OBJ_BLOB, &written_oid, NULL, 0)); |
| 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_read_stream *stream; |
| struct object_id written_oid; |
| const char data[] = "foobar"; |
| char buf[3] = { 0 }; |
| |
| cl_must_pass(odb_source_write_object(&source->base, data, strlen(data), |
| OBJ_BLOB, &written_oid, NULL, 0)); |
| |
| 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_read_stream_read(stream, buf, 2), 2); |
| cl_assert_equal_s(buf, "fo"); |
| cl_assert_equal_i(odb_read_stream_read(stream, buf, 2), 2); |
| cl_assert_equal_s(buf, "ob"); |
| cl_assert_equal_i(odb_read_stream_read(stream, buf, 2), 2); |
| cl_assert_equal_s(buf, "ar"); |
| cl_assert_equal_i(odb_read_stream_read(stream, buf, 2), 0); |
| |
| odb_read_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_must_pass(odb_source_write_object(&source->base, buf.buf, buf.len, |
| OBJ_BLOB, &written_oid, NULL, 0)); |
| 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_must_pass(odb_source_write_object(&source->base, "1", 1, |
| OBJ_BLOB, &written_oid, NULL, 0)); |
| cl_must_pass(odb_source_write_object(&source->base, "2", 1, |
| OBJ_BLOB, &written_oid, NULL, 0)); |
| cl_must_pass(odb_source_write_object(&source->base, "3", 1, |
| OBJ_BLOB, &written_oid, NULL, 0)); |
| |
| 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_must_pass(odb_source_write_object(&source->base, "1", 1, |
| OBJ_BLOB, &written_oid, NULL, 0)); |
| cl_must_pass(odb_source_write_object(&source->base, "2", 1, |
| OBJ_BLOB, &written_oid, NULL, 0)); |
| cl_must_pass(odb_source_write_object(&source->base, "3", 1, |
| OBJ_BLOB, &written_oid, NULL, 0)); |
| |
| 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_must_pass(odb_source_write_object(&source->base, "368317", strlen("368317"), |
| OBJ_BLOB, &oid1, NULL, 0)); |
| 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_must_pass(odb_source_write_object(&source->base, "514796", strlen("514796"), |
| OBJ_BLOB, &oid2, NULL, 0)); |
| 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), 0); |
| |
| cl_must_pass(odb_source_write_object(&source->base, "foobar", |
| strlen("foobar"), OBJ_BLOB, |
| &written_oid, NULL, 0)); |
| cl_assert_equal_i(odb_source_freshen_object(&source->base, |
| &written_oid), 1); |
| |
| odb_source_free(&source->base); |
| } |
| |
| struct membuf_write_stream { |
| struct odb_write_stream base; |
| const char *buf; |
| size_t offset; |
| size_t size; |
| }; |
| |
| static ssize_t membuf_write_stream_read(struct odb_write_stream *stream, |
| unsigned char *buf, size_t len) |
| { |
| struct membuf_write_stream *s = container_of(stream, struct membuf_write_stream, base); |
| size_t chunk_size = 2; |
| |
| if (chunk_size > len) |
| chunk_size = len; |
| if (chunk_size > s->size - s->offset) |
| chunk_size = s->size - s->offset; |
| |
| memcpy(buf, s->buf + s->offset, chunk_size); |
| |
| s->offset += chunk_size; |
| if (s->offset == s->size) |
| s->base.is_finished = 1; |
| |
| 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, |
| .buf = data, |
| .size = strlen(data), |
| }; |
| struct object_id written_oid; |
| |
| cl_must_pass(odb_source_write_object_stream(&source->base, &stream.base, |
| strlen(data), &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); |
| } |