Raw
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 size_t 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 }