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