Raw
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 "unit-test.h"
10 #include "lib-reftable.h"
11 #include "reftable/blocksource.h"
12 #include "reftable/constants.h"
13 #include "reftable/merged.h"
14 #include "reftable/table.h"
15 #include "reftable/reftable-error.h"
16 #include "reftable/reftable-merged.h"
17 #include "reftable/reftable-writer.h"
18
19 static struct reftable_merged_table *
20 merged_table_from_records(struct reftable_ref_record **refs,
21 struct reftable_block_source **source,
22 struct reftable_table ***tables, const size_t *sizes,
23 struct reftable_buf *buf, const size_t n)
24 {
25 struct reftable_merged_table *mt = NULL;
26 struct reftable_write_options opts = {
27 .block_size = 256,
28 };
29 int err;
30
31 REFTABLE_CALLOC_ARRAY(*tables, n);
32 cl_assert(*tables != NULL);
33 REFTABLE_CALLOC_ARRAY(*source, n);
34 cl_assert(*source != NULL);
35
36 for (size_t i = 0; i < n; i++) {
37 cl_reftable_write_to_buf(&buf[i], refs[i], sizes[i], NULL, 0, &opts);
38 block_source_from_buf(&(*source)[i], &buf[i]);
39
40 err = reftable_table_new(&(*tables)[i], &(*source)[i],
41 "name");
42 cl_assert(!err);
43 }
44
45 err = reftable_merged_table_new(&mt, *tables, n, REFTABLE_HASH_SHA1);
46 cl_assert(!err);
47 return mt;
48 }
49
50 static void tables_destroy(struct reftable_table **tables, const size_t n)
51 {
52 for (size_t i = 0; i < n; i++)
53 reftable_table_decref(tables[i]);
54 reftable_free(tables);
55 }
56
57 void test_reftable_merged__single_record(void)
58 {
59 struct reftable_ref_record r1[] = { {
60 .refname = (char *) "b",
61 .update_index = 1,
62 .value_type = REFTABLE_REF_VAL1,
63 .value.val1 = { 1, 2, 3, 0 },
64 } };
65 struct reftable_ref_record r2[] = { {
66 .refname = (char *) "a",
67 .update_index = 2,
68 .value_type = REFTABLE_REF_DELETION,
69 } };
70 struct reftable_ref_record r3[] = { {
71 .refname = (char *) "c",
72 .update_index = 3,
73 .value_type = REFTABLE_REF_DELETION,
74 } };
75
76 struct reftable_ref_record *refs[] = { r1, r2, r3 };
77 size_t sizes[] = { ARRAY_SIZE(r1), ARRAY_SIZE(r2), ARRAY_SIZE(r3) };
78 struct reftable_buf bufs[3] = { REFTABLE_BUF_INIT, REFTABLE_BUF_INIT, REFTABLE_BUF_INIT };
79 struct reftable_block_source *bs = NULL;
80 struct reftable_table **tables = NULL;
81 struct reftable_merged_table *mt =
82 merged_table_from_records(refs, &bs, &tables, sizes, bufs, 3);
83 struct reftable_ref_record ref = { 0 };
84 struct reftable_iterator it = { 0 };
85 int err;
86
87 err = merged_table_init_iter(mt, &it, REFTABLE_BLOCK_TYPE_REF);
88 cl_assert(!err);
89 err = reftable_iterator_seek_ref(&it, "a");
90 cl_assert(!err);
91
92 err = reftable_iterator_next_ref(&it, &ref);
93 cl_assert(!err);
94 cl_assert(reftable_ref_record_equal(&r2[0], &ref,
95 REFTABLE_HASH_SIZE_SHA1) != 0);
96 reftable_ref_record_release(&ref);
97 reftable_iterator_destroy(&it);
98 tables_destroy(tables, 3);
99 reftable_merged_table_free(mt);
100 for (size_t i = 0; i < ARRAY_SIZE(bufs); i++)
101 reftable_buf_release(&bufs[i]);
102 reftable_free(bs);
103 }
104
105 void test_reftable_merged__refs(void)
106 {
107 struct reftable_ref_record r1[] = {
108 {
109 .refname = (char *) "a",
110 .update_index = 1,
111 .value_type = REFTABLE_REF_VAL1,
112 .value.val1 = { 1 },
113 },
114 {
115 .refname = (char *) "b",
116 .update_index = 1,
117 .value_type = REFTABLE_REF_VAL1,
118 .value.val1 = { 1 },
119 },
120 {
121 .refname = (char *) "c",
122 .update_index = 1,
123 .value_type = REFTABLE_REF_VAL1,
124 .value.val1 = { 1 },
125 }
126 };
127 struct reftable_ref_record r2[] = { {
128 .refname = (char *) "a",
129 .update_index = 2,
130 .value_type = REFTABLE_REF_DELETION,
131 } };
132 struct reftable_ref_record r3[] = {
133 {
134 .refname = (char *) "c",
135 .update_index = 3,
136 .value_type = REFTABLE_REF_VAL1,
137 .value.val1 = { 2 },
138 },
139 {
140 .refname = (char *) "d",
141 .update_index = 3,
142 .value_type = REFTABLE_REF_VAL1,
143 .value.val1 = { 1 },
144 },
145 };
146
147 struct reftable_ref_record *want[] = {
148 &r2[0],
149 &r1[1],
150 &r3[0],
151 &r3[1],
152 };
153
154 struct reftable_ref_record *refs[] = { r1, r2, r3 };
155 size_t sizes[3] = { ARRAY_SIZE(r1), ARRAY_SIZE(r2), ARRAY_SIZE(r3) };
156 struct reftable_buf bufs[3] = { REFTABLE_BUF_INIT, REFTABLE_BUF_INIT, REFTABLE_BUF_INIT };
157 struct reftable_block_source *bs = NULL;
158 struct reftable_table **tables = NULL;
159 struct reftable_merged_table *mt =
160 merged_table_from_records(refs, &bs, &tables, sizes, bufs, 3);
161 struct reftable_iterator it = { 0 };
162 int err;
163 struct reftable_ref_record *out = NULL;
164 size_t len = 0;
165 size_t cap = 0;
166 size_t i;
167
168 err = merged_table_init_iter(mt, &it, REFTABLE_BLOCK_TYPE_REF);
169 cl_assert(!err);
170 err = reftable_iterator_seek_ref(&it, "a");
171 cl_assert(err == 0);
172 cl_assert_equal_i(reftable_merged_table_hash_id(mt), REFTABLE_HASH_SHA1);
173 cl_assert_equal_i(reftable_merged_table_min_update_index(mt), 1);
174 cl_assert_equal_i(reftable_merged_table_max_update_index(mt), 3);
175
176 while (len < 100) { /* cap loops/recursion. */
177 struct reftable_ref_record ref = { 0 };
178 int err = reftable_iterator_next_ref(&it, &ref);
179 if (err > 0)
180 break;
181
182 cl_assert(REFTABLE_ALLOC_GROW(out, len + 1, cap) == 0);
183 out[len++] = ref;
184 }
185 reftable_iterator_destroy(&it);
186
187 cl_assert_equal_i(ARRAY_SIZE(want), len);
188 for (i = 0; i < len; i++)
189 cl_assert(reftable_ref_record_equal(want[i], &out[i],
190 REFTABLE_HASH_SIZE_SHA1) != 0);
191 for (i = 0; i < len; i++)
192 reftable_ref_record_release(&out[i]);
193 reftable_free(out);
194
195 for (i = 0; i < 3; i++)
196 reftable_buf_release(&bufs[i]);
197 tables_destroy(tables, 3);
198 reftable_merged_table_free(mt);
199 reftable_free(bs);
200 }
201
202 void test_reftable_merged__seek_multiple_times(void)
203 {
204 struct reftable_ref_record r1[] = {
205 {
206 .refname = (char *) "a",
207 .update_index = 1,
208 .value_type = REFTABLE_REF_VAL1,
209 .value.val1 = { 1 },
210 },
211 {
212 .refname = (char *) "c",
213 .update_index = 1,
214 .value_type = REFTABLE_REF_VAL1,
215 .value.val1 = { 2 },
216 }
217 };
218 struct reftable_ref_record r2[] = {
219 {
220 .refname = (char *) "b",
221 .update_index = 2,
222 .value_type = REFTABLE_REF_VAL1,
223 .value.val1 = { 3 },
224 },
225 {
226 .refname = (char *) "d",
227 .update_index = 2,
228 .value_type = REFTABLE_REF_VAL1,
229 .value.val1 = { 4 },
230 },
231 };
232 struct reftable_ref_record *refs[] = {
233 r1, r2,
234 };
235 size_t sizes[] = {
236 ARRAY_SIZE(r1), ARRAY_SIZE(r2),
237 };
238 struct reftable_buf bufs[] = {
239 REFTABLE_BUF_INIT, REFTABLE_BUF_INIT,
240 };
241 struct reftable_block_source *sources = NULL;
242 struct reftable_table **tables = NULL;
243 struct reftable_ref_record rec = { 0 };
244 struct reftable_iterator it = { 0 };
245 struct reftable_merged_table *mt;
246
247 mt = merged_table_from_records(refs, &sources, &tables, sizes, bufs, 2);
248 merged_table_init_iter(mt, &it, REFTABLE_BLOCK_TYPE_REF);
249
250 for (size_t i = 0; i < 5; i++) {
251 int err = reftable_iterator_seek_ref(&it, "c");
252 cl_assert(!err);
253
254 cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
255 cl_assert_equal_i(reftable_ref_record_equal(&rec, &r1[1],
256 REFTABLE_HASH_SIZE_SHA1), 1);
257
258 cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
259 cl_assert_equal_i(reftable_ref_record_equal(&rec, &r2[1],
260 REFTABLE_HASH_SIZE_SHA1), 1);
261
262 cl_assert(reftable_iterator_next_ref(&it, &rec) > 0);
263 }
264
265 for (size_t i = 0; i < ARRAY_SIZE(bufs); i++)
266 reftable_buf_release(&bufs[i]);
267 tables_destroy(tables, ARRAY_SIZE(refs));
268 reftable_ref_record_release(&rec);
269 reftable_iterator_destroy(&it);
270 reftable_merged_table_free(mt);
271 reftable_free(sources);
272 }
273
274 void test_reftable_merged__seek_multiple_times_no_drain(void)
275 {
276 struct reftable_ref_record r1[] = {
277 {
278 .refname = (char *) "a",
279 .update_index = 1,
280 .value_type = REFTABLE_REF_VAL1,
281 .value.val1 = { 1 },
282 },
283 {
284 .refname = (char *) "c",
285 .update_index = 1,
286 .value_type = REFTABLE_REF_VAL1,
287 .value.val1 = { 2 },
288 }
289 };
290 struct reftable_ref_record r2[] = {
291 {
292 .refname = (char *) "b",
293 .update_index = 2,
294 .value_type = REFTABLE_REF_VAL1,
295 .value.val1 = { 3 },
296 },
297 {
298 .refname = (char *) "d",
299 .update_index = 2,
300 .value_type = REFTABLE_REF_VAL1,
301 .value.val1 = { 4 },
302 },
303 };
304 struct reftable_ref_record *refs[] = {
305 r1, r2,
306 };
307 size_t sizes[] = {
308 ARRAY_SIZE(r1), ARRAY_SIZE(r2),
309 };
310 struct reftable_buf bufs[] = {
311 REFTABLE_BUF_INIT, REFTABLE_BUF_INIT,
312 };
313 struct reftable_block_source *sources = NULL;
314 struct reftable_table **tables = NULL;
315 struct reftable_ref_record rec = { 0 };
316 struct reftable_iterator it = { 0 };
317 struct reftable_merged_table *mt;
318
319 mt = merged_table_from_records(refs, &sources, &tables, sizes, bufs, 2);
320 merged_table_init_iter(mt, &it, REFTABLE_BLOCK_TYPE_REF);
321
322 cl_assert(reftable_iterator_seek_ref(&it, "b") == 0);
323 cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
324 cl_assert_equal_i(reftable_ref_record_equal(&rec, &r2[0],
325 REFTABLE_HASH_SIZE_SHA1), 1);
326
327 cl_assert(reftable_iterator_seek_ref(&it, "a") == 0);
328 cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
329 cl_assert_equal_i(reftable_ref_record_equal(&rec, &r1[0],
330 REFTABLE_HASH_SIZE_SHA1), 1);
331
332 for (size_t i = 0; i < ARRAY_SIZE(bufs); i++)
333 reftable_buf_release(&bufs[i]);
334 tables_destroy(tables, ARRAY_SIZE(refs));
335 reftable_ref_record_release(&rec);
336 reftable_iterator_destroy(&it);
337 reftable_merged_table_free(mt);
338 reftable_free(sources);
339 }
340
341 static struct reftable_merged_table *
342 merged_table_from_log_records(struct reftable_log_record **logs,
343 struct reftable_block_source **source,
344 struct reftable_table ***tables, const size_t *sizes,
345 struct reftable_buf *buf, const size_t n)
346 {
347 struct reftable_merged_table *mt = NULL;
348 struct reftable_write_options opts = {
349 .block_size = 256,
350 .exact_log_message = 1,
351 };
352 int err;
353
354 REFTABLE_CALLOC_ARRAY(*tables, n);
355 cl_assert(*tables != NULL);
356 REFTABLE_CALLOC_ARRAY(*source, n);
357 cl_assert(*source != NULL);
358
359 for (size_t i = 0; i < n; i++) {
360 cl_reftable_write_to_buf(&buf[i], NULL, 0, logs[i], sizes[i], &opts);
361 block_source_from_buf(&(*source)[i], &buf[i]);
362
363 err = reftable_table_new(&(*tables)[i], &(*source)[i],
364 "name");
365 cl_assert(!err);
366 }
367
368 err = reftable_merged_table_new(&mt, *tables, n, REFTABLE_HASH_SHA1);
369 cl_assert(!err);
370 return mt;
371 }
372
373 void test_reftable_merged__logs(void)
374 {
375 struct reftable_log_record r1[] = {
376 {
377 .refname = (char *) "a",
378 .update_index = 2,
379 .value_type = REFTABLE_LOG_UPDATE,
380 .value.update = {
381 .old_hash = { 2 },
382 /* deletion */
383 .name = (char *) "jane doe",
384 .email = (char *) "jane@invalid",
385 .message = (char *) "message2",
386 }
387 },
388 {
389 .refname = (char *) "a",
390 .update_index = 1,
391 .value_type = REFTABLE_LOG_UPDATE,
392 .value.update = {
393 .old_hash = { 1 },
394 .new_hash = { 2 },
395 .name = (char *) "jane doe",
396 .email = (char *) "jane@invalid",
397 .message = (char *) "message1",
398 }
399 },
400 };
401 struct reftable_log_record r2[] = {
402 {
403 .refname = (char *) "a",
404 .update_index = 3,
405 .value_type = REFTABLE_LOG_UPDATE,
406 .value.update = {
407 .new_hash = { 3 },
408 .name = (char *) "jane doe",
409 .email = (char *) "jane@invalid",
410 .message = (char *) "message3",
411 }
412 },
413 };
414 struct reftable_log_record r3[] = {
415 {
416 .refname = (char *) "a",
417 .update_index = 2,
418 .value_type = REFTABLE_LOG_DELETION,
419 },
420 };
421 struct reftable_log_record *want[] = {
422 &r2[0],
423 &r3[0],
424 &r1[1],
425 };
426
427 struct reftable_log_record *logs[] = { r1, r2, r3 };
428 size_t sizes[3] = { ARRAY_SIZE(r1), ARRAY_SIZE(r2), ARRAY_SIZE(r3) };
429 struct reftable_buf bufs[3] = { REFTABLE_BUF_INIT, REFTABLE_BUF_INIT, REFTABLE_BUF_INIT };
430 struct reftable_block_source *bs = NULL;
431 struct reftable_table **tables = NULL;
432 struct reftable_merged_table *mt = merged_table_from_log_records(
433 logs, &bs, &tables, sizes, bufs, 3);
434 struct reftable_iterator it = { 0 };
435 struct reftable_log_record *out = NULL;
436 size_t len = 0;
437 size_t cap = 0;
438 size_t i;
439 int err;
440
441 err = merged_table_init_iter(mt, &it, REFTABLE_BLOCK_TYPE_LOG);
442 cl_assert(!err);
443 err = reftable_iterator_seek_log(&it, "a");
444 cl_assert(!err);
445 cl_assert_equal_i(reftable_merged_table_hash_id(mt), REFTABLE_HASH_SHA1);
446 cl_assert_equal_i(reftable_merged_table_min_update_index(mt), 1);
447 cl_assert_equal_i(reftable_merged_table_max_update_index(mt), 3);
448
449 while (len < 100) { /* cap loops/recursion. */
450 struct reftable_log_record log = { 0 };
451 int err = reftable_iterator_next_log(&it, &log);
452 if (err > 0)
453 break;
454
455 cl_assert(REFTABLE_ALLOC_GROW(out, len + 1, cap) == 0);
456 out[len++] = log;
457 }
458 reftable_iterator_destroy(&it);
459
460 cl_assert_equal_i(ARRAY_SIZE(want), len);
461 for (i = 0; i < len; i++)
462 cl_assert(reftable_log_record_equal(want[i], &out[i],
463 REFTABLE_HASH_SIZE_SHA1) != 0);
464
465 err = merged_table_init_iter(mt, &it, REFTABLE_BLOCK_TYPE_LOG);
466 cl_assert(!err);
467 err = reftable_iterator_seek_log_at(&it, "a", 2);
468 cl_assert(!err);
469 reftable_log_record_release(&out[0]);
470 cl_assert(reftable_iterator_next_log(&it, &out[0]) == 0);
471 cl_assert(reftable_log_record_equal(&out[0], &r3[0],
472 REFTABLE_HASH_SIZE_SHA1) != 0);
473 reftable_iterator_destroy(&it);
474
475 for (i = 0; i < len; i++)
476 reftable_log_record_release(&out[i]);
477 reftable_free(out);
478
479 for (i = 0; i < 3; i++)
480 reftable_buf_release(&bufs[i]);
481 tables_destroy(tables, 3);
482 reftable_merged_table_free(mt);
483 reftable_free(bs);
484 }
485
486 void test_reftable_merged__default_write_opts(void)
487 {
488 struct reftable_write_options opts = { 0 };
489 struct reftable_buf buf = REFTABLE_BUF_INIT;
490 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
491 struct reftable_ref_record rec = {
492 .refname = (char *) "master",
493 .update_index = 1,
494 };
495 int err;
496 struct reftable_block_source source = { 0 };
497 uint32_t hash_id;
498 struct reftable_table *table = NULL;
499 struct reftable_merged_table *merged = NULL;
500
501 reftable_writer_set_limits(w, 1, 1);
502
503 cl_assert_equal_i(reftable_writer_add_ref(w, &rec), 0);
504
505 cl_assert_equal_i(reftable_writer_close(w), 0);
506 reftable_writer_free(w);
507
508 block_source_from_buf(&source, &buf);
509
510 err = reftable_table_new(&table, &source, "filename");
511 cl_assert(!err);
512
513 hash_id = reftable_table_hash_id(table);
514 cl_assert_equal_i(hash_id, REFTABLE_HASH_SHA1);
515
516 err = reftable_merged_table_new(&merged, &table, 1, REFTABLE_HASH_SHA256);
517 cl_assert_equal_i(err, REFTABLE_FORMAT_ERROR);
518 err = reftable_merged_table_new(&merged, &table, 1, REFTABLE_HASH_SHA1);
519 cl_assert(!err);
520
521 reftable_table_decref(table);
522 reftable_merged_table_free(merged);
523 reftable_buf_release(&buf);
524 }