Git fork
1/*
2Copyright 2020 Google LLC
3
4Use of this source code is governed by a BSD-style
5license that can be found in the LICENSE file or at
6https://developers.google.com/open-source/licenses/bsd
7*/
8
9#define DISABLE_SIGN_COMPARE_WARNINGS
10
11#include "unit-test.h"
12#include "lib-reftable.h"
13#include "reftable/basics.h"
14#include "reftable/blocksource.h"
15#include "reftable/reftable-error.h"
16#include "reftable/reftable-writer.h"
17#include "reftable/table.h"
18#include "strbuf.h"
19
20static const int update_index = 5;
21
22void test_reftable_readwrite__buffer(void)
23{
24 struct reftable_buf buf = REFTABLE_BUF_INIT;
25 struct reftable_block_source source = { 0 };
26 struct reftable_block_data out = { 0 };
27 int n;
28 uint8_t in[] = "hello";
29 cl_assert_equal_i(reftable_buf_add(&buf, in, sizeof(in)), 0);
30 block_source_from_buf(&source, &buf);
31 cl_assert_equal_i(block_source_size(&source), 6);
32 n = block_source_read_data(&source, &out, 0, sizeof(in));
33 cl_assert_equal_i(n, sizeof(in));
34 cl_assert(!memcmp(in, out.data, n));
35 block_source_release_data(&out);
36
37 n = block_source_read_data(&source, &out, 1, 2);
38 cl_assert_equal_i(n, 2);
39 cl_assert(!memcmp(out.data, "el", 2));
40
41 block_source_release_data(&out);
42 block_source_close(&source);
43 reftable_buf_release(&buf);
44}
45
46static void write_table(char ***names, struct reftable_buf *buf, int N,
47 int block_size, enum reftable_hash hash_id)
48{
49 struct reftable_write_options opts = {
50 .block_size = block_size,
51 .hash_id = hash_id,
52 };
53 struct reftable_ref_record *refs;
54 struct reftable_log_record *logs;
55 int i;
56
57 REFTABLE_CALLOC_ARRAY(*names, N + 1);
58 cl_assert(*names != NULL);
59 REFTABLE_CALLOC_ARRAY(refs, N);
60 cl_assert(refs != NULL);
61 REFTABLE_CALLOC_ARRAY(logs, N);
62 cl_assert(logs != NULL);
63
64 for (i = 0; i < N; i++) {
65 refs[i].refname = (*names)[i] = xstrfmt("refs/heads/branch%02d", i);
66 refs[i].update_index = update_index;
67 refs[i].value_type = REFTABLE_REF_VAL1;
68 cl_reftable_set_hash(refs[i].value.val1, i,
69 REFTABLE_HASH_SHA1);
70 }
71
72 for (i = 0; i < N; i++) {
73 logs[i].refname = (*names)[i];
74 logs[i].update_index = update_index;
75 logs[i].value_type = REFTABLE_LOG_UPDATE;
76 cl_reftable_set_hash(logs[i].value.update.new_hash, i,
77 REFTABLE_HASH_SHA1);
78 logs[i].value.update.message = (char *) "message";
79 }
80
81 cl_reftable_write_to_buf(buf, refs, N, logs, N, &opts);
82
83 reftable_free(refs);
84 reftable_free(logs);
85}
86
87void test_reftable_readwrite__log_buffer_size(void)
88{
89 struct reftable_buf buf = REFTABLE_BUF_INIT;
90 struct reftable_write_options opts = {
91 .block_size = 4096,
92 };
93 int i;
94 struct reftable_log_record
95 log = { .refname = (char *) "refs/heads/master",
96 .update_index = update_index,
97 .value_type = REFTABLE_LOG_UPDATE,
98 .value = { .update = {
99 .name = (char *) "Han-Wen Nienhuys",
100 .email = (char *) "hanwen@google.com",
101 .tz_offset = 100,
102 .time = 0x5e430672,
103 .message = (char *) "commit: 9\n",
104 } } };
105 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
106 &opts);
107
108 /* This tests buffer extension for log compression. Must use a random
109 hash, to ensure that the compressed part is larger than the original.
110 */
111 for (i = 0; i < REFTABLE_HASH_SIZE_SHA1; i++) {
112 log.value.update.old_hash[i] = (uint8_t)(git_rand(0) % 256);
113 log.value.update.new_hash[i] = (uint8_t)(git_rand(0) % 256);
114 }
115 reftable_writer_set_limits(w, update_index, update_index);
116 cl_assert_equal_i(reftable_writer_add_log(w, &log), 0);
117 cl_assert_equal_i(reftable_writer_close(w), 0);
118 reftable_writer_free(w);
119 reftable_buf_release(&buf);
120}
121
122void test_reftable_readwrite__log_overflow(void)
123{
124 struct reftable_buf buf = REFTABLE_BUF_INIT;
125 char msg[256] = { 0 };
126 struct reftable_write_options opts = {
127 .block_size = ARRAY_SIZE(msg),
128 };
129 struct reftable_log_record log = {
130 .refname = (char *) "refs/heads/master",
131 .update_index = update_index,
132 .value_type = REFTABLE_LOG_UPDATE,
133 .value = {
134 .update = {
135 .old_hash = { 1 },
136 .new_hash = { 2 },
137 .name = (char *) "Han-Wen Nienhuys",
138 .email = (char *) "hanwen@google.com",
139 .tz_offset = 100,
140 .time = 0x5e430672,
141 .message = msg,
142 },
143 },
144 };
145 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
146 &opts);
147
148 memset(msg, 'x', sizeof(msg) - 1);
149 reftable_writer_set_limits(w, update_index, update_index);
150 cl_assert_equal_i(reftable_writer_add_log(w, &log), REFTABLE_ENTRY_TOO_BIG_ERROR);
151 reftable_writer_free(w);
152 reftable_buf_release(&buf);
153}
154
155void test_reftable_readwrite__log_write_limits(void)
156{
157 struct reftable_write_options opts = { 0 };
158 struct reftable_buf buf = REFTABLE_BUF_INIT;
159 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
160 &opts);
161 struct reftable_log_record log = {
162 .refname = (char *)"refs/head/master",
163 .update_index = 0,
164 .value_type = REFTABLE_LOG_UPDATE,
165 .value = {
166 .update = {
167 .old_hash = { 1 },
168 .new_hash = { 2 },
169 .name = (char *)"Han-Wen Nienhuys",
170 .email = (char *)"hanwen@google.com",
171 .tz_offset = 100,
172 .time = 0x5e430672,
173 },
174 },
175 };
176
177 reftable_writer_set_limits(w, 1, 1);
178
179 /* write with update_index (0) below set limits (1, 1) */
180 cl_assert_equal_i(reftable_writer_add_log(w, &log), 0);
181
182 /* write with update_index (1) in the set limits (1, 1) */
183 log.update_index = 1;
184 cl_assert_equal_i(reftable_writer_add_log(w, &log), 0);
185
186 /* write with update_index (3) above set limits (1, 1) */
187 log.update_index = 3;
188 cl_assert_equal_i(reftable_writer_add_log(w, &log), REFTABLE_API_ERROR);
189
190 reftable_writer_free(w);
191 reftable_buf_release(&buf);
192}
193
194void test_reftable_readwrite__log_write_read(void)
195{
196 struct reftable_write_options opts = {
197 .block_size = 256,
198 };
199 struct reftable_ref_record ref = { 0 };
200 struct reftable_log_record log = { 0 };
201 struct reftable_iterator it = { 0 };
202 struct reftable_table *table;
203 struct reftable_block_source source = { 0 };
204 struct reftable_buf buf = REFTABLE_BUF_INIT;
205 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
206 const struct reftable_stats *stats = NULL;
207 int N = 2, i;
208 char **names;
209 int err;
210
211 names = reftable_calloc(N + 1, sizeof(*names));
212 cl_assert(names != NULL);
213
214 reftable_writer_set_limits(w, 0, N);
215
216 for (i = 0; i < N; i++) {
217 char name[256];
218 struct reftable_ref_record ref = { 0 };
219 snprintf(name, sizeof(name), "b%02d%0*d", i, 130, 7);
220 names[i] = xstrdup(name);
221 ref.refname = name;
222 ref.update_index = i;
223
224 cl_assert_equal_i(reftable_writer_add_ref(w, &ref), 0);
225 }
226
227 for (i = 0; i < N; i++) {
228 struct reftable_log_record log = { 0 };
229
230 log.refname = names[i];
231 log.update_index = i;
232 log.value_type = REFTABLE_LOG_UPDATE;
233 cl_reftable_set_hash(log.value.update.old_hash, i,
234 REFTABLE_HASH_SHA1);
235 cl_reftable_set_hash(log.value.update.new_hash, i + 1,
236 REFTABLE_HASH_SHA1);
237
238 cl_assert_equal_i(reftable_writer_add_log(w, &log), 0);
239 }
240
241 cl_assert_equal_i(reftable_writer_close(w), 0);
242
243 stats = reftable_writer_stats(w);
244 cl_assert(stats->log_stats.blocks > 0);
245 reftable_writer_free(w);
246 w = NULL;
247
248 block_source_from_buf(&source, &buf);
249
250 err = reftable_table_new(&table, &source, "file.log");
251 cl_assert(!err);
252
253 err = reftable_table_init_ref_iterator(table, &it);
254 cl_assert(!err);
255
256 err = reftable_iterator_seek_ref(&it, names[N - 1]);
257 cl_assert(!err);
258
259 err = reftable_iterator_next_ref(&it, &ref);
260 cl_assert(!err);
261
262 /* end of iteration. */
263 cl_assert(reftable_iterator_next_ref(&it, &ref) > 0);
264
265 reftable_iterator_destroy(&it);
266 reftable_ref_record_release(&ref);
267
268 err = reftable_table_init_log_iterator(table, &it);
269 cl_assert(!err);
270 err = reftable_iterator_seek_log(&it, "");
271 cl_assert(!err);
272
273 for (i = 0; ; i++) {
274 int err = reftable_iterator_next_log(&it, &log);
275 if (err > 0)
276 break;
277 cl_assert(!err);
278 cl_assert_equal_s(names[i], log.refname);
279 cl_assert_equal_i(i, log.update_index);
280 reftable_log_record_release(&log);
281 }
282
283 cl_assert_equal_i(i, N);
284 reftable_iterator_destroy(&it);
285
286 /* cleanup. */
287 reftable_buf_release(&buf);
288 free_names(names);
289 reftable_table_decref(table);
290}
291
292void test_reftable_readwrite__log_zlib_corruption(void)
293{
294 struct reftable_write_options opts = {
295 .block_size = 256,
296 };
297 struct reftable_iterator it = { 0 };
298 struct reftable_table *table;
299 struct reftable_block_source source = { 0 };
300 struct reftable_buf buf = REFTABLE_BUF_INIT;
301 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
302 &opts);
303 const struct reftable_stats *stats = NULL;
304 char message[100] = { 0 };
305 int i;
306 int err;
307 struct reftable_log_record log = {
308 .refname = (char *) "refname",
309 .value_type = REFTABLE_LOG_UPDATE,
310 .value = {
311 .update = {
312 .new_hash = { 1 },
313 .old_hash = { 2 },
314 .name = (char *) "My Name",
315 .email = (char *) "myname@invalid",
316 .message = message,
317 },
318 },
319 };
320
321 for (i = 0; i < sizeof(message) - 1; i++)
322 message[i] = (uint8_t)(git_rand(0) % 64 + ' ');
323
324 reftable_writer_set_limits(w, 1, 1);
325
326 cl_assert_equal_i(reftable_writer_add_log(w, &log), 0);
327 cl_assert_equal_i(reftable_writer_close(w), 0);
328
329 stats = reftable_writer_stats(w);
330 cl_assert(stats->log_stats.blocks > 0);
331 reftable_writer_free(w);
332 w = NULL;
333
334 /* corrupt the data. */
335 buf.buf[50] ^= 0x99;
336
337 block_source_from_buf(&source, &buf);
338
339 err = reftable_table_new(&table, &source, "file.log");
340 cl_assert(!err);
341
342 err = reftable_table_init_log_iterator(table, &it);
343 cl_assert(!err);
344 err = reftable_iterator_seek_log(&it, "refname");
345 cl_assert_equal_i(err, REFTABLE_ZLIB_ERROR);
346
347 reftable_iterator_destroy(&it);
348
349 /* cleanup. */
350 reftable_table_decref(table);
351 reftable_buf_release(&buf);
352}
353
354void test_reftable_readwrite__table_read_write_sequential(void)
355{
356 char **names;
357 struct reftable_buf buf = REFTABLE_BUF_INIT;
358 int N = 50;
359 struct reftable_iterator it = { 0 };
360 struct reftable_block_source source = { 0 };
361 struct reftable_table *table;
362 int err = 0;
363 int j = 0;
364
365 write_table(&names, &buf, N, 256, REFTABLE_HASH_SHA1);
366
367 block_source_from_buf(&source, &buf);
368
369 err = reftable_table_new(&table, &source, "file.ref");
370 cl_assert(!err);
371
372 err = reftable_table_init_ref_iterator(table, &it);
373 cl_assert(!err);
374 err = reftable_iterator_seek_ref(&it, "");
375 cl_assert(!err);
376
377 for (j = 0; ; j++) {
378 struct reftable_ref_record ref = { 0 };
379 int r = reftable_iterator_next_ref(&it, &ref);
380 cl_assert(r >= 0);
381 if (r > 0)
382 break;
383 cl_assert_equal_s(names[j], ref.refname);
384 cl_assert_equal_i(update_index, ref.update_index);
385 reftable_ref_record_release(&ref);
386 }
387 cl_assert_equal_i(j, N);
388
389 reftable_iterator_destroy(&it);
390 reftable_table_decref(table);
391 reftable_buf_release(&buf);
392 free_names(names);
393}
394
395void test_reftable_readwrite__table_write_small_table(void)
396{
397 char **names;
398 struct reftable_buf buf = REFTABLE_BUF_INIT;
399 int N = 1;
400 write_table(&names, &buf, N, 4096, REFTABLE_HASH_SHA1);
401 cl_assert(buf.len < 200);
402 reftable_buf_release(&buf);
403 free_names(names);
404}
405
406void test_reftable_readwrite__table_read_api(void)
407{
408 char **names;
409 struct reftable_buf buf = REFTABLE_BUF_INIT;
410 int N = 50;
411 struct reftable_table *table;
412 struct reftable_block_source source = { 0 };
413 struct reftable_log_record log = { 0 };
414 struct reftable_iterator it = { 0 };
415 int err;
416
417 write_table(&names, &buf, N, 256, REFTABLE_HASH_SHA1);
418
419 block_source_from_buf(&source, &buf);
420
421 err = reftable_table_new(&table, &source, "file.ref");
422 cl_assert(!err);
423
424 err = reftable_table_init_ref_iterator(table, &it);
425 cl_assert(!err);
426 err = reftable_iterator_seek_ref(&it, names[0]);
427 cl_assert(!err);
428
429 err = reftable_iterator_next_log(&it, &log);
430 cl_assert_equal_i(err, REFTABLE_API_ERROR);
431
432 reftable_buf_release(&buf);
433 free_names(names);
434 reftable_iterator_destroy(&it);
435 reftable_table_decref(table);
436 reftable_buf_release(&buf);
437}
438
439static void t_table_read_write_seek(int index, enum reftable_hash hash_id)
440{
441 char **names;
442 struct reftable_buf buf = REFTABLE_BUF_INIT;
443 int N = 50;
444 struct reftable_table *table;
445 struct reftable_block_source source = { 0 };
446 int err;
447 int i = 0;
448
449 struct reftable_iterator it = { 0 };
450 struct reftable_buf pastLast = REFTABLE_BUF_INIT;
451 struct reftable_ref_record ref = { 0 };
452
453 write_table(&names, &buf, N, 256, hash_id);
454
455 block_source_from_buf(&source, &buf);
456
457 err = reftable_table_new(&table, &source, "file.ref");
458 cl_assert(!err);
459 cl_assert_equal_i(hash_id, reftable_table_hash_id(table));
460
461 if (!index) {
462 table->ref_offsets.index_offset = 0;
463 } else {
464 cl_assert(table->ref_offsets.index_offset > 0);
465 }
466
467 for (i = 1; i < N; i++) {
468 err = reftable_table_init_ref_iterator(table, &it);
469 cl_assert(!err);
470 err = reftable_iterator_seek_ref(&it, names[i]);
471 cl_assert(!err);
472 err = reftable_iterator_next_ref(&it, &ref);
473 cl_assert(!err);
474 cl_assert_equal_s(names[i], ref.refname);
475 cl_assert_equal_i(REFTABLE_REF_VAL1, ref.value_type);
476 cl_assert_equal_i(i, ref.value.val1[0]);
477
478 reftable_ref_record_release(&ref);
479 reftable_iterator_destroy(&it);
480 }
481
482 cl_assert_equal_i(reftable_buf_addstr(&pastLast, names[N - 1]),
483 0);
484 cl_assert_equal_i(reftable_buf_addstr(&pastLast, "/"), 0);
485
486 err = reftable_table_init_ref_iterator(table, &it);
487 cl_assert(!err);
488 err = reftable_iterator_seek_ref(&it, pastLast.buf);
489 if (err == 0) {
490 struct reftable_ref_record ref = { 0 };
491 int err = reftable_iterator_next_ref(&it, &ref);
492 cl_assert(err > 0);
493 } else {
494 cl_assert(err > 0);
495 }
496
497 reftable_buf_release(&pastLast);
498 reftable_iterator_destroy(&it);
499
500 reftable_buf_release(&buf);
501 free_names(names);
502 reftable_table_decref(table);
503}
504
505void test_reftable_readwrite__table_read_write_seek_linear(void)
506{
507 t_table_read_write_seek(0, REFTABLE_HASH_SHA1);
508}
509
510void test_reftable_readwrite__table_read_write_seek_linear_sha256(void)
511{
512 t_table_read_write_seek(0, REFTABLE_HASH_SHA256);
513}
514
515void test_reftable_readwrite__table_read_write_seek_index(void)
516{
517 t_table_read_write_seek(1, REFTABLE_HASH_SHA1);
518}
519
520static void t_table_refs_for(int indexed)
521{
522 char **want_names;
523 int want_names_len = 0;
524 uint8_t want_hash[REFTABLE_HASH_SIZE_SHA1];
525
526 struct reftable_write_options opts = {
527 .block_size = 256,
528 };
529 struct reftable_ref_record ref = { 0 };
530 struct reftable_table *table;
531 struct reftable_block_source source = { 0 };
532 struct reftable_buf buf = REFTABLE_BUF_INIT;
533 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf,
534 &opts);
535 struct reftable_iterator it = { 0 };
536 int N = 50, j, i;
537 int err;
538
539 want_names = reftable_calloc(N + 1, sizeof(*want_names));
540 cl_assert(want_names != NULL);
541
542 cl_reftable_set_hash(want_hash, 4, REFTABLE_HASH_SHA1);
543
544 for (i = 0; i < N; i++) {
545 uint8_t hash[REFTABLE_HASH_SIZE_SHA1];
546 char fill[51] = { 0 };
547 char name[100];
548 struct reftable_ref_record ref = { 0 };
549
550 memset(hash, i, sizeof(hash));
551 memset(fill, 'x', 50);
552 /* Put the variable part in the start */
553 snprintf(name, sizeof(name), "br%02d%s", i, fill);
554 name[40] = 0;
555 ref.refname = name;
556
557 ref.value_type = REFTABLE_REF_VAL2;
558 cl_reftable_set_hash(ref.value.val2.value, i / 4,
559 REFTABLE_HASH_SHA1);
560 cl_reftable_set_hash(ref.value.val2.target_value,
561 3 + i / 4, REFTABLE_HASH_SHA1);
562
563 /* 80 bytes / entry, so 3 entries per block. Yields 17
564 */
565 /* blocks. */
566 cl_assert_equal_i(reftable_writer_add_ref(w, &ref), 0);
567
568 if (!memcmp(ref.value.val2.value, want_hash, REFTABLE_HASH_SIZE_SHA1) ||
569 !memcmp(ref.value.val2.target_value, want_hash, REFTABLE_HASH_SIZE_SHA1))
570 want_names[want_names_len++] = xstrdup(name);
571 }
572
573 cl_assert_equal_i(reftable_writer_close(w), 0);
574
575 reftable_writer_free(w);
576 w = NULL;
577
578 block_source_from_buf(&source, &buf);
579
580 err = reftable_table_new(&table, &source, "file.ref");
581 cl_assert(!err);
582 if (!indexed)
583 table->obj_offsets.is_present = 0;
584
585 err = reftable_table_init_ref_iterator(table, &it);
586 cl_assert(!err);
587 err = reftable_iterator_seek_ref(&it, "");
588 cl_assert(!err);
589 reftable_iterator_destroy(&it);
590
591 err = reftable_table_refs_for(table, &it, want_hash);
592 cl_assert(!err);
593
594 for (j = 0; ; j++) {
595 int err = reftable_iterator_next_ref(&it, &ref);
596 cl_assert(err >= 0);
597 if (err > 0)
598 break;
599 cl_assert(j < want_names_len);
600 cl_assert_equal_s(ref.refname, want_names[j]);
601 reftable_ref_record_release(&ref);
602 }
603 cl_assert_equal_i(j, want_names_len);
604
605 reftable_buf_release(&buf);
606 free_names(want_names);
607 reftable_iterator_destroy(&it);
608 reftable_table_decref(table);
609}
610
611void test_reftable_readwrite__table_refs_for_no_index(void)
612{
613 t_table_refs_for(0);
614}
615
616void test_reftable_readwrite__table_refs_for_obj_index(void)
617{
618 t_table_refs_for(1);
619}
620
621void test_reftable_readwrite__write_empty_table(void)
622{
623 struct reftable_write_options opts = { 0 };
624 struct reftable_buf buf = REFTABLE_BUF_INIT;
625 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
626 struct reftable_block_source source = { 0 };
627 struct reftable_table *table = NULL;
628 struct reftable_ref_record rec = { 0 };
629 struct reftable_iterator it = { 0 };
630 int err;
631
632 reftable_writer_set_limits(w, 1, 1);
633
634 cl_assert_equal_i(reftable_writer_close(w), REFTABLE_EMPTY_TABLE_ERROR);
635 reftable_writer_free(w);
636
637 cl_assert_equal_i(buf.len, header_size(1) + footer_size(1));
638
639 block_source_from_buf(&source, &buf);
640
641 err = reftable_table_new(&table, &source, "filename");
642 cl_assert(!err);
643
644 err = reftable_table_init_ref_iterator(table, &it);
645 cl_assert(!err);
646 err = reftable_iterator_seek_ref(&it, "");
647 cl_assert(!err);
648
649 err = reftable_iterator_next_ref(&it, &rec);
650 cl_assert(err > 0);
651
652 reftable_iterator_destroy(&it);
653 reftable_table_decref(table);
654 reftable_buf_release(&buf);
655}
656
657void test_reftable_readwrite__write_object_id_min_length(void)
658{
659 struct reftable_write_options opts = {
660 .block_size = 75,
661 };
662 struct reftable_buf buf = REFTABLE_BUF_INIT;
663 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
664 struct reftable_ref_record ref = {
665 .update_index = 1,
666 .value_type = REFTABLE_REF_VAL1,
667 .value.val1 = {42},
668 };
669 int i;
670
671 reftable_writer_set_limits(w, 1, 1);
672
673 /* Write the same hash in many refs. If there is only 1 hash, the
674 * disambiguating prefix is length 0 */
675 for (i = 0; i < 256; i++) {
676 char name[256];
677 snprintf(name, sizeof(name), "ref%05d", i);
678 ref.refname = name;
679 cl_assert_equal_i(reftable_writer_add_ref(w, &ref), 0);
680 }
681
682 cl_assert_equal_i(reftable_writer_close(w), 0);
683 cl_assert_equal_i(reftable_writer_stats(w)->object_id_len, 2);
684 reftable_writer_free(w);
685 reftable_buf_release(&buf);
686}
687
688void test_reftable_readwrite__write_object_id_length(void)
689{
690 struct reftable_write_options opts = {
691 .block_size = 75,
692 };
693 struct reftable_buf buf = REFTABLE_BUF_INIT;
694 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
695 struct reftable_ref_record ref = {
696 .update_index = 1,
697 .value_type = REFTABLE_REF_VAL1,
698 .value.val1 = {42},
699 };
700 int i;
701
702 reftable_writer_set_limits(w, 1, 1);
703
704 /* Write the same hash in many refs. If there is only 1 hash, the
705 * disambiguating prefix is length 0 */
706 for (i = 0; i < 256; i++) {
707 char name[256];
708 snprintf(name, sizeof(name), "ref%05d", i);
709 ref.refname = name;
710 ref.value.val1[15] = i;
711 cl_assert(reftable_writer_add_ref(w, &ref) == 0);
712 }
713
714 cl_assert_equal_i(reftable_writer_close(w), 0);
715 cl_assert_equal_i(reftable_writer_stats(w)->object_id_len, 16);
716 reftable_writer_free(w);
717 reftable_buf_release(&buf);
718}
719
720void test_reftable_readwrite__write_empty_key(void)
721{
722 struct reftable_write_options opts = { 0 };
723 struct reftable_buf buf = REFTABLE_BUF_INIT;
724 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
725 struct reftable_ref_record ref = {
726 .refname = (char *) "",
727 .update_index = 1,
728 .value_type = REFTABLE_REF_DELETION,
729 };
730
731 reftable_writer_set_limits(w, 1, 1);
732 cl_assert_equal_i(reftable_writer_add_ref(w, &ref), REFTABLE_API_ERROR);
733 cl_assert_equal_i(reftable_writer_close(w),
734 REFTABLE_EMPTY_TABLE_ERROR);
735 reftable_writer_free(w);
736 reftable_buf_release(&buf);
737}
738
739void test_reftable_readwrite__write_key_order(void)
740{
741 struct reftable_write_options opts = { 0 };
742 struct reftable_buf buf = REFTABLE_BUF_INIT;
743 struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
744 struct reftable_ref_record refs[2] = {
745 {
746 .refname = (char *) "b",
747 .update_index = 1,
748 .value_type = REFTABLE_REF_SYMREF,
749 .value = {
750 .symref = (char *) "target",
751 },
752 }, {
753 .refname = (char *) "a",
754 .update_index = 1,
755 .value_type = REFTABLE_REF_SYMREF,
756 .value = {
757 .symref = (char *) "target",
758 },
759 }
760 };
761
762 reftable_writer_set_limits(w, 1, 1);
763 cl_assert_equal_i(reftable_writer_add_ref(w, &refs[0]), 0);
764 cl_assert_equal_i(reftable_writer_add_ref(w, &refs[1]),
765 REFTABLE_API_ERROR);
766
767 refs[0].update_index = 2;
768 cl_assert_equal_i(reftable_writer_add_ref(w, &refs[0]), REFTABLE_API_ERROR);
769
770 reftable_writer_close(w);
771 reftable_writer_free(w);
772 reftable_buf_release(&buf);
773}
774
775void test_reftable_readwrite__write_multiple_indices(void)
776{
777 struct reftable_write_options opts = {
778 .block_size = 100,
779 };
780 struct reftable_buf writer_buf = REFTABLE_BUF_INIT;
781 struct reftable_block_source source = { 0 };
782 struct reftable_iterator it = { 0 };
783 const struct reftable_stats *stats;
784 struct reftable_writer *writer;
785 struct reftable_table *table;
786 char buf[128];
787 int i;
788 int err;
789
790 writer = cl_reftable_strbuf_writer(&writer_buf, &opts);
791 reftable_writer_set_limits(writer, 1, 1);
792 for (i = 0; i < 100; i++) {
793 struct reftable_ref_record ref = {
794 .update_index = 1,
795 .value_type = REFTABLE_REF_VAL1,
796 .value.val1 = {i},
797 };
798
799 snprintf(buf, sizeof(buf), "refs/heads/%04d", i);
800 ref.refname = buf;
801
802 cl_assert_equal_i(reftable_writer_add_ref(writer, &ref), 0);
803 }
804
805 for (i = 0; i < 100; i++) {
806 struct reftable_log_record log = {
807 .update_index = 1,
808 .value_type = REFTABLE_LOG_UPDATE,
809 .value.update = {
810 .old_hash = { i },
811 .new_hash = { i },
812 },
813 };
814
815 snprintf(buf, sizeof(buf), "refs/heads/%04d", i);
816 log.refname = buf;
817
818 cl_assert_equal_i(reftable_writer_add_log(writer, &log), 0);
819 }
820
821 reftable_writer_close(writer);
822
823 /*
824 * The written data should be sufficiently large to result in indices
825 * for each of the block types.
826 */
827 stats = reftable_writer_stats(writer);
828 cl_assert(stats->ref_stats.index_offset > 0);
829 cl_assert(stats->obj_stats.index_offset > 0);
830 cl_assert(stats->log_stats.index_offset > 0);
831
832 block_source_from_buf(&source, &writer_buf);
833 err = reftable_table_new(&table, &source, "filename");
834 cl_assert(!err);
835
836 /*
837 * Seeking the log uses the log index now. In case there is any
838 * confusion regarding indices we would notice here.
839 */
840 err = reftable_table_init_log_iterator(table, &it);
841 cl_assert(!err);
842 err = reftable_iterator_seek_log(&it, "");
843 cl_assert(!err);
844
845 reftable_iterator_destroy(&it);
846 reftable_writer_free(writer);
847 reftable_table_decref(table);
848 reftable_buf_release(&writer_buf);
849}
850
851void test_reftable_readwrite__write_multi_level_index(void)
852{
853 struct reftable_write_options opts = {
854 .block_size = 100,
855 };
856 struct reftable_buf writer_buf = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
857 struct reftable_block_source source = { 0 };
858 struct reftable_iterator it = { 0 };
859 const struct reftable_stats *stats;
860 struct reftable_writer *writer;
861 struct reftable_table *table;
862 int err;
863
864 writer = cl_reftable_strbuf_writer(&writer_buf, &opts);
865 reftable_writer_set_limits(writer, 1, 1);
866 for (size_t i = 0; i < 200; i++) {
867 struct reftable_ref_record ref = {
868 .update_index = 1,
869 .value_type = REFTABLE_REF_VAL1,
870 .value.val1 = {i},
871 };
872 char buf[128];
873
874 snprintf(buf, sizeof(buf), "refs/heads/%03" PRIuMAX, (uintmax_t)i);
875 ref.refname = buf;
876
877 cl_assert_equal_i(reftable_writer_add_ref(writer, &ref), 0);
878 }
879 reftable_writer_close(writer);
880
881 /*
882 * The written refs should be sufficiently large to result in a
883 * multi-level index.
884 */
885 stats = reftable_writer_stats(writer);
886 cl_assert_equal_i(stats->ref_stats.max_index_level, 2);
887
888 block_source_from_buf(&source, &writer_buf);
889 err = reftable_table_new(&table, &source, "filename");
890 cl_assert(!err);
891
892 /*
893 * Seeking the last ref should work as expected.
894 */
895 err = reftable_table_init_ref_iterator(table, &it);
896 cl_assert(!err);
897 err = reftable_iterator_seek_ref(&it, "refs/heads/199");
898 cl_assert(!err);
899
900 reftable_iterator_destroy(&it);
901 reftable_writer_free(writer);
902 reftable_table_decref(table);
903 reftable_buf_release(&writer_buf);
904 reftable_buf_release(&buf);
905}
906
907void test_reftable_readwrite__corrupt_table_empty(void)
908{
909 struct reftable_buf buf = REFTABLE_BUF_INIT;
910 struct reftable_block_source source = { 0 };
911 struct reftable_table *table;
912 int err;
913
914 block_source_from_buf(&source, &buf);
915 err = reftable_table_new(&table, &source, "file.log");
916 cl_assert_equal_i(err, REFTABLE_FORMAT_ERROR);
917}
918
919void test_reftable_readwrite__corrupt_table(void)
920{
921 uint8_t zeros[1024] = { 0 };
922 struct reftable_buf buf = REFTABLE_BUF_INIT;
923 struct reftable_block_source source = { 0 };
924 struct reftable_table *table;
925 int err;
926
927 cl_assert(!reftable_buf_add(&buf, zeros, sizeof(zeros)));
928
929 block_source_from_buf(&source, &buf);
930 err = reftable_table_new(&table, &source, "file.log");
931 cl_assert_equal_i(err, REFTABLE_FORMAT_ERROR);
932
933 reftable_buf_release(&buf);
934}