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