clean: factorize incompatibility message
[git.git] / reftable / stack_test.c
blobd0b717510fa7d6dd8832bf77490630964e33b23b
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"
20 #include "reader.h"
22 #include <sys/types.h>
23 #include <dirent.h>
25 static void clear_dir(const char *dirname)
27 struct strbuf path = STRBUF_INIT;
28 strbuf_addstr(&path, dirname);
29 remove_dir_recursively(&path, 0);
30 strbuf_release(&path);
33 static int count_dir_entries(const char *dirname)
35 DIR *dir = opendir(dirname);
36 int len = 0;
37 struct dirent *d;
38 if (!dir)
39 return 0;
41 while ((d = readdir(dir))) {
42 if (!strcmp(d->d_name, "..") || !strcmp(d->d_name, "."))
43 continue;
44 len++;
46 closedir(dir);
47 return len;
51 * Work linenumber into the tempdir, so we can see which tests forget to
52 * cleanup.
54 static char *get_tmp_template(int linenumber)
56 const char *tmp = getenv("TMPDIR");
57 static char template[1024];
58 snprintf(template, sizeof(template) - 1, "%s/stack_test-%d.XXXXXX",
59 tmp ? tmp : "/tmp", linenumber);
60 return template;
63 static char *get_tmp_dir(int linenumber)
65 char *dir = get_tmp_template(linenumber);
66 EXPECT(mkdtemp(dir));
67 return dir;
70 static void test_read_file(void)
72 char *fn = get_tmp_template(__LINE__);
73 int fd = mkstemp(fn);
74 char out[1024] = "line1\n\nline2\nline3";
75 int n, err;
76 char **names = NULL;
77 char *want[] = { "line1", "line2", "line3" };
78 int i = 0;
80 EXPECT(fd > 0);
81 n = write(fd, out, strlen(out));
82 EXPECT(n == strlen(out));
83 err = close(fd);
84 EXPECT(err >= 0);
86 err = read_lines(fn, &names);
87 EXPECT_ERR(err);
89 for (i = 0; names[i]; i++) {
90 EXPECT(0 == strcmp(want[i], names[i]));
92 free_names(names);
93 (void) remove(fn);
96 static void test_parse_names(void)
98 char buf[] = "line\n";
99 char **names = NULL;
100 parse_names(buf, strlen(buf), &names);
102 EXPECT(NULL != names[0]);
103 EXPECT(0 == strcmp(names[0], "line"));
104 EXPECT(NULL == names[1]);
105 free_names(names);
108 static void test_names_equal(void)
110 char *a[] = { "a", "b", "c", NULL };
111 char *b[] = { "a", "b", "d", NULL };
112 char *c[] = { "a", "b", NULL };
114 EXPECT(names_equal(a, a));
115 EXPECT(!names_equal(a, b));
116 EXPECT(!names_equal(a, c));
119 static int write_test_ref(struct reftable_writer *wr, void *arg)
121 struct reftable_ref_record *ref = arg;
122 reftable_writer_set_limits(wr, ref->update_index, ref->update_index);
123 return reftable_writer_add_ref(wr, ref);
126 struct write_log_arg {
127 struct reftable_log_record *log;
128 uint64_t update_index;
131 static int write_test_log(struct reftable_writer *wr, void *arg)
133 struct write_log_arg *wla = arg;
135 reftable_writer_set_limits(wr, wla->update_index, wla->update_index);
136 return reftable_writer_add_log(wr, wla->log);
139 static void test_reftable_stack_add_one(void)
141 char *dir = get_tmp_dir(__LINE__);
142 struct strbuf scratch = STRBUF_INIT;
143 int mask = umask(002);
144 struct reftable_write_options cfg = {
145 .default_permissions = 0660,
147 struct reftable_stack *st = NULL;
148 int err;
149 struct reftable_ref_record ref = {
150 .refname = "HEAD",
151 .update_index = 1,
152 .value_type = REFTABLE_REF_SYMREF,
153 .value.symref = "master",
155 struct reftable_ref_record dest = { NULL };
156 struct stat stat_result = { 0 };
157 err = reftable_new_stack(&st, dir, cfg);
158 EXPECT_ERR(err);
160 err = reftable_stack_add(st, &write_test_ref, &ref);
161 EXPECT_ERR(err);
163 err = reftable_stack_read_ref(st, ref.refname, &dest);
164 EXPECT_ERR(err);
165 EXPECT(0 == strcmp("master", dest.value.symref));
166 EXPECT(st->readers_len > 0);
168 printf("testing print functionality:\n");
169 err = reftable_stack_print_directory(dir, GIT_SHA1_FORMAT_ID);
170 EXPECT_ERR(err);
172 err = reftable_stack_print_directory(dir, GIT_SHA256_FORMAT_ID);
173 EXPECT(err == REFTABLE_FORMAT_ERROR);
175 #ifndef GIT_WINDOWS_NATIVE
176 strbuf_addstr(&scratch, dir);
177 strbuf_addstr(&scratch, "/tables.list");
178 err = stat(scratch.buf, &stat_result);
179 EXPECT(!err);
180 EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
182 strbuf_reset(&scratch);
183 strbuf_addstr(&scratch, dir);
184 strbuf_addstr(&scratch, "/");
185 /* do not try at home; not an external API for reftable. */
186 strbuf_addstr(&scratch, st->readers[0]->name);
187 err = stat(scratch.buf, &stat_result);
188 EXPECT(!err);
189 EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
190 #else
191 (void) stat_result;
192 #endif
194 reftable_ref_record_release(&dest);
195 reftable_stack_destroy(st);
196 strbuf_release(&scratch);
197 clear_dir(dir);
198 umask(mask);
201 static void test_reftable_stack_uptodate(void)
203 struct reftable_write_options cfg = { 0 };
204 struct reftable_stack *st1 = NULL;
205 struct reftable_stack *st2 = NULL;
206 char *dir = get_tmp_dir(__LINE__);
208 int err;
209 struct reftable_ref_record ref1 = {
210 .refname = "HEAD",
211 .update_index = 1,
212 .value_type = REFTABLE_REF_SYMREF,
213 .value.symref = "master",
215 struct reftable_ref_record ref2 = {
216 .refname = "branch2",
217 .update_index = 2,
218 .value_type = REFTABLE_REF_SYMREF,
219 .value.symref = "master",
223 /* simulate multi-process access to the same stack
224 by creating two stacks for the same directory.
226 err = reftable_new_stack(&st1, dir, cfg);
227 EXPECT_ERR(err);
229 err = reftable_new_stack(&st2, dir, cfg);
230 EXPECT_ERR(err);
232 err = reftable_stack_add(st1, &write_test_ref, &ref1);
233 EXPECT_ERR(err);
235 err = reftable_stack_add(st2, &write_test_ref, &ref2);
236 EXPECT(err == REFTABLE_LOCK_ERROR);
238 err = reftable_stack_reload(st2);
239 EXPECT_ERR(err);
241 err = reftable_stack_add(st2, &write_test_ref, &ref2);
242 EXPECT_ERR(err);
243 reftable_stack_destroy(st1);
244 reftable_stack_destroy(st2);
245 clear_dir(dir);
248 static void test_reftable_stack_transaction_api(void)
250 char *dir = get_tmp_dir(__LINE__);
252 struct reftable_write_options cfg = { 0 };
253 struct reftable_stack *st = NULL;
254 int err;
255 struct reftable_addition *add = NULL;
257 struct reftable_ref_record ref = {
258 .refname = "HEAD",
259 .update_index = 1,
260 .value_type = REFTABLE_REF_SYMREF,
261 .value.symref = "master",
263 struct reftable_ref_record dest = { NULL };
266 err = reftable_new_stack(&st, dir, cfg);
267 EXPECT_ERR(err);
269 reftable_addition_destroy(add);
271 err = reftable_stack_new_addition(&add, st);
272 EXPECT_ERR(err);
274 err = reftable_addition_add(add, &write_test_ref, &ref);
275 EXPECT_ERR(err);
277 err = reftable_addition_commit(add);
278 EXPECT_ERR(err);
280 reftable_addition_destroy(add);
282 err = reftable_stack_read_ref(st, ref.refname, &dest);
283 EXPECT_ERR(err);
284 EXPECT(REFTABLE_REF_SYMREF == dest.value_type);
285 EXPECT(0 == strcmp("master", dest.value.symref));
287 reftable_ref_record_release(&dest);
288 reftable_stack_destroy(st);
289 clear_dir(dir);
292 static void test_reftable_stack_validate_refname(void)
294 struct reftable_write_options cfg = { 0 };
295 struct reftable_stack *st = NULL;
296 int err;
297 char *dir = get_tmp_dir(__LINE__);
299 int i;
300 struct reftable_ref_record ref = {
301 .refname = "a/b",
302 .update_index = 1,
303 .value_type = REFTABLE_REF_SYMREF,
304 .value.symref = "master",
306 char *additions[] = { "a", "a/b/c" };
308 err = reftable_new_stack(&st, dir, cfg);
309 EXPECT_ERR(err);
311 err = reftable_stack_add(st, &write_test_ref, &ref);
312 EXPECT_ERR(err);
314 for (i = 0; i < ARRAY_SIZE(additions); i++) {
315 struct reftable_ref_record ref = {
316 .refname = additions[i],
317 .update_index = 1,
318 .value_type = REFTABLE_REF_SYMREF,
319 .value.symref = "master",
322 err = reftable_stack_add(st, &write_test_ref, &ref);
323 EXPECT(err == REFTABLE_NAME_CONFLICT);
326 reftable_stack_destroy(st);
327 clear_dir(dir);
330 static int write_error(struct reftable_writer *wr, void *arg)
332 return *((int *)arg);
335 static void test_reftable_stack_update_index_check(void)
337 char *dir = get_tmp_dir(__LINE__);
339 struct reftable_write_options cfg = { 0 };
340 struct reftable_stack *st = NULL;
341 int err;
342 struct reftable_ref_record ref1 = {
343 .refname = "name1",
344 .update_index = 1,
345 .value_type = REFTABLE_REF_SYMREF,
346 .value.symref = "master",
348 struct reftable_ref_record ref2 = {
349 .refname = "name2",
350 .update_index = 1,
351 .value_type = REFTABLE_REF_SYMREF,
352 .value.symref = "master",
355 err = reftable_new_stack(&st, dir, cfg);
356 EXPECT_ERR(err);
358 err = reftable_stack_add(st, &write_test_ref, &ref1);
359 EXPECT_ERR(err);
361 err = reftable_stack_add(st, &write_test_ref, &ref2);
362 EXPECT(err == REFTABLE_API_ERROR);
363 reftable_stack_destroy(st);
364 clear_dir(dir);
367 static void test_reftable_stack_lock_failure(void)
369 char *dir = get_tmp_dir(__LINE__);
371 struct reftable_write_options cfg = { 0 };
372 struct reftable_stack *st = NULL;
373 int err, i;
375 err = reftable_new_stack(&st, dir, cfg);
376 EXPECT_ERR(err);
377 for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) {
378 err = reftable_stack_add(st, &write_error, &i);
379 EXPECT(err == i);
382 reftable_stack_destroy(st);
383 clear_dir(dir);
386 static void test_reftable_stack_add(void)
388 int i = 0;
389 int err = 0;
390 struct reftable_write_options cfg = {
391 .exact_log_message = 1,
393 struct reftable_stack *st = NULL;
394 char *dir = get_tmp_dir(__LINE__);
396 struct reftable_ref_record refs[2] = { { NULL } };
397 struct reftable_log_record logs[2] = { { NULL } };
398 int N = ARRAY_SIZE(refs);
401 err = reftable_new_stack(&st, dir, cfg);
402 EXPECT_ERR(err);
403 st->disable_auto_compact = 1;
405 for (i = 0; i < N; i++) {
406 char buf[256];
407 snprintf(buf, sizeof(buf), "branch%02d", i);
408 refs[i].refname = xstrdup(buf);
409 refs[i].update_index = i + 1;
410 refs[i].value_type = REFTABLE_REF_VAL1;
411 refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
412 set_test_hash(refs[i].value.val1, i);
414 logs[i].refname = xstrdup(buf);
415 logs[i].update_index = N + i + 1;
416 logs[i].value_type = REFTABLE_LOG_UPDATE;
418 logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
419 logs[i].value.update.email = xstrdup("identity@invalid");
420 set_test_hash(logs[i].value.update.new_hash, i);
423 for (i = 0; i < N; i++) {
424 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
425 EXPECT_ERR(err);
428 for (i = 0; i < N; i++) {
429 struct write_log_arg arg = {
430 .log = &logs[i],
431 .update_index = reftable_stack_next_update_index(st),
433 int err = reftable_stack_add(st, &write_test_log, &arg);
434 EXPECT_ERR(err);
437 err = reftable_stack_compact_all(st, NULL);
438 EXPECT_ERR(err);
440 for (i = 0; i < N; i++) {
441 struct reftable_ref_record dest = { NULL };
443 int err = reftable_stack_read_ref(st, refs[i].refname, &dest);
444 EXPECT_ERR(err);
445 EXPECT(reftable_ref_record_equal(&dest, refs + i,
446 GIT_SHA1_RAWSZ));
447 reftable_ref_record_release(&dest);
450 for (i = 0; i < N; i++) {
451 struct reftable_log_record dest = { NULL };
452 int err = reftable_stack_read_log(st, refs[i].refname, &dest);
453 EXPECT_ERR(err);
454 EXPECT(reftable_log_record_equal(&dest, logs + i,
455 GIT_SHA1_RAWSZ));
456 reftable_log_record_release(&dest);
459 /* cleanup */
460 reftable_stack_destroy(st);
461 for (i = 0; i < N; i++) {
462 reftable_ref_record_release(&refs[i]);
463 reftable_log_record_release(&logs[i]);
465 clear_dir(dir);
468 static void test_reftable_stack_log_normalize(void)
470 int err = 0;
471 struct reftable_write_options cfg = {
474 struct reftable_stack *st = NULL;
475 char *dir = get_tmp_dir(__LINE__);
477 uint8_t h1[GIT_SHA1_RAWSZ] = { 0x01 }, h2[GIT_SHA1_RAWSZ] = { 0x02 };
479 struct reftable_log_record input = { .refname = "branch",
480 .update_index = 1,
481 .value_type = REFTABLE_LOG_UPDATE,
482 .value = { .update = {
483 .new_hash = h1,
484 .old_hash = h2,
485 } } };
486 struct reftable_log_record dest = {
487 .update_index = 0,
489 struct write_log_arg arg = {
490 .log = &input,
491 .update_index = 1,
494 err = reftable_new_stack(&st, dir, cfg);
495 EXPECT_ERR(err);
497 input.value.update.message = "one\ntwo";
498 err = reftable_stack_add(st, &write_test_log, &arg);
499 EXPECT(err == REFTABLE_API_ERROR);
501 input.value.update.message = "one";
502 err = reftable_stack_add(st, &write_test_log, &arg);
503 EXPECT_ERR(err);
505 err = reftable_stack_read_log(st, input.refname, &dest);
506 EXPECT_ERR(err);
507 EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
509 input.value.update.message = "two\n";
510 arg.update_index = 2;
511 err = reftable_stack_add(st, &write_test_log, &arg);
512 EXPECT_ERR(err);
513 err = reftable_stack_read_log(st, input.refname, &dest);
514 EXPECT_ERR(err);
515 EXPECT(0 == strcmp(dest.value.update.message, "two\n"));
517 /* cleanup */
518 reftable_stack_destroy(st);
519 reftable_log_record_release(&dest);
520 clear_dir(dir);
523 static void test_reftable_stack_tombstone(void)
525 int i = 0;
526 char *dir = get_tmp_dir(__LINE__);
528 struct reftable_write_options cfg = { 0 };
529 struct reftable_stack *st = NULL;
530 int err;
531 struct reftable_ref_record refs[2] = { { NULL } };
532 struct reftable_log_record logs[2] = { { NULL } };
533 int N = ARRAY_SIZE(refs);
534 struct reftable_ref_record dest = { NULL };
535 struct reftable_log_record log_dest = { NULL };
538 err = reftable_new_stack(&st, dir, cfg);
539 EXPECT_ERR(err);
541 /* even entries add the refs, odd entries delete them. */
542 for (i = 0; i < N; i++) {
543 const char *buf = "branch";
544 refs[i].refname = xstrdup(buf);
545 refs[i].update_index = i + 1;
546 if (i % 2 == 0) {
547 refs[i].value_type = REFTABLE_REF_VAL1;
548 refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
549 set_test_hash(refs[i].value.val1, i);
552 logs[i].refname = xstrdup(buf);
553 /* update_index is part of the key. */
554 logs[i].update_index = 42;
555 if (i % 2 == 0) {
556 logs[i].value_type = REFTABLE_LOG_UPDATE;
557 logs[i].value.update.new_hash =
558 reftable_malloc(GIT_SHA1_RAWSZ);
559 set_test_hash(logs[i].value.update.new_hash, i);
560 logs[i].value.update.email =
561 xstrdup("identity@invalid");
564 for (i = 0; i < N; i++) {
565 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
566 EXPECT_ERR(err);
569 for (i = 0; i < N; i++) {
570 struct write_log_arg arg = {
571 .log = &logs[i],
572 .update_index = reftable_stack_next_update_index(st),
574 int err = reftable_stack_add(st, &write_test_log, &arg);
575 EXPECT_ERR(err);
578 err = reftable_stack_read_ref(st, "branch", &dest);
579 EXPECT(err == 1);
580 reftable_ref_record_release(&dest);
582 err = reftable_stack_read_log(st, "branch", &log_dest);
583 EXPECT(err == 1);
584 reftable_log_record_release(&log_dest);
586 err = reftable_stack_compact_all(st, NULL);
587 EXPECT_ERR(err);
589 err = reftable_stack_read_ref(st, "branch", &dest);
590 EXPECT(err == 1);
592 err = reftable_stack_read_log(st, "branch", &log_dest);
593 EXPECT(err == 1);
594 reftable_ref_record_release(&dest);
595 reftable_log_record_release(&log_dest);
597 /* cleanup */
598 reftable_stack_destroy(st);
599 for (i = 0; i < N; i++) {
600 reftable_ref_record_release(&refs[i]);
601 reftable_log_record_release(&logs[i]);
603 clear_dir(dir);
606 static void test_reftable_stack_hash_id(void)
608 char *dir = get_tmp_dir(__LINE__);
610 struct reftable_write_options cfg = { 0 };
611 struct reftable_stack *st = NULL;
612 int err;
614 struct reftable_ref_record ref = {
615 .refname = "master",
616 .value_type = REFTABLE_REF_SYMREF,
617 .value.symref = "target",
618 .update_index = 1,
620 struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
621 struct reftable_stack *st32 = NULL;
622 struct reftable_write_options cfg_default = { 0 };
623 struct reftable_stack *st_default = NULL;
624 struct reftable_ref_record dest = { NULL };
626 err = reftable_new_stack(&st, dir, cfg);
627 EXPECT_ERR(err);
629 err = reftable_stack_add(st, &write_test_ref, &ref);
630 EXPECT_ERR(err);
632 /* can't read it with the wrong hash ID. */
633 err = reftable_new_stack(&st32, dir, cfg32);
634 EXPECT(err == REFTABLE_FORMAT_ERROR);
636 /* check that we can read it back with default config too. */
637 err = reftable_new_stack(&st_default, dir, cfg_default);
638 EXPECT_ERR(err);
640 err = reftable_stack_read_ref(st_default, "master", &dest);
641 EXPECT_ERR(err);
643 EXPECT(reftable_ref_record_equal(&ref, &dest, GIT_SHA1_RAWSZ));
644 reftable_ref_record_release(&dest);
645 reftable_stack_destroy(st);
646 reftable_stack_destroy(st_default);
647 clear_dir(dir);
650 static void test_log2(void)
652 EXPECT(1 == fastlog2(3));
653 EXPECT(2 == fastlog2(4));
654 EXPECT(2 == fastlog2(5));
657 static void test_sizes_to_segments(void)
659 uint64_t sizes[] = { 2, 3, 4, 5, 7, 9 };
660 /* .................0 1 2 3 4 5 */
662 int seglen = 0;
663 struct segment *segs =
664 sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
665 EXPECT(segs[2].log == 3);
666 EXPECT(segs[2].start == 5);
667 EXPECT(segs[2].end == 6);
669 EXPECT(segs[1].log == 2);
670 EXPECT(segs[1].start == 2);
671 EXPECT(segs[1].end == 5);
672 reftable_free(segs);
675 static void test_sizes_to_segments_empty(void)
677 int seglen = 0;
678 struct segment *segs = sizes_to_segments(&seglen, NULL, 0);
679 EXPECT(seglen == 0);
680 reftable_free(segs);
683 static void test_sizes_to_segments_all_equal(void)
685 uint64_t sizes[] = { 5, 5 };
687 int seglen = 0;
688 struct segment *segs =
689 sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
690 EXPECT(seglen == 1);
691 EXPECT(segs[0].start == 0);
692 EXPECT(segs[0].end == 2);
693 reftable_free(segs);
696 static void test_suggest_compaction_segment(void)
698 uint64_t sizes[] = { 128, 64, 17, 16, 9, 9, 9, 16, 16 };
699 /* .................0 1 2 3 4 5 6 */
700 struct segment min =
701 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
702 EXPECT(min.start == 2);
703 EXPECT(min.end == 7);
706 static void test_suggest_compaction_segment_nothing(void)
708 uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
709 struct segment result =
710 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
711 EXPECT(result.start == result.end);
714 static void test_reflog_expire(void)
716 char *dir = get_tmp_dir(__LINE__);
718 struct reftable_write_options cfg = { 0 };
719 struct reftable_stack *st = NULL;
720 struct reftable_log_record logs[20] = { { NULL } };
721 int N = ARRAY_SIZE(logs) - 1;
722 int i = 0;
723 int err;
724 struct reftable_log_expiry_config expiry = {
725 .time = 10,
727 struct reftable_log_record log = { NULL };
730 err = reftable_new_stack(&st, dir, cfg);
731 EXPECT_ERR(err);
733 for (i = 1; i <= N; i++) {
734 char buf[256];
735 snprintf(buf, sizeof(buf), "branch%02d", i);
737 logs[i].refname = xstrdup(buf);
738 logs[i].update_index = i;
739 logs[i].value_type = REFTABLE_LOG_UPDATE;
740 logs[i].value.update.time = i;
741 logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
742 logs[i].value.update.email = xstrdup("identity@invalid");
743 set_test_hash(logs[i].value.update.new_hash, i);
746 for (i = 1; i <= N; i++) {
747 struct write_log_arg arg = {
748 .log = &logs[i],
749 .update_index = reftable_stack_next_update_index(st),
751 int err = reftable_stack_add(st, &write_test_log, &arg);
752 EXPECT_ERR(err);
755 err = reftable_stack_compact_all(st, NULL);
756 EXPECT_ERR(err);
758 err = reftable_stack_compact_all(st, &expiry);
759 EXPECT_ERR(err);
761 err = reftable_stack_read_log(st, logs[9].refname, &log);
762 EXPECT(err == 1);
764 err = reftable_stack_read_log(st, logs[11].refname, &log);
765 EXPECT_ERR(err);
767 expiry.min_update_index = 15;
768 err = reftable_stack_compact_all(st, &expiry);
769 EXPECT_ERR(err);
771 err = reftable_stack_read_log(st, logs[14].refname, &log);
772 EXPECT(err == 1);
774 err = reftable_stack_read_log(st, logs[16].refname, &log);
775 EXPECT_ERR(err);
777 /* cleanup */
778 reftable_stack_destroy(st);
779 for (i = 0; i <= N; i++) {
780 reftable_log_record_release(&logs[i]);
782 clear_dir(dir);
783 reftable_log_record_release(&log);
786 static int write_nothing(struct reftable_writer *wr, void *arg)
788 reftable_writer_set_limits(wr, 1, 1);
789 return 0;
792 static void test_empty_add(void)
794 struct reftable_write_options cfg = { 0 };
795 struct reftable_stack *st = NULL;
796 int err;
797 char *dir = get_tmp_dir(__LINE__);
799 struct reftable_stack *st2 = NULL;
802 err = reftable_new_stack(&st, dir, cfg);
803 EXPECT_ERR(err);
805 err = reftable_stack_add(st, &write_nothing, NULL);
806 EXPECT_ERR(err);
808 err = reftable_new_stack(&st2, dir, cfg);
809 EXPECT_ERR(err);
810 clear_dir(dir);
811 reftable_stack_destroy(st);
812 reftable_stack_destroy(st2);
815 static void test_reftable_stack_auto_compaction(void)
817 struct reftable_write_options cfg = { 0 };
818 struct reftable_stack *st = NULL;
819 char *dir = get_tmp_dir(__LINE__);
821 int err, i;
822 int N = 100;
824 err = reftable_new_stack(&st, dir, cfg);
825 EXPECT_ERR(err);
827 st->disable_auto_compact = 1; /* call manually below for coverage. */
828 for (i = 0; i < N; i++) {
829 char name[100];
830 struct reftable_ref_record ref = {
831 .refname = name,
832 .update_index = reftable_stack_next_update_index(st),
833 .value_type = REFTABLE_REF_SYMREF,
834 .value.symref = "master",
836 snprintf(name, sizeof(name), "branch%04d", i);
838 err = reftable_stack_add(st, &write_test_ref, &ref);
839 EXPECT_ERR(err);
841 err = reftable_stack_auto_compact(st);
842 EXPECT_ERR(err);
843 EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
846 EXPECT(reftable_stack_compaction_stats(st)->entries_written <
847 (uint64_t)(N * fastlog2(N)));
849 reftable_stack_destroy(st);
850 clear_dir(dir);
853 static void test_reftable_stack_compaction_concurrent(void)
855 struct reftable_write_options cfg = { 0 };
856 struct reftable_stack *st1 = NULL, *st2 = NULL;
857 char *dir = get_tmp_dir(__LINE__);
859 int err, i;
860 int N = 3;
862 err = reftable_new_stack(&st1, dir, cfg);
863 EXPECT_ERR(err);
865 for (i = 0; i < N; i++) {
866 char name[100];
867 struct reftable_ref_record ref = {
868 .refname = name,
869 .update_index = reftable_stack_next_update_index(st1),
870 .value_type = REFTABLE_REF_SYMREF,
871 .value.symref = "master",
873 snprintf(name, sizeof(name), "branch%04d", i);
875 err = reftable_stack_add(st1, &write_test_ref, &ref);
876 EXPECT_ERR(err);
879 err = reftable_new_stack(&st2, dir, cfg);
880 EXPECT_ERR(err);
882 err = reftable_stack_compact_all(st1, NULL);
883 EXPECT_ERR(err);
885 reftable_stack_destroy(st1);
886 reftable_stack_destroy(st2);
888 EXPECT(count_dir_entries(dir) == 2);
889 clear_dir(dir);
892 static void unclean_stack_close(struct reftable_stack *st)
894 /* break abstraction boundary to simulate unclean shutdown. */
895 int i = 0;
896 for (; i < st->readers_len; i++) {
897 reftable_reader_free(st->readers[i]);
899 st->readers_len = 0;
900 FREE_AND_NULL(st->readers);
903 static void test_reftable_stack_compaction_concurrent_clean(void)
905 struct reftable_write_options cfg = { 0 };
906 struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
907 char *dir = get_tmp_dir(__LINE__);
909 int err, i;
910 int N = 3;
912 err = reftable_new_stack(&st1, dir, cfg);
913 EXPECT_ERR(err);
915 for (i = 0; i < N; i++) {
916 char name[100];
917 struct reftable_ref_record ref = {
918 .refname = name,
919 .update_index = reftable_stack_next_update_index(st1),
920 .value_type = REFTABLE_REF_SYMREF,
921 .value.symref = "master",
923 snprintf(name, sizeof(name), "branch%04d", i);
925 err = reftable_stack_add(st1, &write_test_ref, &ref);
926 EXPECT_ERR(err);
929 err = reftable_new_stack(&st2, dir, cfg);
930 EXPECT_ERR(err);
932 err = reftable_stack_compact_all(st1, NULL);
933 EXPECT_ERR(err);
935 unclean_stack_close(st1);
936 unclean_stack_close(st2);
938 err = reftable_new_stack(&st3, dir, cfg);
939 EXPECT_ERR(err);
941 err = reftable_stack_clean(st3);
942 EXPECT_ERR(err);
943 EXPECT(count_dir_entries(dir) == 2);
945 reftable_stack_destroy(st1);
946 reftable_stack_destroy(st2);
947 reftable_stack_destroy(st3);
949 clear_dir(dir);
952 int stack_test_main(int argc, const char *argv[])
954 RUN_TEST(test_empty_add);
955 RUN_TEST(test_log2);
956 RUN_TEST(test_names_equal);
957 RUN_TEST(test_parse_names);
958 RUN_TEST(test_read_file);
959 RUN_TEST(test_reflog_expire);
960 RUN_TEST(test_reftable_stack_add);
961 RUN_TEST(test_reftable_stack_add_one);
962 RUN_TEST(test_reftable_stack_auto_compaction);
963 RUN_TEST(test_reftable_stack_compaction_concurrent);
964 RUN_TEST(test_reftable_stack_compaction_concurrent_clean);
965 RUN_TEST(test_reftable_stack_hash_id);
966 RUN_TEST(test_reftable_stack_lock_failure);
967 RUN_TEST(test_reftable_stack_log_normalize);
968 RUN_TEST(test_reftable_stack_tombstone);
969 RUN_TEST(test_reftable_stack_transaction_api);
970 RUN_TEST(test_reftable_stack_update_index_check);
971 RUN_TEST(test_reftable_stack_uptodate);
972 RUN_TEST(test_reftable_stack_validate_refname);
973 RUN_TEST(test_sizes_to_segments);
974 RUN_TEST(test_sizes_to_segments_all_equal);
975 RUN_TEST(test_sizes_to_segments_empty);
976 RUN_TEST(test_suggest_compaction_segment);
977 RUN_TEST(test_suggest_compaction_segment_nothing);
978 return 0;