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