]>
Commit | Line | Data |
---|---|---|
1ae2b8cd HWN |
1 | /* |
2 | Copyright 2020 Google LLC | |
3 | ||
4 | Use of this source code is governed by a BSD-style | |
5 | license that can be found in the LICENSE file or at | |
6 | https://developers.google.com/open-source/licenses/bsd | |
7 | */ | |
8 | ||
9 | #include "merged.h" | |
10 | ||
11 | #include "system.h" | |
12 | ||
13 | #include "basics.h" | |
14 | #include "blocksource.h" | |
15 | #include "constants.h" | |
16 | #include "reader.h" | |
17 | #include "record.h" | |
18 | #include "test_framework.h" | |
19 | #include "reftable-merged.h" | |
20 | #include "reftable-tests.h" | |
21 | #include "reftable-generic.h" | |
22 | #include "reftable-writer.h" | |
23 | ||
24 | static void write_test_table(struct strbuf *buf, | |
25 | struct reftable_ref_record refs[], int n) | |
26 | { | |
22d2f70e ÆAB |
27 | uint64_t min = 0xffffffff; |
28 | uint64_t max = 0; | |
1ae2b8cd HWN |
29 | int i = 0; |
30 | int err; | |
31 | ||
32 | struct reftable_write_options opts = { | |
33 | .block_size = 256, | |
34 | }; | |
35 | struct reftable_writer *w = NULL; | |
36 | for (i = 0; i < n; i++) { | |
37 | uint64_t ui = refs[i].update_index; | |
38 | if (ui > max) { | |
39 | max = ui; | |
40 | } | |
41 | if (ui < min) { | |
42 | min = ui; | |
43 | } | |
44 | } | |
45 | ||
46 | w = reftable_new_writer(&strbuf_add_void, buf, &opts); | |
47 | reftable_writer_set_limits(w, min, max); | |
48 | ||
49 | for (i = 0; i < n; i++) { | |
50 | uint64_t before = refs[i].update_index; | |
51 | int n = reftable_writer_add_ref(w, &refs[i]); | |
52 | EXPECT(n == 0); | |
53 | EXPECT(before == refs[i].update_index); | |
54 | } | |
55 | ||
56 | err = reftable_writer_close(w); | |
57 | EXPECT_ERR(err); | |
58 | ||
59 | reftable_writer_free(w); | |
60 | } | |
61 | ||
62 | static void write_test_log_table(struct strbuf *buf, | |
63 | struct reftable_log_record logs[], int n, | |
64 | uint64_t update_index) | |
65 | { | |
66 | int i = 0; | |
67 | int err; | |
68 | ||
69 | struct reftable_write_options opts = { | |
70 | .block_size = 256, | |
71 | .exact_log_message = 1, | |
72 | }; | |
73 | struct reftable_writer *w = NULL; | |
74 | w = reftable_new_writer(&strbuf_add_void, buf, &opts); | |
75 | reftable_writer_set_limits(w, update_index, update_index); | |
76 | ||
77 | for (i = 0; i < n; i++) { | |
78 | int err = reftable_writer_add_log(w, &logs[i]); | |
79 | EXPECT_ERR(err); | |
80 | } | |
81 | ||
82 | err = reftable_writer_close(w); | |
83 | EXPECT_ERR(err); | |
84 | ||
85 | reftable_writer_free(w); | |
86 | } | |
87 | ||
88 | static struct reftable_merged_table * | |
89 | merged_table_from_records(struct reftable_ref_record **refs, | |
90 | struct reftable_block_source **source, | |
91 | struct reftable_reader ***readers, int *sizes, | |
92 | struct strbuf *buf, int n) | |
93 | { | |
94 | int i = 0; | |
95 | struct reftable_merged_table *mt = NULL; | |
96 | int err; | |
97 | struct reftable_table *tabs = | |
98 | reftable_calloc(n * sizeof(struct reftable_table)); | |
99 | *readers = reftable_calloc(n * sizeof(struct reftable_reader *)); | |
100 | *source = reftable_calloc(n * sizeof(**source)); | |
101 | for (i = 0; i < n; i++) { | |
102 | write_test_table(&buf[i], refs[i], sizes[i]); | |
103 | block_source_from_strbuf(&(*source)[i], &buf[i]); | |
104 | ||
105 | err = reftable_new_reader(&(*readers)[i], &(*source)[i], | |
106 | "name"); | |
107 | EXPECT_ERR(err); | |
108 | reftable_table_from_reader(&tabs[i], (*readers)[i]); | |
109 | } | |
110 | ||
111 | err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID); | |
112 | EXPECT_ERR(err); | |
113 | return mt; | |
114 | } | |
115 | ||
116 | static void readers_destroy(struct reftable_reader **readers, size_t n) | |
117 | { | |
118 | int i = 0; | |
119 | for (; i < n; i++) | |
120 | reftable_reader_free(readers[i]); | |
121 | reftable_free(readers); | |
122 | } | |
123 | ||
124 | static void test_merged_between(void) | |
125 | { | |
126 | uint8_t hash1[GIT_SHA1_RAWSZ] = { 1, 2, 3, 0 }; | |
127 | ||
128 | struct reftable_ref_record r1[] = { { | |
129 | .refname = "b", | |
130 | .update_index = 1, | |
131 | .value_type = REFTABLE_REF_VAL1, | |
132 | .value.val1 = hash1, | |
133 | } }; | |
134 | struct reftable_ref_record r2[] = { { | |
135 | .refname = "a", | |
136 | .update_index = 2, | |
137 | .value_type = REFTABLE_REF_DELETION, | |
138 | } }; | |
139 | ||
140 | struct reftable_ref_record *refs[] = { r1, r2 }; | |
141 | int sizes[] = { 1, 1 }; | |
142 | struct strbuf bufs[2] = { STRBUF_INIT, STRBUF_INIT }; | |
143 | struct reftable_block_source *bs = NULL; | |
144 | struct reftable_reader **readers = NULL; | |
145 | struct reftable_merged_table *mt = | |
146 | merged_table_from_records(refs, &bs, &readers, sizes, bufs, 2); | |
147 | int i; | |
148 | struct reftable_ref_record ref = { NULL }; | |
149 | struct reftable_iterator it = { NULL }; | |
150 | int err = reftable_merged_table_seek_ref(mt, &it, "a"); | |
151 | EXPECT_ERR(err); | |
152 | ||
153 | err = reftable_iterator_next_ref(&it, &ref); | |
154 | EXPECT_ERR(err); | |
155 | EXPECT(ref.update_index == 2); | |
156 | reftable_ref_record_release(&ref); | |
157 | reftable_iterator_destroy(&it); | |
158 | readers_destroy(readers, 2); | |
159 | reftable_merged_table_free(mt); | |
160 | for (i = 0; i < ARRAY_SIZE(bufs); i++) { | |
161 | strbuf_release(&bufs[i]); | |
162 | } | |
163 | reftable_free(bs); | |
164 | } | |
165 | ||
166 | static void test_merged(void) | |
167 | { | |
168 | uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 }; | |
169 | uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 }; | |
170 | struct reftable_ref_record r1[] = { | |
171 | { | |
172 | .refname = "a", | |
173 | .update_index = 1, | |
174 | .value_type = REFTABLE_REF_VAL1, | |
175 | .value.val1 = hash1, | |
176 | }, | |
177 | { | |
178 | .refname = "b", | |
179 | .update_index = 1, | |
180 | .value_type = REFTABLE_REF_VAL1, | |
181 | .value.val1 = hash1, | |
182 | }, | |
183 | { | |
184 | .refname = "c", | |
185 | .update_index = 1, | |
186 | .value_type = REFTABLE_REF_VAL1, | |
187 | .value.val1 = hash1, | |
188 | } | |
189 | }; | |
190 | struct reftable_ref_record r2[] = { { | |
191 | .refname = "a", | |
192 | .update_index = 2, | |
193 | .value_type = REFTABLE_REF_DELETION, | |
194 | } }; | |
195 | struct reftable_ref_record r3[] = { | |
196 | { | |
197 | .refname = "c", | |
198 | .update_index = 3, | |
199 | .value_type = REFTABLE_REF_VAL1, | |
200 | .value.val1 = hash2, | |
201 | }, | |
202 | { | |
203 | .refname = "d", | |
204 | .update_index = 3, | |
205 | .value_type = REFTABLE_REF_VAL1, | |
206 | .value.val1 = hash1, | |
207 | }, | |
208 | }; | |
209 | ||
f2b25514 HWN |
210 | struct reftable_ref_record *want[] = { |
211 | &r2[0], | |
212 | &r1[1], | |
213 | &r3[0], | |
214 | &r3[1], | |
1ae2b8cd HWN |
215 | }; |
216 | ||
217 | struct reftable_ref_record *refs[] = { r1, r2, r3 }; | |
218 | int sizes[3] = { 3, 1, 2 }; | |
219 | struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; | |
220 | struct reftable_block_source *bs = NULL; | |
221 | struct reftable_reader **readers = NULL; | |
222 | struct reftable_merged_table *mt = | |
223 | merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3); | |
224 | ||
225 | struct reftable_iterator it = { NULL }; | |
226 | int err = reftable_merged_table_seek_ref(mt, &it, "a"); | |
227 | struct reftable_ref_record *out = NULL; | |
228 | size_t len = 0; | |
229 | size_t cap = 0; | |
230 | int i = 0; | |
231 | ||
232 | EXPECT_ERR(err); | |
233 | EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID); | |
234 | EXPECT(reftable_merged_table_min_update_index(mt) == 1); | |
235 | ||
236 | while (len < 100) { /* cap loops/recursion. */ | |
237 | struct reftable_ref_record ref = { NULL }; | |
238 | int err = reftable_iterator_next_ref(&it, &ref); | |
239 | if (err > 0) { | |
240 | break; | |
241 | } | |
242 | if (len == cap) { | |
243 | cap = 2 * cap + 1; | |
244 | out = reftable_realloc( | |
245 | out, sizeof(struct reftable_ref_record) * cap); | |
246 | } | |
247 | out[len++] = ref; | |
248 | } | |
249 | reftable_iterator_destroy(&it); | |
250 | ||
251 | EXPECT(ARRAY_SIZE(want) == len); | |
252 | for (i = 0; i < len; i++) { | |
f2b25514 | 253 | EXPECT(reftable_ref_record_equal(want[i], &out[i], |
1ae2b8cd HWN |
254 | GIT_SHA1_RAWSZ)); |
255 | } | |
256 | for (i = 0; i < len; i++) { | |
257 | reftable_ref_record_release(&out[i]); | |
258 | } | |
259 | reftable_free(out); | |
260 | ||
261 | for (i = 0; i < 3; i++) { | |
262 | strbuf_release(&bufs[i]); | |
263 | } | |
264 | readers_destroy(readers, 3); | |
265 | reftable_merged_table_free(mt); | |
266 | reftable_free(bs); | |
267 | } | |
268 | ||
269 | static struct reftable_merged_table * | |
270 | merged_table_from_log_records(struct reftable_log_record **logs, | |
271 | struct reftable_block_source **source, | |
272 | struct reftable_reader ***readers, int *sizes, | |
273 | struct strbuf *buf, int n) | |
274 | { | |
275 | int i = 0; | |
276 | struct reftable_merged_table *mt = NULL; | |
277 | int err; | |
278 | struct reftable_table *tabs = | |
279 | reftable_calloc(n * sizeof(struct reftable_table)); | |
280 | *readers = reftable_calloc(n * sizeof(struct reftable_reader *)); | |
281 | *source = reftable_calloc(n * sizeof(**source)); | |
282 | for (i = 0; i < n; i++) { | |
283 | write_test_log_table(&buf[i], logs[i], sizes[i], i + 1); | |
284 | block_source_from_strbuf(&(*source)[i], &buf[i]); | |
285 | ||
286 | err = reftable_new_reader(&(*readers)[i], &(*source)[i], | |
287 | "name"); | |
288 | EXPECT_ERR(err); | |
289 | reftable_table_from_reader(&tabs[i], (*readers)[i]); | |
290 | } | |
291 | ||
292 | err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID); | |
293 | EXPECT_ERR(err); | |
294 | return mt; | |
295 | } | |
296 | ||
297 | static void test_merged_logs(void) | |
298 | { | |
299 | uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 }; | |
300 | uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 }; | |
301 | uint8_t hash3[GIT_SHA1_RAWSZ] = { 3 }; | |
302 | struct reftable_log_record r1[] = { | |
303 | { | |
304 | .refname = "a", | |
305 | .update_index = 2, | |
306 | .value_type = REFTABLE_LOG_UPDATE, | |
307 | .value.update = { | |
308 | .old_hash = hash2, | |
309 | /* deletion */ | |
310 | .name = "jane doe", | |
311 | .email = "jane@invalid", | |
312 | .message = "message2", | |
313 | } | |
314 | }, | |
315 | { | |
316 | .refname = "a", | |
317 | .update_index = 1, | |
318 | .value_type = REFTABLE_LOG_UPDATE, | |
319 | .value.update = { | |
320 | .old_hash = hash1, | |
321 | .new_hash = hash2, | |
322 | .name = "jane doe", | |
323 | .email = "jane@invalid", | |
324 | .message = "message1", | |
325 | } | |
326 | }, | |
327 | }; | |
328 | struct reftable_log_record r2[] = { | |
329 | { | |
330 | .refname = "a", | |
331 | .update_index = 3, | |
332 | .value_type = REFTABLE_LOG_UPDATE, | |
333 | .value.update = { | |
334 | .new_hash = hash3, | |
335 | .name = "jane doe", | |
336 | .email = "jane@invalid", | |
337 | .message = "message3", | |
338 | } | |
339 | }, | |
340 | }; | |
341 | struct reftable_log_record r3[] = { | |
342 | { | |
343 | .refname = "a", | |
344 | .update_index = 2, | |
345 | .value_type = REFTABLE_LOG_DELETION, | |
346 | }, | |
347 | }; | |
f2b25514 HWN |
348 | struct reftable_log_record *want[] = { |
349 | &r2[0], | |
350 | &r3[0], | |
351 | &r1[1], | |
1ae2b8cd HWN |
352 | }; |
353 | ||
354 | struct reftable_log_record *logs[] = { r1, r2, r3 }; | |
355 | int sizes[3] = { 2, 1, 1 }; | |
356 | struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; | |
357 | struct reftable_block_source *bs = NULL; | |
358 | struct reftable_reader **readers = NULL; | |
359 | struct reftable_merged_table *mt = merged_table_from_log_records( | |
360 | logs, &bs, &readers, sizes, bufs, 3); | |
361 | ||
362 | struct reftable_iterator it = { NULL }; | |
363 | int err = reftable_merged_table_seek_log(mt, &it, "a"); | |
364 | struct reftable_log_record *out = NULL; | |
365 | size_t len = 0; | |
366 | size_t cap = 0; | |
367 | int i = 0; | |
368 | ||
369 | EXPECT_ERR(err); | |
370 | EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID); | |
371 | EXPECT(reftable_merged_table_min_update_index(mt) == 1); | |
372 | ||
373 | while (len < 100) { /* cap loops/recursion. */ | |
374 | struct reftable_log_record log = { NULL }; | |
375 | int err = reftable_iterator_next_log(&it, &log); | |
376 | if (err > 0) { | |
377 | break; | |
378 | } | |
379 | if (len == cap) { | |
380 | cap = 2 * cap + 1; | |
381 | out = reftable_realloc( | |
382 | out, sizeof(struct reftable_log_record) * cap); | |
383 | } | |
384 | out[len++] = log; | |
385 | } | |
386 | reftable_iterator_destroy(&it); | |
387 | ||
388 | EXPECT(ARRAY_SIZE(want) == len); | |
389 | for (i = 0; i < len; i++) { | |
f2b25514 | 390 | EXPECT(reftable_log_record_equal(want[i], &out[i], |
1ae2b8cd HWN |
391 | GIT_SHA1_RAWSZ)); |
392 | } | |
393 | ||
394 | err = reftable_merged_table_seek_log_at(mt, &it, "a", 2); | |
395 | EXPECT_ERR(err); | |
396 | reftable_log_record_release(&out[0]); | |
397 | err = reftable_iterator_next_log(&it, &out[0]); | |
398 | EXPECT_ERR(err); | |
399 | EXPECT(reftable_log_record_equal(&out[0], &r3[0], GIT_SHA1_RAWSZ)); | |
400 | reftable_iterator_destroy(&it); | |
401 | ||
402 | for (i = 0; i < len; i++) { | |
403 | reftable_log_record_release(&out[i]); | |
404 | } | |
405 | reftable_free(out); | |
406 | ||
407 | for (i = 0; i < 3; i++) { | |
408 | strbuf_release(&bufs[i]); | |
409 | } | |
410 | readers_destroy(readers, 3); | |
411 | reftable_merged_table_free(mt); | |
412 | reftable_free(bs); | |
413 | } | |
414 | ||
415 | static void test_default_write_opts(void) | |
416 | { | |
417 | struct reftable_write_options opts = { 0 }; | |
418 | struct strbuf buf = STRBUF_INIT; | |
419 | struct reftable_writer *w = | |
420 | reftable_new_writer(&strbuf_add_void, &buf, &opts); | |
421 | ||
422 | struct reftable_ref_record rec = { | |
423 | .refname = "master", | |
424 | .update_index = 1, | |
425 | }; | |
426 | int err; | |
427 | struct reftable_block_source source = { NULL }; | |
428 | struct reftable_table *tab = reftable_calloc(sizeof(*tab) * 1); | |
429 | uint32_t hash_id; | |
430 | struct reftable_reader *rd = NULL; | |
431 | struct reftable_merged_table *merged = NULL; | |
432 | ||
433 | reftable_writer_set_limits(w, 1, 1); | |
434 | ||
435 | err = reftable_writer_add_ref(w, &rec); | |
436 | EXPECT_ERR(err); | |
437 | ||
438 | err = reftable_writer_close(w); | |
439 | EXPECT_ERR(err); | |
440 | reftable_writer_free(w); | |
441 | ||
442 | block_source_from_strbuf(&source, &buf); | |
443 | ||
444 | err = reftable_new_reader(&rd, &source, "filename"); | |
445 | EXPECT_ERR(err); | |
446 | ||
447 | hash_id = reftable_reader_hash_id(rd); | |
448 | EXPECT(hash_id == GIT_SHA1_FORMAT_ID); | |
449 | ||
450 | reftable_table_from_reader(&tab[0], rd); | |
451 | err = reftable_new_merged_table(&merged, tab, 1, GIT_SHA1_FORMAT_ID); | |
452 | EXPECT_ERR(err); | |
453 | ||
454 | reftable_reader_free(rd); | |
455 | reftable_merged_table_free(merged); | |
456 | strbuf_release(&buf); | |
457 | } | |
458 | ||
459 | /* XXX test refs_for(oid) */ | |
460 | ||
461 | int merged_test_main(int argc, const char *argv[]) | |
462 | { | |
463 | RUN_TEST(test_merged_logs); | |
464 | RUN_TEST(test_merged_between); | |
465 | RUN_TEST(test_merged); | |
466 | RUN_TEST(test_default_write_opts); | |
467 | return 0; | |
468 | } |