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