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