Skip to content

Commit fe633bd

Browse files
pks-tgitster
authored andcommitted
t/unit-tests: add tests for the in-memory object source
While the in-memory object source is a full-fledged source, our code base only exercises parts of its functionality because we only use it in git-blame(1). Implement unit tests to verify that the yet-unused functionality of the backend works as expected. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 98a194d commit fe633bd

File tree

3 files changed

+315
-0
lines changed

3 files changed

+315
-0
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,7 @@ CLAR_TEST_SUITES += u-hash
15291529
CLAR_TEST_SUITES += u-hashmap
15301530
CLAR_TEST_SUITES += u-list-objects-filter-options
15311531
CLAR_TEST_SUITES += u-mem-pool
1532+
CLAR_TEST_SUITES += u-odb-inmemory
15321533
CLAR_TEST_SUITES += u-oid-array
15331534
CLAR_TEST_SUITES += u-oidmap
15341535
CLAR_TEST_SUITES += u-oidtree

t/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ clar_test_suites = [
66
'unit-tests/u-hashmap.c',
77
'unit-tests/u-list-objects-filter-options.c',
88
'unit-tests/u-mem-pool.c',
9+
'unit-tests/u-odb-inmemory.c',
910
'unit-tests/u-oid-array.c',
1011
'unit-tests/u-oidmap.c',
1112
'unit-tests/u-oidtree.c',

t/unit-tests/u-odb-inmemory.c

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
#include "unit-test.h"
2+
#include "hex.h"
3+
#include "odb/source-inmemory.h"
4+
#include "odb/streaming.h"
5+
#include "oidset.h"
6+
#include "repository.h"
7+
#include "strbuf.h"
8+
9+
#define RANDOM_OID "da39a3ee5e6b4b0d3255bfef95601890afd80709"
10+
#define FOOBAR_OID "f6ea0495187600e7b2288c8ac19c5886383a4632"
11+
12+
static struct repository repo = {
13+
.hash_algo = &hash_algos[GIT_HASH_SHA1],
14+
};
15+
static struct object_database *odb;
16+
17+
static void cl_assert_object_info(struct odb_source_inmemory *source,
18+
const struct object_id *oid,
19+
enum object_type expected_type,
20+
const char *expected_content)
21+
{
22+
enum object_type actual_type;
23+
unsigned long actual_size;
24+
void *actual_content;
25+
struct object_info oi = {
26+
.typep = &actual_type,
27+
.sizep = &actual_size,
28+
.contentp = &actual_content,
29+
};
30+
31+
cl_must_pass(odb_source_read_object_info(&source->base, oid, &oi, 0));
32+
cl_assert_equal_u(actual_size, strlen(expected_content));
33+
cl_assert_equal_u(actual_type, expected_type);
34+
cl_assert_equal_s((char *) actual_content, expected_content);
35+
36+
free(actual_content);
37+
}
38+
39+
void test_odb_inmemory__initialize(void)
40+
{
41+
odb = odb_new(&repo, "", "");
42+
}
43+
44+
void test_odb_inmemory__cleanup(void)
45+
{
46+
odb_free(odb);
47+
}
48+
49+
void test_odb_inmemory__new(void)
50+
{
51+
struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
52+
cl_assert_equal_i(source->base.type, ODB_SOURCE_INMEMORY);
53+
odb_source_free(&source->base);
54+
}
55+
56+
void test_odb_inmemory__read_missing_object(void)
57+
{
58+
struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
59+
struct object_id oid;
60+
const char *end;
61+
62+
cl_must_pass(parse_oid_hex_algop(RANDOM_OID, &oid, &end, repo.hash_algo));
63+
cl_must_fail(odb_source_read_object_info(&source->base, &oid, NULL, 0));
64+
65+
odb_source_free(&source->base);
66+
}
67+
68+
void test_odb_inmemory__read_empty_tree(void)
69+
{
70+
struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
71+
cl_assert_object_info(source, repo.hash_algo->empty_tree, OBJ_TREE, "");
72+
odb_source_free(&source->base);
73+
}
74+
75+
void test_odb_inmemory__read_written_object(void)
76+
{
77+
struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
78+
const char data[] = "foobar";
79+
struct object_id written_oid;
80+
81+
cl_must_pass(odb_source_write_object(&source->base, data, strlen(data),
82+
OBJ_BLOB, &written_oid, NULL, 0));
83+
cl_assert_equal_s(oid_to_hex(&written_oid), FOOBAR_OID);
84+
cl_assert_object_info(source, &written_oid, OBJ_BLOB, "foobar");
85+
86+
odb_source_free(&source->base);
87+
}
88+
89+
void test_odb_inmemory__read_stream_object(void)
90+
{
91+
struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
92+
struct odb_read_stream *stream;
93+
struct object_id written_oid;
94+
const char data[] = "foobar";
95+
char buf[3] = { 0 };
96+
97+
cl_must_pass(odb_source_write_object(&source->base, data, strlen(data),
98+
OBJ_BLOB, &written_oid, NULL, 0));
99+
100+
cl_must_pass(odb_source_read_object_stream(&stream, &source->base,
101+
&written_oid));
102+
cl_assert_equal_i(stream->type, OBJ_BLOB);
103+
cl_assert_equal_u(stream->size, 6);
104+
105+
cl_assert_equal_i(odb_read_stream_read(stream, buf, 2), 2);
106+
cl_assert_equal_s(buf, "fo");
107+
cl_assert_equal_i(odb_read_stream_read(stream, buf, 2), 2);
108+
cl_assert_equal_s(buf, "ob");
109+
cl_assert_equal_i(odb_read_stream_read(stream, buf, 2), 2);
110+
cl_assert_equal_s(buf, "ar");
111+
cl_assert_equal_i(odb_read_stream_read(stream, buf, 2), 0);
112+
113+
odb_read_stream_close(stream);
114+
odb_source_free(&source->base);
115+
}
116+
117+
static int add_one_object(const struct object_id *oid,
118+
struct object_info *oi UNUSED,
119+
void *payload)
120+
{
121+
struct oidset *actual_oids = payload;
122+
cl_must_pass(oidset_insert(actual_oids, oid));
123+
return 0;
124+
}
125+
126+
void test_odb_inmemory__for_each_object(void)
127+
{
128+
struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
129+
struct odb_for_each_object_options opts = { 0 };
130+
struct oidset expected_oids = OIDSET_INIT;
131+
struct oidset actual_oids = OIDSET_INIT;
132+
struct strbuf buf = STRBUF_INIT;
133+
134+
cl_must_pass(odb_source_for_each_object(&source->base, NULL,
135+
add_one_object, &actual_oids, &opts));
136+
cl_assert_equal_u(oidset_size(&actual_oids), 0);
137+
138+
for (int i = 0; i < 10; i++) {
139+
struct object_id written_oid;
140+
141+
strbuf_reset(&buf);
142+
strbuf_addf(&buf, "%d", i);
143+
144+
cl_must_pass(odb_source_write_object(&source->base, buf.buf, buf.len,
145+
OBJ_BLOB, &written_oid, NULL, 0));
146+
cl_must_pass(oidset_insert(&expected_oids, &written_oid));
147+
}
148+
149+
cl_must_pass(odb_source_for_each_object(&source->base, NULL,
150+
add_one_object, &actual_oids, &opts));
151+
cl_assert_equal_b(oidset_equal(&expected_oids, &actual_oids), true);
152+
153+
odb_source_free(&source->base);
154+
oidset_clear(&expected_oids);
155+
oidset_clear(&actual_oids);
156+
strbuf_release(&buf);
157+
}
158+
159+
static int abort_after_two_objects(const struct object_id *oid UNUSED,
160+
struct object_info *oi UNUSED,
161+
void *payload)
162+
{
163+
unsigned *counter = payload;
164+
(*counter)++;
165+
if (*counter == 2)
166+
return 123;
167+
return 0;
168+
}
169+
170+
void test_odb_inmemory__for_each_object_can_abort_iteration(void)
171+
{
172+
struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
173+
struct odb_for_each_object_options opts = { 0 };
174+
struct object_id written_oid;
175+
unsigned counter = 0;
176+
177+
cl_must_pass(odb_source_write_object(&source->base, "1", 1,
178+
OBJ_BLOB, &written_oid, NULL, 0));
179+
cl_must_pass(odb_source_write_object(&source->base, "2", 1,
180+
OBJ_BLOB, &written_oid, NULL, 0));
181+
cl_must_pass(odb_source_write_object(&source->base, "3", 1,
182+
OBJ_BLOB, &written_oid, NULL, 0));
183+
184+
cl_assert_equal_i(odb_source_for_each_object(&source->base, NULL,
185+
abort_after_two_objects,
186+
&counter, &opts),
187+
123);
188+
cl_assert_equal_u(counter, 2);
189+
190+
odb_source_free(&source->base);
191+
}
192+
193+
void test_odb_inmemory__count_objects(void)
194+
{
195+
struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
196+
struct object_id written_oid;
197+
unsigned long count;
198+
199+
cl_must_pass(odb_source_count_objects(&source->base, 0, &count));
200+
cl_assert_equal_u(count, 0);
201+
202+
cl_must_pass(odb_source_write_object(&source->base, "1", 1,
203+
OBJ_BLOB, &written_oid, NULL, 0));
204+
cl_must_pass(odb_source_write_object(&source->base, "2", 1,
205+
OBJ_BLOB, &written_oid, NULL, 0));
206+
cl_must_pass(odb_source_write_object(&source->base, "3", 1,
207+
OBJ_BLOB, &written_oid, NULL, 0));
208+
209+
cl_must_pass(odb_source_count_objects(&source->base, 0, &count));
210+
cl_assert_equal_u(count, 3);
211+
212+
odb_source_free(&source->base);
213+
}
214+
215+
void test_odb_inmemory__find_abbrev_len(void)
216+
{
217+
struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
218+
struct object_id oid1, oid2;
219+
unsigned abbrev_len;
220+
221+
/*
222+
* The two blobs we're about to write share the first 10 hex characters
223+
* of their object IDs ("a09f43dc45"), so at least 11 characters are
224+
* needed to tell them apart:
225+
*
226+
* "368317" -> a09f43dc4562d45115583f5094640ae237df55f7
227+
* "514796" -> a09f43dc45fef837235eb7e6b1a6ca5e169a3981
228+
*
229+
* With only one blob written we expect a length of 4.
230+
*/
231+
cl_must_pass(odb_source_write_object(&source->base, "368317", strlen("368317"),
232+
OBJ_BLOB, &oid1, NULL, 0));
233+
cl_must_pass(odb_source_find_abbrev_len(&source->base, &oid1, 4,
234+
&abbrev_len));
235+
cl_assert_equal_u(abbrev_len, 4);
236+
237+
/*
238+
* With both objects present, the shared 10-character prefix means we
239+
* need at least 11 characters to uniquely identify either object.
240+
*/
241+
cl_must_pass(odb_source_write_object(&source->base, "514796", strlen("514796"),
242+
OBJ_BLOB, &oid2, NULL, 0));
243+
cl_must_pass(odb_source_find_abbrev_len(&source->base, &oid1, 4,
244+
&abbrev_len));
245+
cl_assert_equal_u(abbrev_len, 11);
246+
247+
odb_source_free(&source->base);
248+
}
249+
250+
void test_odb_inmemory__freshen_object(void)
251+
{
252+
struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
253+
struct object_id written_oid;
254+
struct object_id oid;
255+
const char *end;
256+
257+
cl_must_pass(parse_oid_hex_algop(RANDOM_OID, &oid, &end, repo.hash_algo));
258+
cl_assert_equal_i(odb_source_freshen_object(&source->base, &oid), 0);
259+
260+
cl_must_pass(odb_source_write_object(&source->base, "foobar",
261+
strlen("foobar"), OBJ_BLOB,
262+
&written_oid, NULL, 0));
263+
cl_assert_equal_i(odb_source_freshen_object(&source->base,
264+
&written_oid), 1);
265+
266+
odb_source_free(&source->base);
267+
}
268+
269+
struct membuf_write_stream {
270+
struct odb_write_stream base;
271+
const char *buf;
272+
size_t offset;
273+
size_t size;
274+
};
275+
276+
static ssize_t membuf_write_stream_read(struct odb_write_stream *stream,
277+
unsigned char *buf, size_t len)
278+
{
279+
struct membuf_write_stream *s = container_of(stream, struct membuf_write_stream, base);
280+
size_t chunk_size = 2;
281+
282+
if (chunk_size > len)
283+
chunk_size = len;
284+
if (chunk_size > s->size - s->offset)
285+
chunk_size = s->size - s->offset;
286+
287+
memcpy(buf, s->buf + s->offset, chunk_size);
288+
289+
s->offset += chunk_size;
290+
if (s->offset == s->size)
291+
s->base.is_finished = 1;
292+
293+
return chunk_size;
294+
}
295+
296+
void test_odb_inmemory__write_object_stream(void)
297+
{
298+
struct odb_source_inmemory *source = odb_source_inmemory_new(odb);
299+
const char data[] = "foobar";
300+
struct membuf_write_stream stream = {
301+
.base.read = membuf_write_stream_read,
302+
.buf = data,
303+
.size = strlen(data),
304+
};
305+
struct object_id written_oid;
306+
307+
cl_must_pass(odb_source_write_object_stream(&source->base, &stream.base,
308+
strlen(data), &written_oid));
309+
cl_assert_equal_s(oid_to_hex(&written_oid), FOOBAR_OID);
310+
cl_assert_object_info(source, &written_oid, OBJ_BLOB, "foobar");
311+
312+
odb_source_free(&source->base);
313+
}

0 commit comments

Comments
 (0)