Merge branch 'en/sparse-checkout-set'
[git/debian.git] / reftable / stack_test.c
blobeb0b7228b0c65bf5461282be7dfd3e6cb7786c9b
1 /*
2 Copyright 2020 Google LLC
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 */
9 #include "stack.h"
11 #include "system.h"
13 #include "reftable-reader.h"
14 #include "merged.h"
15 #include "basics.h"
16 #include "constants.h"
17 #include "record.h"
18 #include "test_framework.h"
19 #include "reftable-tests.h"
21 #include <sys/types.h>
22 #include <dirent.h>
24 static void clear_dir(const char *dirname)
26 struct strbuf path = STRBUF_INIT;
27 strbuf_addstr(&path, dirname);
28 remove_dir_recursively(&path, 0);
29 strbuf_release(&path);
32 static int count_dir_entries(const char *dirname)
34 DIR *dir = opendir(dirname);
35 int len = 0;
36 struct dirent *d;
37 if (dir == NULL)
38 return 0;
40 while ((d = readdir(dir))) {
41 if (!strcmp(d->d_name, "..") || !strcmp(d->d_name, "."))
42 continue;
43 len++;
45 closedir(dir);
46 return len;
50 * Work linenumber into the tempdir, so we can see which tests forget to
51 * cleanup.
53 static char *get_tmp_template(int linenumber)
55 const char *tmp = getenv("TMPDIR");
56 static char template[1024];
57 snprintf(template, sizeof(template) - 1, "%s/stack_test-%d.XXXXXX",
58 tmp ? tmp : "/tmp", linenumber);
59 return template;
62 static char *get_tmp_dir(int linenumber)
64 char *dir = get_tmp_template(linenumber);
65 EXPECT(mkdtemp(dir));
66 return dir;
69 static void test_read_file(void)
71 char *fn = get_tmp_template(__LINE__);
72 int fd = mkstemp(fn);
73 char out[1024] = "line1\n\nline2\nline3";
74 int n, err;
75 char **names = NULL;
76 char *want[] = { "line1", "line2", "line3" };
77 int i = 0;
79 EXPECT(fd > 0);
80 n = write(fd, out, strlen(out));
81 EXPECT(n == strlen(out));
82 err = close(fd);
83 EXPECT(err >= 0);
85 err = read_lines(fn, &names);
86 EXPECT_ERR(err);
88 for (i = 0; names[i]; i++) {
89 EXPECT(0 == strcmp(want[i], names[i]));
91 free_names(names);
92 remove(fn);
95 static void test_parse_names(void)
97 char buf[] = "line\n";
98 char **names = NULL;
99 parse_names(buf, strlen(buf), &names);
101 EXPECT(NULL != names[0]);
102 EXPECT(0 == strcmp(names[0], "line"));
103 EXPECT(NULL == names[1]);
104 free_names(names);
107 static void test_names_equal(void)
109 char *a[] = { "a", "b", "c", NULL };
110 char *b[] = { "a", "b", "d", NULL };
111 char *c[] = { "a", "b", NULL };
113 EXPECT(names_equal(a, a));
114 EXPECT(!names_equal(a, b));
115 EXPECT(!names_equal(a, c));
118 static int write_test_ref(struct reftable_writer *wr, void *arg)
120 struct reftable_ref_record *ref = arg;
121 reftable_writer_set_limits(wr, ref->update_index, ref->update_index);
122 return reftable_writer_add_ref(wr, ref);
125 struct write_log_arg {
126 struct reftable_log_record *log;
127 uint64_t update_index;
130 static int write_test_log(struct reftable_writer *wr, void *arg)
132 struct write_log_arg *wla = arg;
134 reftable_writer_set_limits(wr, wla->update_index, wla->update_index);
135 return reftable_writer_add_log(wr, wla->log);
138 static void test_reftable_stack_add_one(void)
140 char *dir = get_tmp_dir(__LINE__);
142 struct reftable_write_options cfg = { 0 };
143 struct reftable_stack *st = NULL;
144 int err;
145 struct reftable_ref_record ref = {
146 .refname = "HEAD",
147 .update_index = 1,
148 .value_type = REFTABLE_REF_SYMREF,
149 .value.symref = "master",
151 struct reftable_ref_record dest = { NULL };
154 err = reftable_new_stack(&st, dir, cfg);
155 EXPECT_ERR(err);
157 err = reftable_stack_add(st, &write_test_ref, &ref);
158 EXPECT_ERR(err);
160 err = reftable_stack_read_ref(st, ref.refname, &dest);
161 EXPECT_ERR(err);
162 EXPECT(0 == strcmp("master", dest.value.symref));
164 printf("testing print functionality:\n");
165 err = reftable_stack_print_directory(dir, GIT_SHA1_FORMAT_ID);
166 EXPECT_ERR(err);
168 err = reftable_stack_print_directory(dir, GIT_SHA256_FORMAT_ID);
169 EXPECT(err == REFTABLE_FORMAT_ERROR);
171 reftable_ref_record_release(&dest);
172 reftable_stack_destroy(st);
173 clear_dir(dir);
176 static void test_reftable_stack_uptodate(void)
178 struct reftable_write_options cfg = { 0 };
179 struct reftable_stack *st1 = NULL;
180 struct reftable_stack *st2 = NULL;
181 char *dir = get_tmp_dir(__LINE__);
183 int err;
184 struct reftable_ref_record ref1 = {
185 .refname = "HEAD",
186 .update_index = 1,
187 .value_type = REFTABLE_REF_SYMREF,
188 .value.symref = "master",
190 struct reftable_ref_record ref2 = {
191 .refname = "branch2",
192 .update_index = 2,
193 .value_type = REFTABLE_REF_SYMREF,
194 .value.symref = "master",
198 /* simulate multi-process access to the same stack
199 by creating two stacks for the same directory.
201 err = reftable_new_stack(&st1, dir, cfg);
202 EXPECT_ERR(err);
204 err = reftable_new_stack(&st2, dir, cfg);
205 EXPECT_ERR(err);
207 err = reftable_stack_add(st1, &write_test_ref, &ref1);
208 EXPECT_ERR(err);
210 err = reftable_stack_add(st2, &write_test_ref, &ref2);
211 EXPECT(err == REFTABLE_LOCK_ERROR);
213 err = reftable_stack_reload(st2);
214 EXPECT_ERR(err);
216 err = reftable_stack_add(st2, &write_test_ref, &ref2);
217 EXPECT_ERR(err);
218 reftable_stack_destroy(st1);
219 reftable_stack_destroy(st2);
220 clear_dir(dir);
223 static void test_reftable_stack_transaction_api(void)
225 char *dir = get_tmp_dir(__LINE__);
227 struct reftable_write_options cfg = { 0 };
228 struct reftable_stack *st = NULL;
229 int err;
230 struct reftable_addition *add = NULL;
232 struct reftable_ref_record ref = {
233 .refname = "HEAD",
234 .update_index = 1,
235 .value_type = REFTABLE_REF_SYMREF,
236 .value.symref = "master",
238 struct reftable_ref_record dest = { NULL };
241 err = reftable_new_stack(&st, dir, cfg);
242 EXPECT_ERR(err);
244 reftable_addition_destroy(add);
246 err = reftable_stack_new_addition(&add, st);
247 EXPECT_ERR(err);
249 err = reftable_addition_add(add, &write_test_ref, &ref);
250 EXPECT_ERR(err);
252 err = reftable_addition_commit(add);
253 EXPECT_ERR(err);
255 reftable_addition_destroy(add);
257 err = reftable_stack_read_ref(st, ref.refname, &dest);
258 EXPECT_ERR(err);
259 EXPECT(REFTABLE_REF_SYMREF == dest.value_type);
260 EXPECT(0 == strcmp("master", dest.value.symref));
262 reftable_ref_record_release(&dest);
263 reftable_stack_destroy(st);
264 clear_dir(dir);
267 static void test_reftable_stack_validate_refname(void)
269 struct reftable_write_options cfg = { 0 };
270 struct reftable_stack *st = NULL;
271 int err;
272 char *dir = get_tmp_dir(__LINE__);
274 int i;
275 struct reftable_ref_record ref = {
276 .refname = "a/b",
277 .update_index = 1,
278 .value_type = REFTABLE_REF_SYMREF,
279 .value.symref = "master",
281 char *additions[] = { "a", "a/b/c" };
283 err = reftable_new_stack(&st, dir, cfg);
284 EXPECT_ERR(err);
286 err = reftable_stack_add(st, &write_test_ref, &ref);
287 EXPECT_ERR(err);
289 for (i = 0; i < ARRAY_SIZE(additions); i++) {
290 struct reftable_ref_record ref = {
291 .refname = additions[i],
292 .update_index = 1,
293 .value_type = REFTABLE_REF_SYMREF,
294 .value.symref = "master",
297 err = reftable_stack_add(st, &write_test_ref, &ref);
298 EXPECT(err == REFTABLE_NAME_CONFLICT);
301 reftable_stack_destroy(st);
302 clear_dir(dir);
305 static int write_error(struct reftable_writer *wr, void *arg)
307 return *((int *)arg);
310 static void test_reftable_stack_update_index_check(void)
312 char *dir = get_tmp_dir(__LINE__);
314 struct reftable_write_options cfg = { 0 };
315 struct reftable_stack *st = NULL;
316 int err;
317 struct reftable_ref_record ref1 = {
318 .refname = "name1",
319 .update_index = 1,
320 .value_type = REFTABLE_REF_SYMREF,
321 .value.symref = "master",
323 struct reftable_ref_record ref2 = {
324 .refname = "name2",
325 .update_index = 1,
326 .value_type = REFTABLE_REF_SYMREF,
327 .value.symref = "master",
330 err = reftable_new_stack(&st, dir, cfg);
331 EXPECT_ERR(err);
333 err = reftable_stack_add(st, &write_test_ref, &ref1);
334 EXPECT_ERR(err);
336 err = reftable_stack_add(st, &write_test_ref, &ref2);
337 EXPECT(err == REFTABLE_API_ERROR);
338 reftable_stack_destroy(st);
339 clear_dir(dir);
342 static void test_reftable_stack_lock_failure(void)
344 char *dir = get_tmp_dir(__LINE__);
346 struct reftable_write_options cfg = { 0 };
347 struct reftable_stack *st = NULL;
348 int err, i;
350 err = reftable_new_stack(&st, dir, cfg);
351 EXPECT_ERR(err);
352 for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) {
353 err = reftable_stack_add(st, &write_error, &i);
354 EXPECT(err == i);
357 reftable_stack_destroy(st);
358 clear_dir(dir);
361 static void test_reftable_stack_add(void)
363 int i = 0;
364 int err = 0;
365 struct reftable_write_options cfg = {
366 .exact_log_message = 1,
368 struct reftable_stack *st = NULL;
369 char *dir = get_tmp_dir(__LINE__);
371 struct reftable_ref_record refs[2] = { { NULL } };
372 struct reftable_log_record logs[2] = { { NULL } };
373 int N = ARRAY_SIZE(refs);
376 err = reftable_new_stack(&st, dir, cfg);
377 EXPECT_ERR(err);
378 st->disable_auto_compact = 1;
380 for (i = 0; i < N; i++) {
381 char buf[256];
382 snprintf(buf, sizeof(buf), "branch%02d", i);
383 refs[i].refname = xstrdup(buf);
384 refs[i].update_index = i + 1;
385 refs[i].value_type = REFTABLE_REF_VAL1;
386 refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
387 set_test_hash(refs[i].value.val1, i);
389 logs[i].refname = xstrdup(buf);
390 logs[i].update_index = N + i + 1;
391 logs[i].value_type = REFTABLE_LOG_UPDATE;
393 logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
394 logs[i].value.update.email = xstrdup("identity@invalid");
395 set_test_hash(logs[i].value.update.new_hash, i);
398 for (i = 0; i < N; i++) {
399 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
400 EXPECT_ERR(err);
403 for (i = 0; i < N; i++) {
404 struct write_log_arg arg = {
405 .log = &logs[i],
406 .update_index = reftable_stack_next_update_index(st),
408 int err = reftable_stack_add(st, &write_test_log, &arg);
409 EXPECT_ERR(err);
412 err = reftable_stack_compact_all(st, NULL);
413 EXPECT_ERR(err);
415 for (i = 0; i < N; i++) {
416 struct reftable_ref_record dest = { NULL };
418 int err = reftable_stack_read_ref(st, refs[i].refname, &dest);
419 EXPECT_ERR(err);
420 EXPECT(reftable_ref_record_equal(&dest, refs + i,
421 GIT_SHA1_RAWSZ));
422 reftable_ref_record_release(&dest);
425 for (i = 0; i < N; i++) {
426 struct reftable_log_record dest = { NULL };
427 int err = reftable_stack_read_log(st, refs[i].refname, &dest);
428 EXPECT_ERR(err);
429 EXPECT(reftable_log_record_equal(&dest, logs + i,
430 GIT_SHA1_RAWSZ));
431 reftable_log_record_release(&dest);
434 /* cleanup */
435 reftable_stack_destroy(st);
436 for (i = 0; i < N; i++) {
437 reftable_ref_record_release(&refs[i]);
438 reftable_log_record_release(&logs[i]);
440 clear_dir(dir);
443 static void test_reftable_stack_log_normalize(void)
445 int err = 0;
446 struct reftable_write_options cfg = {
449 struct reftable_stack *st = NULL;
450 char *dir = get_tmp_dir(__LINE__);
452 uint8_t h1[GIT_SHA1_RAWSZ] = { 0x01 }, h2[GIT_SHA1_RAWSZ] = { 0x02 };
454 struct reftable_log_record input = { .refname = "branch",
455 .update_index = 1,
456 .value_type = REFTABLE_LOG_UPDATE,
457 .value = { .update = {
458 .new_hash = h1,
459 .old_hash = h2,
460 } } };
461 struct reftable_log_record dest = {
462 .update_index = 0,
464 struct write_log_arg arg = {
465 .log = &input,
466 .update_index = 1,
469 err = reftable_new_stack(&st, dir, cfg);
470 EXPECT_ERR(err);
472 input.value.update.message = "one\ntwo";
473 err = reftable_stack_add(st, &write_test_log, &arg);
474 EXPECT(err == REFTABLE_API_ERROR);
476 input.value.update.message = "one";
477 err = reftable_stack_add(st, &write_test_log, &arg);
478 EXPECT_ERR(err);
480 err = reftable_stack_read_log(st, input.refname, &dest);
481 EXPECT_ERR(err);
482 EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
484 input.value.update.message = "two\n";
485 arg.update_index = 2;
486 err = reftable_stack_add(st, &write_test_log, &arg);
487 EXPECT_ERR(err);
488 err = reftable_stack_read_log(st, input.refname, &dest);
489 EXPECT_ERR(err);
490 EXPECT(0 == strcmp(dest.value.update.message, "two\n"));
492 /* cleanup */
493 reftable_stack_destroy(st);
494 reftable_log_record_release(&dest);
495 clear_dir(dir);
498 static void test_reftable_stack_tombstone(void)
500 int i = 0;
501 char *dir = get_tmp_dir(__LINE__);
503 struct reftable_write_options cfg = { 0 };
504 struct reftable_stack *st = NULL;
505 int err;
506 struct reftable_ref_record refs[2] = { { NULL } };
507 struct reftable_log_record logs[2] = { { NULL } };
508 int N = ARRAY_SIZE(refs);
509 struct reftable_ref_record dest = { NULL };
510 struct reftable_log_record log_dest = { NULL };
513 err = reftable_new_stack(&st, dir, cfg);
514 EXPECT_ERR(err);
516 /* even entries add the refs, odd entries delete them. */
517 for (i = 0; i < N; i++) {
518 const char *buf = "branch";
519 refs[i].refname = xstrdup(buf);
520 refs[i].update_index = i + 1;
521 if (i % 2 == 0) {
522 refs[i].value_type = REFTABLE_REF_VAL1;
523 refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
524 set_test_hash(refs[i].value.val1, i);
527 logs[i].refname = xstrdup(buf);
528 /* update_index is part of the key. */
529 logs[i].update_index = 42;
530 if (i % 2 == 0) {
531 logs[i].value_type = REFTABLE_LOG_UPDATE;
532 logs[i].value.update.new_hash =
533 reftable_malloc(GIT_SHA1_RAWSZ);
534 set_test_hash(logs[i].value.update.new_hash, i);
535 logs[i].value.update.email =
536 xstrdup("identity@invalid");
539 for (i = 0; i < N; i++) {
540 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
541 EXPECT_ERR(err);
544 for (i = 0; i < N; i++) {
545 struct write_log_arg arg = {
546 .log = &logs[i],
547 .update_index = reftable_stack_next_update_index(st),
549 int err = reftable_stack_add(st, &write_test_log, &arg);
550 EXPECT_ERR(err);
553 err = reftable_stack_read_ref(st, "branch", &dest);
554 EXPECT(err == 1);
555 reftable_ref_record_release(&dest);
557 err = reftable_stack_read_log(st, "branch", &log_dest);
558 EXPECT(err == 1);
559 reftable_log_record_release(&log_dest);
561 err = reftable_stack_compact_all(st, NULL);
562 EXPECT_ERR(err);
564 err = reftable_stack_read_ref(st, "branch", &dest);
565 EXPECT(err == 1);
567 err = reftable_stack_read_log(st, "branch", &log_dest);
568 EXPECT(err == 1);
569 reftable_ref_record_release(&dest);
570 reftable_log_record_release(&log_dest);
572 /* cleanup */
573 reftable_stack_destroy(st);
574 for (i = 0; i < N; i++) {
575 reftable_ref_record_release(&refs[i]);
576 reftable_log_record_release(&logs[i]);
578 clear_dir(dir);
581 static void test_reftable_stack_hash_id(void)
583 char *dir = get_tmp_dir(__LINE__);
585 struct reftable_write_options cfg = { 0 };
586 struct reftable_stack *st = NULL;
587 int err;
589 struct reftable_ref_record ref = {
590 .refname = "master",
591 .value_type = REFTABLE_REF_SYMREF,
592 .value.symref = "target",
593 .update_index = 1,
595 struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
596 struct reftable_stack *st32 = NULL;
597 struct reftable_write_options cfg_default = { 0 };
598 struct reftable_stack *st_default = NULL;
599 struct reftable_ref_record dest = { NULL };
601 err = reftable_new_stack(&st, dir, cfg);
602 EXPECT_ERR(err);
604 err = reftable_stack_add(st, &write_test_ref, &ref);
605 EXPECT_ERR(err);
607 /* can't read it with the wrong hash ID. */
608 err = reftable_new_stack(&st32, dir, cfg32);
609 EXPECT(err == REFTABLE_FORMAT_ERROR);
611 /* check that we can read it back with default config too. */
612 err = reftable_new_stack(&st_default, dir, cfg_default);
613 EXPECT_ERR(err);
615 err = reftable_stack_read_ref(st_default, "master", &dest);
616 EXPECT_ERR(err);
618 EXPECT(reftable_ref_record_equal(&ref, &dest, GIT_SHA1_RAWSZ));
619 reftable_ref_record_release(&dest);
620 reftable_stack_destroy(st);
621 reftable_stack_destroy(st_default);
622 clear_dir(dir);
625 static void test_log2(void)
627 EXPECT(1 == fastlog2(3));
628 EXPECT(2 == fastlog2(4));
629 EXPECT(2 == fastlog2(5));
632 static void test_sizes_to_segments(void)
634 uint64_t sizes[] = { 2, 3, 4, 5, 7, 9 };
635 /* .................0 1 2 3 4 5 */
637 int seglen = 0;
638 struct segment *segs =
639 sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
640 EXPECT(segs[2].log == 3);
641 EXPECT(segs[2].start == 5);
642 EXPECT(segs[2].end == 6);
644 EXPECT(segs[1].log == 2);
645 EXPECT(segs[1].start == 2);
646 EXPECT(segs[1].end == 5);
647 reftable_free(segs);
650 static void test_sizes_to_segments_empty(void)
652 int seglen = 0;
653 struct segment *segs = sizes_to_segments(&seglen, NULL, 0);
654 EXPECT(seglen == 0);
655 reftable_free(segs);
658 static void test_sizes_to_segments_all_equal(void)
660 uint64_t sizes[] = { 5, 5 };
662 int seglen = 0;
663 struct segment *segs =
664 sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
665 EXPECT(seglen == 1);
666 EXPECT(segs[0].start == 0);
667 EXPECT(segs[0].end == 2);
668 reftable_free(segs);
671 static void test_suggest_compaction_segment(void)
673 uint64_t sizes[] = { 128, 64, 17, 16, 9, 9, 9, 16, 16 };
674 /* .................0 1 2 3 4 5 6 */
675 struct segment min =
676 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
677 EXPECT(min.start == 2);
678 EXPECT(min.end == 7);
681 static void test_suggest_compaction_segment_nothing(void)
683 uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
684 struct segment result =
685 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
686 EXPECT(result.start == result.end);
689 static void test_reflog_expire(void)
691 char *dir = get_tmp_dir(__LINE__);
693 struct reftable_write_options cfg = { 0 };
694 struct reftable_stack *st = NULL;
695 struct reftable_log_record logs[20] = { { NULL } };
696 int N = ARRAY_SIZE(logs) - 1;
697 int i = 0;
698 int err;
699 struct reftable_log_expiry_config expiry = {
700 .time = 10,
702 struct reftable_log_record log = { NULL };
705 err = reftable_new_stack(&st, dir, cfg);
706 EXPECT_ERR(err);
708 for (i = 1; i <= N; i++) {
709 char buf[256];
710 snprintf(buf, sizeof(buf), "branch%02d", i);
712 logs[i].refname = xstrdup(buf);
713 logs[i].update_index = i;
714 logs[i].value_type = REFTABLE_LOG_UPDATE;
715 logs[i].value.update.time = i;
716 logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
717 logs[i].value.update.email = xstrdup("identity@invalid");
718 set_test_hash(logs[i].value.update.new_hash, i);
721 for (i = 1; i <= N; i++) {
722 struct write_log_arg arg = {
723 .log = &logs[i],
724 .update_index = reftable_stack_next_update_index(st),
726 int err = reftable_stack_add(st, &write_test_log, &arg);
727 EXPECT_ERR(err);
730 err = reftable_stack_compact_all(st, NULL);
731 EXPECT_ERR(err);
733 err = reftable_stack_compact_all(st, &expiry);
734 EXPECT_ERR(err);
736 err = reftable_stack_read_log(st, logs[9].refname, &log);
737 EXPECT(err == 1);
739 err = reftable_stack_read_log(st, logs[11].refname, &log);
740 EXPECT_ERR(err);
742 expiry.min_update_index = 15;
743 err = reftable_stack_compact_all(st, &expiry);
744 EXPECT_ERR(err);
746 err = reftable_stack_read_log(st, logs[14].refname, &log);
747 EXPECT(err == 1);
749 err = reftable_stack_read_log(st, logs[16].refname, &log);
750 EXPECT_ERR(err);
752 /* cleanup */
753 reftable_stack_destroy(st);
754 for (i = 0; i <= N; i++) {
755 reftable_log_record_release(&logs[i]);
757 clear_dir(dir);
758 reftable_log_record_release(&log);
761 static int write_nothing(struct reftable_writer *wr, void *arg)
763 reftable_writer_set_limits(wr, 1, 1);
764 return 0;
767 static void test_empty_add(void)
769 struct reftable_write_options cfg = { 0 };
770 struct reftable_stack *st = NULL;
771 int err;
772 char *dir = get_tmp_dir(__LINE__);
774 struct reftable_stack *st2 = NULL;
777 err = reftable_new_stack(&st, dir, cfg);
778 EXPECT_ERR(err);
780 err = reftable_stack_add(st, &write_nothing, NULL);
781 EXPECT_ERR(err);
783 err = reftable_new_stack(&st2, dir, cfg);
784 EXPECT_ERR(err);
785 clear_dir(dir);
786 reftable_stack_destroy(st);
787 reftable_stack_destroy(st2);
790 static void test_reftable_stack_auto_compaction(void)
792 struct reftable_write_options cfg = { 0 };
793 struct reftable_stack *st = NULL;
794 char *dir = get_tmp_dir(__LINE__);
796 int err, i;
797 int N = 100;
799 err = reftable_new_stack(&st, dir, cfg);
800 EXPECT_ERR(err);
802 st->disable_auto_compact = 1; /* call manually below for coverage. */
803 for (i = 0; i < N; i++) {
804 char name[100];
805 struct reftable_ref_record ref = {
806 .refname = name,
807 .update_index = reftable_stack_next_update_index(st),
808 .value_type = REFTABLE_REF_SYMREF,
809 .value.symref = "master",
811 snprintf(name, sizeof(name), "branch%04d", i);
813 err = reftable_stack_add(st, &write_test_ref, &ref);
814 EXPECT_ERR(err);
816 err = reftable_stack_auto_compact(st);
817 EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
820 EXPECT(reftable_stack_compaction_stats(st)->entries_written <
821 (uint64_t)(N * fastlog2(N)));
823 reftable_stack_destroy(st);
824 clear_dir(dir);
827 static void test_reftable_stack_compaction_concurrent(void)
829 struct reftable_write_options cfg = { 0 };
830 struct reftable_stack *st1 = NULL, *st2 = NULL;
831 char *dir = get_tmp_dir(__LINE__);
833 int err, i;
834 int N = 3;
836 err = reftable_new_stack(&st1, dir, cfg);
837 EXPECT_ERR(err);
839 for (i = 0; i < N; i++) {
840 char name[100];
841 struct reftable_ref_record ref = {
842 .refname = name,
843 .update_index = reftable_stack_next_update_index(st1),
844 .value_type = REFTABLE_REF_SYMREF,
845 .value.symref = "master",
847 snprintf(name, sizeof(name), "branch%04d", i);
849 err = reftable_stack_add(st1, &write_test_ref, &ref);
850 EXPECT_ERR(err);
853 err = reftable_new_stack(&st2, dir, cfg);
854 EXPECT_ERR(err);
856 err = reftable_stack_compact_all(st1, NULL);
857 EXPECT_ERR(err);
859 reftable_stack_destroy(st1);
860 reftable_stack_destroy(st2);
862 EXPECT(count_dir_entries(dir) == 2);
863 clear_dir(dir);
866 static void unclean_stack_close(struct reftable_stack *st)
868 /* break abstraction boundary to simulate unclean shutdown. */
869 int i = 0;
870 for (; i < st->readers_len; i++) {
871 reftable_reader_free(st->readers[i]);
873 st->readers_len = 0;
874 FREE_AND_NULL(st->readers);
877 static void test_reftable_stack_compaction_concurrent_clean(void)
879 struct reftable_write_options cfg = { 0 };
880 struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
881 char *dir = get_tmp_dir(__LINE__);
883 int err, i;
884 int N = 3;
886 err = reftable_new_stack(&st1, dir, cfg);
887 EXPECT_ERR(err);
889 for (i = 0; i < N; i++) {
890 char name[100];
891 struct reftable_ref_record ref = {
892 .refname = name,
893 .update_index = reftable_stack_next_update_index(st1),
894 .value_type = REFTABLE_REF_SYMREF,
895 .value.symref = "master",
897 snprintf(name, sizeof(name), "branch%04d", i);
899 err = reftable_stack_add(st1, &write_test_ref, &ref);
900 EXPECT_ERR(err);
903 err = reftable_new_stack(&st2, dir, cfg);
904 EXPECT_ERR(err);
906 err = reftable_stack_compact_all(st1, NULL);
907 EXPECT_ERR(err);
909 unclean_stack_close(st1);
910 unclean_stack_close(st2);
912 err = reftable_new_stack(&st3, dir, cfg);
913 EXPECT_ERR(err);
915 err = reftable_stack_clean(st3);
916 EXPECT_ERR(err);
917 EXPECT(count_dir_entries(dir) == 2);
919 reftable_stack_destroy(st1);
920 reftable_stack_destroy(st2);
921 reftable_stack_destroy(st3);
923 clear_dir(dir);
926 int stack_test_main(int argc, const char *argv[])
928 RUN_TEST(test_empty_add);
929 RUN_TEST(test_log2);
930 RUN_TEST(test_names_equal);
931 RUN_TEST(test_parse_names);
932 RUN_TEST(test_read_file);
933 RUN_TEST(test_reflog_expire);
934 RUN_TEST(test_reftable_stack_add);
935 RUN_TEST(test_reftable_stack_add_one);
936 RUN_TEST(test_reftable_stack_auto_compaction);
937 RUN_TEST(test_reftable_stack_compaction_concurrent);
938 RUN_TEST(test_reftable_stack_compaction_concurrent_clean);
939 RUN_TEST(test_reftable_stack_hash_id);
940 RUN_TEST(test_reftable_stack_lock_failure);
941 RUN_TEST(test_reftable_stack_log_normalize);
942 RUN_TEST(test_reftable_stack_tombstone);
943 RUN_TEST(test_reftable_stack_transaction_api);
944 RUN_TEST(test_reftable_stack_update_index_check);
945 RUN_TEST(test_reftable_stack_uptodate);
946 RUN_TEST(test_reftable_stack_validate_refname);
947 RUN_TEST(test_sizes_to_segments);
948 RUN_TEST(test_sizes_to_segments_all_equal);
949 RUN_TEST(test_sizes_to_segments_empty);
950 RUN_TEST(test_suggest_compaction_segment);
951 RUN_TEST(test_suggest_compaction_segment_nothing);
952 return 0;