]> git.ipfire.org Git - thirdparty/git.git/blob - reftable/stack_test.c
Merge branch 'rs/imap-send-use-xsnprintf'
[thirdparty/git.git] / reftable / stack_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 "stack.h"
10
11 #include "system.h"
12
13 #include "reftable-reader.h"
14 #include "merged.h"
15 #include "basics.h"
16 #include "record.h"
17 #include "test_framework.h"
18 #include "reftable-tests.h"
19 #include "reader.h"
20
21 #include <sys/types.h>
22 #include <dirent.h>
23
24 static void clear_dir(const char *dirname)
25 {
26 struct strbuf path = STRBUF_INIT;
27 strbuf_addstr(&path, dirname);
28 remove_dir_recursively(&path, 0);
29 strbuf_release(&path);
30 }
31
32 static int count_dir_entries(const char *dirname)
33 {
34 DIR *dir = opendir(dirname);
35 int len = 0;
36 struct dirent *d;
37 if (!dir)
38 return 0;
39
40 while ((d = readdir(dir))) {
41 /*
42 * Besides skipping over "." and "..", we also need to
43 * skip over other files that have a leading ".". This
44 * is due to behaviour of NFS, which will rename files
45 * to ".nfs*" to emulate delete-on-last-close.
46 *
47 * In any case this should be fine as the reftable
48 * library will never write files with leading dots
49 * anyway.
50 */
51 if (starts_with(d->d_name, "."))
52 continue;
53 len++;
54 }
55 closedir(dir);
56 return len;
57 }
58
59 /*
60 * Work linenumber into the tempdir, so we can see which tests forget to
61 * cleanup.
62 */
63 static char *get_tmp_template(int linenumber)
64 {
65 const char *tmp = getenv("TMPDIR");
66 static char template[1024];
67 snprintf(template, sizeof(template) - 1, "%s/stack_test-%d.XXXXXX",
68 tmp ? tmp : "/tmp", linenumber);
69 return template;
70 }
71
72 static char *get_tmp_dir(int linenumber)
73 {
74 char *dir = get_tmp_template(linenumber);
75 EXPECT(mkdtemp(dir));
76 return dir;
77 }
78
79 static void test_read_file(void)
80 {
81 char *fn = get_tmp_template(__LINE__);
82 int fd = mkstemp(fn);
83 char out[1024] = "line1\n\nline2\nline3";
84 int n, err;
85 char **names = NULL;
86 char *want[] = { "line1", "line2", "line3" };
87 int i = 0;
88
89 EXPECT(fd > 0);
90 n = write_in_full(fd, out, strlen(out));
91 EXPECT(n == strlen(out));
92 err = close(fd);
93 EXPECT(err >= 0);
94
95 err = read_lines(fn, &names);
96 EXPECT_ERR(err);
97
98 for (i = 0; names[i]; i++) {
99 EXPECT(0 == strcmp(want[i], names[i]));
100 }
101 free_names(names);
102 (void) remove(fn);
103 }
104
105 static void test_parse_names(void)
106 {
107 char buf[] = "line\n";
108 char **names = NULL;
109 parse_names(buf, strlen(buf), &names);
110
111 EXPECT(NULL != names[0]);
112 EXPECT(0 == strcmp(names[0], "line"));
113 EXPECT(NULL == names[1]);
114 free_names(names);
115 }
116
117 static void test_names_equal(void)
118 {
119 char *a[] = { "a", "b", "c", NULL };
120 char *b[] = { "a", "b", "d", NULL };
121 char *c[] = { "a", "b", NULL };
122
123 EXPECT(names_equal(a, a));
124 EXPECT(!names_equal(a, b));
125 EXPECT(!names_equal(a, c));
126 }
127
128 static int write_test_ref(struct reftable_writer *wr, void *arg)
129 {
130 struct reftable_ref_record *ref = arg;
131 reftable_writer_set_limits(wr, ref->update_index, ref->update_index);
132 return reftable_writer_add_ref(wr, ref);
133 }
134
135 struct write_log_arg {
136 struct reftable_log_record *log;
137 uint64_t update_index;
138 };
139
140 static int write_test_log(struct reftable_writer *wr, void *arg)
141 {
142 struct write_log_arg *wla = arg;
143
144 reftable_writer_set_limits(wr, wla->update_index, wla->update_index);
145 return reftable_writer_add_log(wr, wla->log);
146 }
147
148 static void test_reftable_stack_add_one(void)
149 {
150 char *dir = get_tmp_dir(__LINE__);
151 struct strbuf scratch = STRBUF_INIT;
152 int mask = umask(002);
153 struct reftable_write_options cfg = {
154 .default_permissions = 0660,
155 };
156 struct reftable_stack *st = NULL;
157 int err;
158 struct reftable_ref_record ref = {
159 .refname = "HEAD",
160 .update_index = 1,
161 .value_type = REFTABLE_REF_SYMREF,
162 .value.symref = "master",
163 };
164 struct reftable_ref_record dest = { NULL };
165 struct stat stat_result = { 0 };
166 err = reftable_new_stack(&st, dir, cfg);
167 EXPECT_ERR(err);
168
169 err = reftable_stack_add(st, &write_test_ref, &ref);
170 EXPECT_ERR(err);
171
172 err = reftable_stack_read_ref(st, ref.refname, &dest);
173 EXPECT_ERR(err);
174 EXPECT(0 == strcmp("master", dest.value.symref));
175 EXPECT(st->readers_len > 0);
176
177 printf("testing print functionality:\n");
178 err = reftable_stack_print_directory(dir, GIT_SHA1_FORMAT_ID);
179 EXPECT_ERR(err);
180
181 err = reftable_stack_print_directory(dir, GIT_SHA256_FORMAT_ID);
182 EXPECT(err == REFTABLE_FORMAT_ERROR);
183
184 #ifndef GIT_WINDOWS_NATIVE
185 strbuf_addstr(&scratch, dir);
186 strbuf_addstr(&scratch, "/tables.list");
187 err = stat(scratch.buf, &stat_result);
188 EXPECT(!err);
189 EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
190
191 strbuf_reset(&scratch);
192 strbuf_addstr(&scratch, dir);
193 strbuf_addstr(&scratch, "/");
194 /* do not try at home; not an external API for reftable. */
195 strbuf_addstr(&scratch, st->readers[0]->name);
196 err = stat(scratch.buf, &stat_result);
197 EXPECT(!err);
198 EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
199 #else
200 (void) stat_result;
201 #endif
202
203 reftable_ref_record_release(&dest);
204 reftable_stack_destroy(st);
205 strbuf_release(&scratch);
206 clear_dir(dir);
207 umask(mask);
208 }
209
210 static void test_reftable_stack_uptodate(void)
211 {
212 struct reftable_write_options cfg = { 0 };
213 struct reftable_stack *st1 = NULL;
214 struct reftable_stack *st2 = NULL;
215 char *dir = get_tmp_dir(__LINE__);
216
217 int err;
218 struct reftable_ref_record ref1 = {
219 .refname = "HEAD",
220 .update_index = 1,
221 .value_type = REFTABLE_REF_SYMREF,
222 .value.symref = "master",
223 };
224 struct reftable_ref_record ref2 = {
225 .refname = "branch2",
226 .update_index = 2,
227 .value_type = REFTABLE_REF_SYMREF,
228 .value.symref = "master",
229 };
230
231
232 /* simulate multi-process access to the same stack
233 by creating two stacks for the same directory.
234 */
235 err = reftable_new_stack(&st1, dir, cfg);
236 EXPECT_ERR(err);
237
238 err = reftable_new_stack(&st2, dir, cfg);
239 EXPECT_ERR(err);
240
241 err = reftable_stack_add(st1, &write_test_ref, &ref1);
242 EXPECT_ERR(err);
243
244 err = reftable_stack_add(st2, &write_test_ref, &ref2);
245 EXPECT(err == REFTABLE_OUTDATED_ERROR);
246
247 err = reftable_stack_reload(st2);
248 EXPECT_ERR(err);
249
250 err = reftable_stack_add(st2, &write_test_ref, &ref2);
251 EXPECT_ERR(err);
252 reftable_stack_destroy(st1);
253 reftable_stack_destroy(st2);
254 clear_dir(dir);
255 }
256
257 static void test_reftable_stack_transaction_api(void)
258 {
259 char *dir = get_tmp_dir(__LINE__);
260
261 struct reftable_write_options cfg = { 0 };
262 struct reftable_stack *st = NULL;
263 int err;
264 struct reftable_addition *add = NULL;
265
266 struct reftable_ref_record ref = {
267 .refname = "HEAD",
268 .update_index = 1,
269 .value_type = REFTABLE_REF_SYMREF,
270 .value.symref = "master",
271 };
272 struct reftable_ref_record dest = { NULL };
273
274
275 err = reftable_new_stack(&st, dir, cfg);
276 EXPECT_ERR(err);
277
278 reftable_addition_destroy(add);
279
280 err = reftable_stack_new_addition(&add, st);
281 EXPECT_ERR(err);
282
283 err = reftable_addition_add(add, &write_test_ref, &ref);
284 EXPECT_ERR(err);
285
286 err = reftable_addition_commit(add);
287 EXPECT_ERR(err);
288
289 reftable_addition_destroy(add);
290
291 err = reftable_stack_read_ref(st, ref.refname, &dest);
292 EXPECT_ERR(err);
293 EXPECT(REFTABLE_REF_SYMREF == dest.value_type);
294 EXPECT(0 == strcmp("master", dest.value.symref));
295
296 reftable_ref_record_release(&dest);
297 reftable_stack_destroy(st);
298 clear_dir(dir);
299 }
300
301 static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
302 {
303 char *dir = get_tmp_dir(__LINE__);
304 struct reftable_write_options cfg = {0};
305 struct reftable_addition *add = NULL;
306 struct reftable_stack *st = NULL;
307 int i, n = 20, err;
308
309 err = reftable_new_stack(&st, dir, cfg);
310 EXPECT_ERR(err);
311
312 for (i = 0; i <= n; i++) {
313 struct reftable_ref_record ref = {
314 .update_index = reftable_stack_next_update_index(st),
315 .value_type = REFTABLE_REF_SYMREF,
316 .value.symref = "master",
317 };
318 char name[100];
319
320 snprintf(name, sizeof(name), "branch%04d", i);
321 ref.refname = name;
322
323 /*
324 * Disable auto-compaction for all but the last runs. Like this
325 * we can ensure that we indeed honor this setting and have
326 * better control over when exactly auto compaction runs.
327 */
328 st->disable_auto_compact = i != n;
329
330 err = reftable_stack_new_addition(&add, st);
331 EXPECT_ERR(err);
332
333 err = reftable_addition_add(add, &write_test_ref, &ref);
334 EXPECT_ERR(err);
335
336 err = reftable_addition_commit(add);
337 EXPECT_ERR(err);
338
339 reftable_addition_destroy(add);
340
341 /*
342 * The stack length should grow continuously for all runs where
343 * auto compaction is disabled. When enabled, we should merge
344 * all tables in the stack.
345 */
346 if (i != n)
347 EXPECT(st->merged->stack_len == i + 1);
348 else
349 EXPECT(st->merged->stack_len == 1);
350 }
351
352 reftable_stack_destroy(st);
353 clear_dir(dir);
354 }
355
356 static void test_reftable_stack_auto_compaction_fails_gracefully(void)
357 {
358 struct reftable_ref_record ref = {
359 .refname = "refs/heads/master",
360 .update_index = 1,
361 .value_type = REFTABLE_REF_VAL1,
362 .value.val1 = {0x01},
363 };
364 struct reftable_write_options cfg = {0};
365 struct reftable_stack *st;
366 struct strbuf table_path = STRBUF_INIT;
367 char *dir = get_tmp_dir(__LINE__);
368 int err;
369
370 err = reftable_new_stack(&st, dir, cfg);
371 EXPECT_ERR(err);
372
373 err = reftable_stack_add(st, write_test_ref, &ref);
374 EXPECT_ERR(err);
375 EXPECT(st->merged->stack_len == 1);
376 EXPECT(st->stats.attempts == 0);
377 EXPECT(st->stats.failures == 0);
378
379 /*
380 * Lock the newly written table such that it cannot be compacted.
381 * Adding a new table to the stack should not be impacted by this, even
382 * though auto-compaction will now fail.
383 */
384 strbuf_addf(&table_path, "%s/%s.lock", dir, st->readers[0]->name);
385 write_file_buf(table_path.buf, "", 0);
386
387 ref.update_index = 2;
388 err = reftable_stack_add(st, write_test_ref, &ref);
389 EXPECT_ERR(err);
390 EXPECT(st->merged->stack_len == 2);
391 EXPECT(st->stats.attempts == 1);
392 EXPECT(st->stats.failures == 1);
393
394 reftable_stack_destroy(st);
395 strbuf_release(&table_path);
396 clear_dir(dir);
397 }
398
399 static void test_reftable_stack_validate_refname(void)
400 {
401 struct reftable_write_options cfg = { 0 };
402 struct reftable_stack *st = NULL;
403 int err;
404 char *dir = get_tmp_dir(__LINE__);
405
406 int i;
407 struct reftable_ref_record ref = {
408 .refname = "a/b",
409 .update_index = 1,
410 .value_type = REFTABLE_REF_SYMREF,
411 .value.symref = "master",
412 };
413 char *additions[] = { "a", "a/b/c" };
414
415 err = reftable_new_stack(&st, dir, cfg);
416 EXPECT_ERR(err);
417
418 err = reftable_stack_add(st, &write_test_ref, &ref);
419 EXPECT_ERR(err);
420
421 for (i = 0; i < ARRAY_SIZE(additions); i++) {
422 struct reftable_ref_record ref = {
423 .refname = additions[i],
424 .update_index = 1,
425 .value_type = REFTABLE_REF_SYMREF,
426 .value.symref = "master",
427 };
428
429 err = reftable_stack_add(st, &write_test_ref, &ref);
430 EXPECT(err == REFTABLE_NAME_CONFLICT);
431 }
432
433 reftable_stack_destroy(st);
434 clear_dir(dir);
435 }
436
437 static int write_error(struct reftable_writer *wr, void *arg)
438 {
439 return *((int *)arg);
440 }
441
442 static void test_reftable_stack_update_index_check(void)
443 {
444 char *dir = get_tmp_dir(__LINE__);
445
446 struct reftable_write_options cfg = { 0 };
447 struct reftable_stack *st = NULL;
448 int err;
449 struct reftable_ref_record ref1 = {
450 .refname = "name1",
451 .update_index = 1,
452 .value_type = REFTABLE_REF_SYMREF,
453 .value.symref = "master",
454 };
455 struct reftable_ref_record ref2 = {
456 .refname = "name2",
457 .update_index = 1,
458 .value_type = REFTABLE_REF_SYMREF,
459 .value.symref = "master",
460 };
461
462 err = reftable_new_stack(&st, dir, cfg);
463 EXPECT_ERR(err);
464
465 err = reftable_stack_add(st, &write_test_ref, &ref1);
466 EXPECT_ERR(err);
467
468 err = reftable_stack_add(st, &write_test_ref, &ref2);
469 EXPECT(err == REFTABLE_API_ERROR);
470 reftable_stack_destroy(st);
471 clear_dir(dir);
472 }
473
474 static void test_reftable_stack_lock_failure(void)
475 {
476 char *dir = get_tmp_dir(__LINE__);
477
478 struct reftable_write_options cfg = { 0 };
479 struct reftable_stack *st = NULL;
480 int err, i;
481
482 err = reftable_new_stack(&st, dir, cfg);
483 EXPECT_ERR(err);
484 for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) {
485 err = reftable_stack_add(st, &write_error, &i);
486 EXPECT(err == i);
487 }
488
489 reftable_stack_destroy(st);
490 clear_dir(dir);
491 }
492
493 static void test_reftable_stack_add(void)
494 {
495 int i = 0;
496 int err = 0;
497 struct reftable_write_options cfg = {
498 .exact_log_message = 1,
499 .default_permissions = 0660,
500 };
501 struct reftable_stack *st = NULL;
502 char *dir = get_tmp_dir(__LINE__);
503 struct reftable_ref_record refs[2] = { { NULL } };
504 struct reftable_log_record logs[2] = { { NULL } };
505 struct strbuf path = STRBUF_INIT;
506 struct stat stat_result;
507 int N = ARRAY_SIZE(refs);
508
509 err = reftable_new_stack(&st, dir, cfg);
510 EXPECT_ERR(err);
511 st->disable_auto_compact = 1;
512
513 for (i = 0; i < N; i++) {
514 char buf[256];
515 snprintf(buf, sizeof(buf), "branch%02d", i);
516 refs[i].refname = xstrdup(buf);
517 refs[i].update_index = i + 1;
518 refs[i].value_type = REFTABLE_REF_VAL1;
519 set_test_hash(refs[i].value.val1, i);
520
521 logs[i].refname = xstrdup(buf);
522 logs[i].update_index = N + i + 1;
523 logs[i].value_type = REFTABLE_LOG_UPDATE;
524 logs[i].value.update.email = xstrdup("identity@invalid");
525 set_test_hash(logs[i].value.update.new_hash, i);
526 }
527
528 for (i = 0; i < N; i++) {
529 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
530 EXPECT_ERR(err);
531 }
532
533 for (i = 0; i < N; i++) {
534 struct write_log_arg arg = {
535 .log = &logs[i],
536 .update_index = reftable_stack_next_update_index(st),
537 };
538 int err = reftable_stack_add(st, &write_test_log, &arg);
539 EXPECT_ERR(err);
540 }
541
542 err = reftable_stack_compact_all(st, NULL);
543 EXPECT_ERR(err);
544
545 for (i = 0; i < N; i++) {
546 struct reftable_ref_record dest = { NULL };
547
548 int err = reftable_stack_read_ref(st, refs[i].refname, &dest);
549 EXPECT_ERR(err);
550 EXPECT(reftable_ref_record_equal(&dest, refs + i,
551 GIT_SHA1_RAWSZ));
552 reftable_ref_record_release(&dest);
553 }
554
555 for (i = 0; i < N; i++) {
556 struct reftable_log_record dest = { NULL };
557 int err = reftable_stack_read_log(st, refs[i].refname, &dest);
558 EXPECT_ERR(err);
559 EXPECT(reftable_log_record_equal(&dest, logs + i,
560 GIT_SHA1_RAWSZ));
561 reftable_log_record_release(&dest);
562 }
563
564 #ifndef GIT_WINDOWS_NATIVE
565 strbuf_addstr(&path, dir);
566 strbuf_addstr(&path, "/tables.list");
567 err = stat(path.buf, &stat_result);
568 EXPECT(!err);
569 EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
570
571 strbuf_reset(&path);
572 strbuf_addstr(&path, dir);
573 strbuf_addstr(&path, "/");
574 /* do not try at home; not an external API for reftable. */
575 strbuf_addstr(&path, st->readers[0]->name);
576 err = stat(path.buf, &stat_result);
577 EXPECT(!err);
578 EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
579 #else
580 (void) stat_result;
581 #endif
582
583 /* cleanup */
584 reftable_stack_destroy(st);
585 for (i = 0; i < N; i++) {
586 reftable_ref_record_release(&refs[i]);
587 reftable_log_record_release(&logs[i]);
588 }
589 strbuf_release(&path);
590 clear_dir(dir);
591 }
592
593 static void test_reftable_stack_log_normalize(void)
594 {
595 int err = 0;
596 struct reftable_write_options cfg = {
597 0,
598 };
599 struct reftable_stack *st = NULL;
600 char *dir = get_tmp_dir(__LINE__);
601 struct reftable_log_record input = {
602 .refname = "branch",
603 .update_index = 1,
604 .value_type = REFTABLE_LOG_UPDATE,
605 .value = {
606 .update = {
607 .new_hash = { 1 },
608 .old_hash = { 2 },
609 },
610 },
611 };
612 struct reftable_log_record dest = {
613 .update_index = 0,
614 };
615 struct write_log_arg arg = {
616 .log = &input,
617 .update_index = 1,
618 };
619
620 err = reftable_new_stack(&st, dir, cfg);
621 EXPECT_ERR(err);
622
623 input.value.update.message = "one\ntwo";
624 err = reftable_stack_add(st, &write_test_log, &arg);
625 EXPECT(err == REFTABLE_API_ERROR);
626
627 input.value.update.message = "one";
628 err = reftable_stack_add(st, &write_test_log, &arg);
629 EXPECT_ERR(err);
630
631 err = reftable_stack_read_log(st, input.refname, &dest);
632 EXPECT_ERR(err);
633 EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
634
635 input.value.update.message = "two\n";
636 arg.update_index = 2;
637 err = reftable_stack_add(st, &write_test_log, &arg);
638 EXPECT_ERR(err);
639 err = reftable_stack_read_log(st, input.refname, &dest);
640 EXPECT_ERR(err);
641 EXPECT(0 == strcmp(dest.value.update.message, "two\n"));
642
643 /* cleanup */
644 reftable_stack_destroy(st);
645 reftable_log_record_release(&dest);
646 clear_dir(dir);
647 }
648
649 static void test_reftable_stack_tombstone(void)
650 {
651 int i = 0;
652 char *dir = get_tmp_dir(__LINE__);
653
654 struct reftable_write_options cfg = { 0 };
655 struct reftable_stack *st = NULL;
656 int err;
657 struct reftable_ref_record refs[2] = { { NULL } };
658 struct reftable_log_record logs[2] = { { NULL } };
659 int N = ARRAY_SIZE(refs);
660 struct reftable_ref_record dest = { NULL };
661 struct reftable_log_record log_dest = { NULL };
662
663
664 err = reftable_new_stack(&st, dir, cfg);
665 EXPECT_ERR(err);
666
667 /* even entries add the refs, odd entries delete them. */
668 for (i = 0; i < N; i++) {
669 const char *buf = "branch";
670 refs[i].refname = xstrdup(buf);
671 refs[i].update_index = i + 1;
672 if (i % 2 == 0) {
673 refs[i].value_type = REFTABLE_REF_VAL1;
674 set_test_hash(refs[i].value.val1, i);
675 }
676
677 logs[i].refname = xstrdup(buf);
678 /* update_index is part of the key. */
679 logs[i].update_index = 42;
680 if (i % 2 == 0) {
681 logs[i].value_type = REFTABLE_LOG_UPDATE;
682 set_test_hash(logs[i].value.update.new_hash, i);
683 logs[i].value.update.email =
684 xstrdup("identity@invalid");
685 }
686 }
687 for (i = 0; i < N; i++) {
688 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
689 EXPECT_ERR(err);
690 }
691
692 for (i = 0; i < N; i++) {
693 struct write_log_arg arg = {
694 .log = &logs[i],
695 .update_index = reftable_stack_next_update_index(st),
696 };
697 int err = reftable_stack_add(st, &write_test_log, &arg);
698 EXPECT_ERR(err);
699 }
700
701 err = reftable_stack_read_ref(st, "branch", &dest);
702 EXPECT(err == 1);
703 reftable_ref_record_release(&dest);
704
705 err = reftable_stack_read_log(st, "branch", &log_dest);
706 EXPECT(err == 1);
707 reftable_log_record_release(&log_dest);
708
709 err = reftable_stack_compact_all(st, NULL);
710 EXPECT_ERR(err);
711
712 err = reftable_stack_read_ref(st, "branch", &dest);
713 EXPECT(err == 1);
714
715 err = reftable_stack_read_log(st, "branch", &log_dest);
716 EXPECT(err == 1);
717 reftable_ref_record_release(&dest);
718 reftable_log_record_release(&log_dest);
719
720 /* cleanup */
721 reftable_stack_destroy(st);
722 for (i = 0; i < N; i++) {
723 reftable_ref_record_release(&refs[i]);
724 reftable_log_record_release(&logs[i]);
725 }
726 clear_dir(dir);
727 }
728
729 static void test_reftable_stack_hash_id(void)
730 {
731 char *dir = get_tmp_dir(__LINE__);
732
733 struct reftable_write_options cfg = { 0 };
734 struct reftable_stack *st = NULL;
735 int err;
736
737 struct reftable_ref_record ref = {
738 .refname = "master",
739 .value_type = REFTABLE_REF_SYMREF,
740 .value.symref = "target",
741 .update_index = 1,
742 };
743 struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
744 struct reftable_stack *st32 = NULL;
745 struct reftable_write_options cfg_default = { 0 };
746 struct reftable_stack *st_default = NULL;
747 struct reftable_ref_record dest = { NULL };
748
749 err = reftable_new_stack(&st, dir, cfg);
750 EXPECT_ERR(err);
751
752 err = reftable_stack_add(st, &write_test_ref, &ref);
753 EXPECT_ERR(err);
754
755 /* can't read it with the wrong hash ID. */
756 err = reftable_new_stack(&st32, dir, cfg32);
757 EXPECT(err == REFTABLE_FORMAT_ERROR);
758
759 /* check that we can read it back with default config too. */
760 err = reftable_new_stack(&st_default, dir, cfg_default);
761 EXPECT_ERR(err);
762
763 err = reftable_stack_read_ref(st_default, "master", &dest);
764 EXPECT_ERR(err);
765
766 EXPECT(reftable_ref_record_equal(&ref, &dest, GIT_SHA1_RAWSZ));
767 reftable_ref_record_release(&dest);
768 reftable_stack_destroy(st);
769 reftable_stack_destroy(st_default);
770 clear_dir(dir);
771 }
772
773 static void test_log2(void)
774 {
775 EXPECT(1 == fastlog2(3));
776 EXPECT(2 == fastlog2(4));
777 EXPECT(2 == fastlog2(5));
778 }
779
780 static void test_sizes_to_segments(void)
781 {
782 uint64_t sizes[] = { 2, 3, 4, 5, 7, 9 };
783 /* .................0 1 2 3 4 5 */
784
785 size_t seglen = 0;
786 struct segment *segs =
787 sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
788 EXPECT(segs[2].log == 3);
789 EXPECT(segs[2].start == 5);
790 EXPECT(segs[2].end == 6);
791
792 EXPECT(segs[1].log == 2);
793 EXPECT(segs[1].start == 2);
794 EXPECT(segs[1].end == 5);
795 reftable_free(segs);
796 }
797
798 static void test_sizes_to_segments_empty(void)
799 {
800 size_t seglen = 0;
801 struct segment *segs = sizes_to_segments(&seglen, NULL, 0);
802 EXPECT(seglen == 0);
803 reftable_free(segs);
804 }
805
806 static void test_sizes_to_segments_all_equal(void)
807 {
808 uint64_t sizes[] = { 5, 5 };
809 size_t seglen = 0;
810 struct segment *segs =
811 sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
812 EXPECT(seglen == 1);
813 EXPECT(segs[0].start == 0);
814 EXPECT(segs[0].end == 2);
815 reftable_free(segs);
816 }
817
818 static void test_suggest_compaction_segment(void)
819 {
820 uint64_t sizes[] = { 128, 64, 17, 16, 9, 9, 9, 16, 16 };
821 /* .................0 1 2 3 4 5 6 */
822 struct segment min =
823 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
824 EXPECT(min.start == 2);
825 EXPECT(min.end == 7);
826 }
827
828 static void test_suggest_compaction_segment_nothing(void)
829 {
830 uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
831 struct segment result =
832 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
833 EXPECT(result.start == result.end);
834 }
835
836 static void test_reflog_expire(void)
837 {
838 char *dir = get_tmp_dir(__LINE__);
839
840 struct reftable_write_options cfg = { 0 };
841 struct reftable_stack *st = NULL;
842 struct reftable_log_record logs[20] = { { NULL } };
843 int N = ARRAY_SIZE(logs) - 1;
844 int i = 0;
845 int err;
846 struct reftable_log_expiry_config expiry = {
847 .time = 10,
848 };
849 struct reftable_log_record log = { NULL };
850
851
852 err = reftable_new_stack(&st, dir, cfg);
853 EXPECT_ERR(err);
854
855 for (i = 1; i <= N; i++) {
856 char buf[256];
857 snprintf(buf, sizeof(buf), "branch%02d", i);
858
859 logs[i].refname = xstrdup(buf);
860 logs[i].update_index = i;
861 logs[i].value_type = REFTABLE_LOG_UPDATE;
862 logs[i].value.update.time = i;
863 logs[i].value.update.email = xstrdup("identity@invalid");
864 set_test_hash(logs[i].value.update.new_hash, i);
865 }
866
867 for (i = 1; i <= N; i++) {
868 struct write_log_arg arg = {
869 .log = &logs[i],
870 .update_index = reftable_stack_next_update_index(st),
871 };
872 int err = reftable_stack_add(st, &write_test_log, &arg);
873 EXPECT_ERR(err);
874 }
875
876 err = reftable_stack_compact_all(st, NULL);
877 EXPECT_ERR(err);
878
879 err = reftable_stack_compact_all(st, &expiry);
880 EXPECT_ERR(err);
881
882 err = reftable_stack_read_log(st, logs[9].refname, &log);
883 EXPECT(err == 1);
884
885 err = reftable_stack_read_log(st, logs[11].refname, &log);
886 EXPECT_ERR(err);
887
888 expiry.min_update_index = 15;
889 err = reftable_stack_compact_all(st, &expiry);
890 EXPECT_ERR(err);
891
892 err = reftable_stack_read_log(st, logs[14].refname, &log);
893 EXPECT(err == 1);
894
895 err = reftable_stack_read_log(st, logs[16].refname, &log);
896 EXPECT_ERR(err);
897
898 /* cleanup */
899 reftable_stack_destroy(st);
900 for (i = 0; i <= N; i++) {
901 reftable_log_record_release(&logs[i]);
902 }
903 clear_dir(dir);
904 reftable_log_record_release(&log);
905 }
906
907 static int write_nothing(struct reftable_writer *wr, void *arg)
908 {
909 reftable_writer_set_limits(wr, 1, 1);
910 return 0;
911 }
912
913 static void test_empty_add(void)
914 {
915 struct reftable_write_options cfg = { 0 };
916 struct reftable_stack *st = NULL;
917 int err;
918 char *dir = get_tmp_dir(__LINE__);
919
920 struct reftable_stack *st2 = NULL;
921
922
923 err = reftable_new_stack(&st, dir, cfg);
924 EXPECT_ERR(err);
925
926 err = reftable_stack_add(st, &write_nothing, NULL);
927 EXPECT_ERR(err);
928
929 err = reftable_new_stack(&st2, dir, cfg);
930 EXPECT_ERR(err);
931 clear_dir(dir);
932 reftable_stack_destroy(st);
933 reftable_stack_destroy(st2);
934 }
935
936 static void test_reftable_stack_auto_compaction(void)
937 {
938 struct reftable_write_options cfg = { 0 };
939 struct reftable_stack *st = NULL;
940 char *dir = get_tmp_dir(__LINE__);
941
942 int err, i;
943 int N = 100;
944
945 err = reftable_new_stack(&st, dir, cfg);
946 EXPECT_ERR(err);
947
948 st->disable_auto_compact = 1; /* call manually below for coverage. */
949 for (i = 0; i < N; i++) {
950 char name[100];
951 struct reftable_ref_record ref = {
952 .refname = name,
953 .update_index = reftable_stack_next_update_index(st),
954 .value_type = REFTABLE_REF_SYMREF,
955 .value.symref = "master",
956 };
957 snprintf(name, sizeof(name), "branch%04d", i);
958
959 err = reftable_stack_add(st, &write_test_ref, &ref);
960 EXPECT_ERR(err);
961
962 err = reftable_stack_auto_compact(st);
963 EXPECT_ERR(err);
964 EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
965 }
966
967 EXPECT(reftable_stack_compaction_stats(st)->entries_written <
968 (uint64_t)(N * fastlog2(N)));
969
970 reftable_stack_destroy(st);
971 clear_dir(dir);
972 }
973
974 static void test_reftable_stack_add_performs_auto_compaction(void)
975 {
976 struct reftable_write_options cfg = { 0 };
977 struct reftable_stack *st = NULL;
978 struct strbuf refname = STRBUF_INIT;
979 char *dir = get_tmp_dir(__LINE__);
980 int err, i, n = 20;
981
982 err = reftable_new_stack(&st, dir, cfg);
983 EXPECT_ERR(err);
984
985 for (i = 0; i <= n; i++) {
986 struct reftable_ref_record ref = {
987 .update_index = reftable_stack_next_update_index(st),
988 .value_type = REFTABLE_REF_SYMREF,
989 .value.symref = "master",
990 };
991
992 /*
993 * Disable auto-compaction for all but the last runs. Like this
994 * we can ensure that we indeed honor this setting and have
995 * better control over when exactly auto compaction runs.
996 */
997 st->disable_auto_compact = i != n;
998
999 strbuf_reset(&refname);
1000 strbuf_addf(&refname, "branch-%04d", i);
1001 ref.refname = refname.buf;
1002
1003 err = reftable_stack_add(st, &write_test_ref, &ref);
1004 EXPECT_ERR(err);
1005
1006 /*
1007 * The stack length should grow continuously for all runs where
1008 * auto compaction is disabled. When enabled, we should merge
1009 * all tables in the stack.
1010 */
1011 if (i != n)
1012 EXPECT(st->merged->stack_len == i + 1);
1013 else
1014 EXPECT(st->merged->stack_len == 1);
1015 }
1016
1017 reftable_stack_destroy(st);
1018 strbuf_release(&refname);
1019 clear_dir(dir);
1020 }
1021
1022 static void test_reftable_stack_compaction_concurrent(void)
1023 {
1024 struct reftable_write_options cfg = { 0 };
1025 struct reftable_stack *st1 = NULL, *st2 = NULL;
1026 char *dir = get_tmp_dir(__LINE__);
1027
1028 int err, i;
1029 int N = 3;
1030
1031 err = reftable_new_stack(&st1, dir, cfg);
1032 EXPECT_ERR(err);
1033
1034 for (i = 0; i < N; i++) {
1035 char name[100];
1036 struct reftable_ref_record ref = {
1037 .refname = name,
1038 .update_index = reftable_stack_next_update_index(st1),
1039 .value_type = REFTABLE_REF_SYMREF,
1040 .value.symref = "master",
1041 };
1042 snprintf(name, sizeof(name), "branch%04d", i);
1043
1044 err = reftable_stack_add(st1, &write_test_ref, &ref);
1045 EXPECT_ERR(err);
1046 }
1047
1048 err = reftable_new_stack(&st2, dir, cfg);
1049 EXPECT_ERR(err);
1050
1051 err = reftable_stack_compact_all(st1, NULL);
1052 EXPECT_ERR(err);
1053
1054 reftable_stack_destroy(st1);
1055 reftable_stack_destroy(st2);
1056
1057 EXPECT(count_dir_entries(dir) == 2);
1058 clear_dir(dir);
1059 }
1060
1061 static void unclean_stack_close(struct reftable_stack *st)
1062 {
1063 /* break abstraction boundary to simulate unclean shutdown. */
1064 int i = 0;
1065 for (; i < st->readers_len; i++) {
1066 reftable_reader_free(st->readers[i]);
1067 }
1068 st->readers_len = 0;
1069 FREE_AND_NULL(st->readers);
1070 }
1071
1072 static void test_reftable_stack_compaction_concurrent_clean(void)
1073 {
1074 struct reftable_write_options cfg = { 0 };
1075 struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
1076 char *dir = get_tmp_dir(__LINE__);
1077
1078 int err, i;
1079 int N = 3;
1080
1081 err = reftable_new_stack(&st1, dir, cfg);
1082 EXPECT_ERR(err);
1083
1084 for (i = 0; i < N; i++) {
1085 char name[100];
1086 struct reftable_ref_record ref = {
1087 .refname = name,
1088 .update_index = reftable_stack_next_update_index(st1),
1089 .value_type = REFTABLE_REF_SYMREF,
1090 .value.symref = "master",
1091 };
1092 snprintf(name, sizeof(name), "branch%04d", i);
1093
1094 err = reftable_stack_add(st1, &write_test_ref, &ref);
1095 EXPECT_ERR(err);
1096 }
1097
1098 err = reftable_new_stack(&st2, dir, cfg);
1099 EXPECT_ERR(err);
1100
1101 err = reftable_stack_compact_all(st1, NULL);
1102 EXPECT_ERR(err);
1103
1104 unclean_stack_close(st1);
1105 unclean_stack_close(st2);
1106
1107 err = reftable_new_stack(&st3, dir, cfg);
1108 EXPECT_ERR(err);
1109
1110 err = reftable_stack_clean(st3);
1111 EXPECT_ERR(err);
1112 EXPECT(count_dir_entries(dir) == 2);
1113
1114 reftable_stack_destroy(st1);
1115 reftable_stack_destroy(st2);
1116 reftable_stack_destroy(st3);
1117
1118 clear_dir(dir);
1119 }
1120
1121 int stack_test_main(int argc, const char *argv[])
1122 {
1123 RUN_TEST(test_empty_add);
1124 RUN_TEST(test_log2);
1125 RUN_TEST(test_names_equal);
1126 RUN_TEST(test_parse_names);
1127 RUN_TEST(test_read_file);
1128 RUN_TEST(test_reflog_expire);
1129 RUN_TEST(test_reftable_stack_add);
1130 RUN_TEST(test_reftable_stack_add_one);
1131 RUN_TEST(test_reftable_stack_auto_compaction);
1132 RUN_TEST(test_reftable_stack_add_performs_auto_compaction);
1133 RUN_TEST(test_reftable_stack_compaction_concurrent);
1134 RUN_TEST(test_reftable_stack_compaction_concurrent_clean);
1135 RUN_TEST(test_reftable_stack_hash_id);
1136 RUN_TEST(test_reftable_stack_lock_failure);
1137 RUN_TEST(test_reftable_stack_log_normalize);
1138 RUN_TEST(test_reftable_stack_tombstone);
1139 RUN_TEST(test_reftable_stack_transaction_api);
1140 RUN_TEST(test_reftable_stack_transaction_api_performs_auto_compaction);
1141 RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
1142 RUN_TEST(test_reftable_stack_update_index_check);
1143 RUN_TEST(test_reftable_stack_uptodate);
1144 RUN_TEST(test_reftable_stack_validate_refname);
1145 RUN_TEST(test_sizes_to_segments);
1146 RUN_TEST(test_sizes_to_segments_all_equal);
1147 RUN_TEST(test_sizes_to_segments_empty);
1148 RUN_TEST(test_suggest_compaction_segment);
1149 RUN_TEST(test_suggest_compaction_segment_nothing);
1150 return 0;
1151 }