The eighth batch
[alt-git.git] / reftable / stack_test.c
blob7889f818d16ebb96a169bf2b95f4b2dbd071b75a
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 "record.h"
17 #include "test_framework.h"
18 #include "reftable-tests.h"
19 #include "reader.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)
38 return 0;
40 while ((d = readdir(dir))) {
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.
47 * In any case this should be fine as the reftable
48 * library will never write files with leading dots
49 * anyway.
51 if (starts_with(d->d_name, "."))
52 continue;
53 len++;
55 closedir(dir);
56 return len;
60 * Work linenumber into the tempdir, so we can see which tests forget to
61 * cleanup.
63 static char *get_tmp_template(int linenumber)
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;
72 static char *get_tmp_dir(int linenumber)
74 char *dir = get_tmp_template(linenumber);
75 EXPECT(mkdtemp(dir));
76 return dir;
79 static void test_read_file(void)
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;
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);
95 err = read_lines(fn, &names);
96 EXPECT_ERR(err);
98 for (i = 0; names[i]; i++) {
99 EXPECT(0 == strcmp(want[i], names[i]));
101 free_names(names);
102 (void) remove(fn);
105 static void test_parse_names(void)
107 char buf[] = "line\n";
108 char **names = NULL;
109 parse_names(buf, strlen(buf), &names);
111 EXPECT(NULL != names[0]);
112 EXPECT(0 == strcmp(names[0], "line"));
113 EXPECT(NULL == names[1]);
114 free_names(names);
117 static void test_names_equal(void)
119 char *a[] = { "a", "b", "c", NULL };
120 char *b[] = { "a", "b", "d", NULL };
121 char *c[] = { "a", "b", NULL };
123 EXPECT(names_equal(a, a));
124 EXPECT(!names_equal(a, b));
125 EXPECT(!names_equal(a, c));
128 static int write_test_ref(struct reftable_writer *wr, void *arg)
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);
135 struct write_log_arg {
136 struct reftable_log_record *log;
137 uint64_t update_index;
140 static int write_test_log(struct reftable_writer *wr, void *arg)
142 struct write_log_arg *wla = arg;
144 reftable_writer_set_limits(wr, wla->update_index, wla->update_index);
145 return reftable_writer_add_log(wr, wla->log);
148 static void test_reftable_stack_add_one(void)
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,
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",
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);
169 err = reftable_stack_add(st, &write_test_ref, &ref);
170 EXPECT_ERR(err);
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);
177 printf("testing print functionality:\n");
178 err = reftable_stack_print_directory(dir, GIT_SHA1_FORMAT_ID);
179 EXPECT_ERR(err);
181 err = reftable_stack_print_directory(dir, GIT_SHA256_FORMAT_ID);
182 EXPECT(err == REFTABLE_FORMAT_ERROR);
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);
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
203 reftable_ref_record_release(&dest);
204 reftable_stack_destroy(st);
205 strbuf_release(&scratch);
206 clear_dir(dir);
207 umask(mask);
210 static void test_reftable_stack_uptodate(void)
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__);
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",
224 struct reftable_ref_record ref2 = {
225 .refname = "branch2",
226 .update_index = 2,
227 .value_type = REFTABLE_REF_SYMREF,
228 .value.symref = "master",
232 /* simulate multi-process access to the same stack
233 by creating two stacks for the same directory.
235 err = reftable_new_stack(&st1, dir, cfg);
236 EXPECT_ERR(err);
238 err = reftable_new_stack(&st2, dir, cfg);
239 EXPECT_ERR(err);
241 err = reftable_stack_add(st1, &write_test_ref, &ref1);
242 EXPECT_ERR(err);
244 err = reftable_stack_add(st2, &write_test_ref, &ref2);
245 EXPECT(err == REFTABLE_OUTDATED_ERROR);
247 err = reftable_stack_reload(st2);
248 EXPECT_ERR(err);
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);
257 static void test_reftable_stack_transaction_api(void)
259 char *dir = get_tmp_dir(__LINE__);
261 struct reftable_write_options cfg = { 0 };
262 struct reftable_stack *st = NULL;
263 int err;
264 struct reftable_addition *add = NULL;
266 struct reftable_ref_record ref = {
267 .refname = "HEAD",
268 .update_index = 1,
269 .value_type = REFTABLE_REF_SYMREF,
270 .value.symref = "master",
272 struct reftable_ref_record dest = { NULL };
275 err = reftable_new_stack(&st, dir, cfg);
276 EXPECT_ERR(err);
278 reftable_addition_destroy(add);
280 err = reftable_stack_new_addition(&add, st);
281 EXPECT_ERR(err);
283 err = reftable_addition_add(add, &write_test_ref, &ref);
284 EXPECT_ERR(err);
286 err = reftable_addition_commit(add);
287 EXPECT_ERR(err);
289 reftable_addition_destroy(add);
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));
296 reftable_ref_record_release(&dest);
297 reftable_stack_destroy(st);
298 clear_dir(dir);
301 static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
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;
309 err = reftable_new_stack(&st, dir, cfg);
310 EXPECT_ERR(err);
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",
318 char name[100];
320 snprintf(name, sizeof(name), "branch%04d", i);
321 ref.refname = name;
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.
328 st->config.disable_auto_compact = i != n;
330 err = reftable_stack_new_addition(&add, st);
331 EXPECT_ERR(err);
333 err = reftable_addition_add(add, &write_test_ref, &ref);
334 EXPECT_ERR(err);
336 err = reftable_addition_commit(add);
337 EXPECT_ERR(err);
339 reftable_addition_destroy(add);
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.
346 if (i != n)
347 EXPECT(st->merged->stack_len == i + 1);
348 else
349 EXPECT(st->merged->stack_len == 1);
352 reftable_stack_destroy(st);
353 clear_dir(dir);
356 static void test_reftable_stack_auto_compaction_fails_gracefully(void)
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},
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;
370 err = reftable_new_stack(&st, dir, cfg);
371 EXPECT_ERR(err);
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);
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.
384 strbuf_addf(&table_path, "%s/%s.lock", dir, st->readers[0]->name);
385 write_file_buf(table_path.buf, "", 0);
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);
394 reftable_stack_destroy(st);
395 strbuf_release(&table_path);
396 clear_dir(dir);
399 static int write_error(struct reftable_writer *wr, void *arg)
401 return *((int *)arg);
404 static void test_reftable_stack_update_index_check(void)
406 char *dir = get_tmp_dir(__LINE__);
408 struct reftable_write_options cfg = { 0 };
409 struct reftable_stack *st = NULL;
410 int err;
411 struct reftable_ref_record ref1 = {
412 .refname = "name1",
413 .update_index = 1,
414 .value_type = REFTABLE_REF_SYMREF,
415 .value.symref = "master",
417 struct reftable_ref_record ref2 = {
418 .refname = "name2",
419 .update_index = 1,
420 .value_type = REFTABLE_REF_SYMREF,
421 .value.symref = "master",
424 err = reftable_new_stack(&st, dir, cfg);
425 EXPECT_ERR(err);
427 err = reftable_stack_add(st, &write_test_ref, &ref1);
428 EXPECT_ERR(err);
430 err = reftable_stack_add(st, &write_test_ref, &ref2);
431 EXPECT(err == REFTABLE_API_ERROR);
432 reftable_stack_destroy(st);
433 clear_dir(dir);
436 static void test_reftable_stack_lock_failure(void)
438 char *dir = get_tmp_dir(__LINE__);
440 struct reftable_write_options cfg = { 0 };
441 struct reftable_stack *st = NULL;
442 int err, i;
444 err = reftable_new_stack(&st, dir, cfg);
445 EXPECT_ERR(err);
446 for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) {
447 err = reftable_stack_add(st, &write_error, &i);
448 EXPECT(err == i);
451 reftable_stack_destroy(st);
452 clear_dir(dir);
455 static void test_reftable_stack_add(void)
457 int i = 0;
458 int err = 0;
459 struct reftable_write_options cfg = {
460 .exact_log_message = 1,
461 .default_permissions = 0660,
462 .disable_auto_compact = 1,
464 struct reftable_stack *st = NULL;
465 char *dir = get_tmp_dir(__LINE__);
466 struct reftable_ref_record refs[2] = { { NULL } };
467 struct reftable_log_record logs[2] = { { NULL } };
468 struct strbuf path = STRBUF_INIT;
469 struct stat stat_result;
470 int N = ARRAY_SIZE(refs);
472 err = reftable_new_stack(&st, dir, cfg);
473 EXPECT_ERR(err);
475 for (i = 0; i < N; i++) {
476 char buf[256];
477 snprintf(buf, sizeof(buf), "branch%02d", i);
478 refs[i].refname = xstrdup(buf);
479 refs[i].update_index = i + 1;
480 refs[i].value_type = REFTABLE_REF_VAL1;
481 set_test_hash(refs[i].value.val1, i);
483 logs[i].refname = xstrdup(buf);
484 logs[i].update_index = N + i + 1;
485 logs[i].value_type = REFTABLE_LOG_UPDATE;
486 logs[i].value.update.email = xstrdup("identity@invalid");
487 set_test_hash(logs[i].value.update.new_hash, i);
490 for (i = 0; i < N; i++) {
491 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
492 EXPECT_ERR(err);
495 for (i = 0; i < N; i++) {
496 struct write_log_arg arg = {
497 .log = &logs[i],
498 .update_index = reftable_stack_next_update_index(st),
500 int err = reftable_stack_add(st, &write_test_log, &arg);
501 EXPECT_ERR(err);
504 err = reftable_stack_compact_all(st, NULL);
505 EXPECT_ERR(err);
507 for (i = 0; i < N; i++) {
508 struct reftable_ref_record dest = { NULL };
510 int err = reftable_stack_read_ref(st, refs[i].refname, &dest);
511 EXPECT_ERR(err);
512 EXPECT(reftable_ref_record_equal(&dest, refs + i,
513 GIT_SHA1_RAWSZ));
514 reftable_ref_record_release(&dest);
517 for (i = 0; i < N; i++) {
518 struct reftable_log_record dest = { NULL };
519 int err = reftable_stack_read_log(st, refs[i].refname, &dest);
520 EXPECT_ERR(err);
521 EXPECT(reftable_log_record_equal(&dest, logs + i,
522 GIT_SHA1_RAWSZ));
523 reftable_log_record_release(&dest);
526 #ifndef GIT_WINDOWS_NATIVE
527 strbuf_addstr(&path, dir);
528 strbuf_addstr(&path, "/tables.list");
529 err = stat(path.buf, &stat_result);
530 EXPECT(!err);
531 EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
533 strbuf_reset(&path);
534 strbuf_addstr(&path, dir);
535 strbuf_addstr(&path, "/");
536 /* do not try at home; not an external API for reftable. */
537 strbuf_addstr(&path, st->readers[0]->name);
538 err = stat(path.buf, &stat_result);
539 EXPECT(!err);
540 EXPECT((stat_result.st_mode & 0777) == cfg.default_permissions);
541 #else
542 (void) stat_result;
543 #endif
545 /* cleanup */
546 reftable_stack_destroy(st);
547 for (i = 0; i < N; i++) {
548 reftable_ref_record_release(&refs[i]);
549 reftable_log_record_release(&logs[i]);
551 strbuf_release(&path);
552 clear_dir(dir);
555 static void test_reftable_stack_log_normalize(void)
557 int err = 0;
558 struct reftable_write_options cfg = {
561 struct reftable_stack *st = NULL;
562 char *dir = get_tmp_dir(__LINE__);
563 struct reftable_log_record input = {
564 .refname = "branch",
565 .update_index = 1,
566 .value_type = REFTABLE_LOG_UPDATE,
567 .value = {
568 .update = {
569 .new_hash = { 1 },
570 .old_hash = { 2 },
574 struct reftable_log_record dest = {
575 .update_index = 0,
577 struct write_log_arg arg = {
578 .log = &input,
579 .update_index = 1,
582 err = reftable_new_stack(&st, dir, cfg);
583 EXPECT_ERR(err);
585 input.value.update.message = "one\ntwo";
586 err = reftable_stack_add(st, &write_test_log, &arg);
587 EXPECT(err == REFTABLE_API_ERROR);
589 input.value.update.message = "one";
590 err = reftable_stack_add(st, &write_test_log, &arg);
591 EXPECT_ERR(err);
593 err = reftable_stack_read_log(st, input.refname, &dest);
594 EXPECT_ERR(err);
595 EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
597 input.value.update.message = "two\n";
598 arg.update_index = 2;
599 err = reftable_stack_add(st, &write_test_log, &arg);
600 EXPECT_ERR(err);
601 err = reftable_stack_read_log(st, input.refname, &dest);
602 EXPECT_ERR(err);
603 EXPECT(0 == strcmp(dest.value.update.message, "two\n"));
605 /* cleanup */
606 reftable_stack_destroy(st);
607 reftable_log_record_release(&dest);
608 clear_dir(dir);
611 static void test_reftable_stack_tombstone(void)
613 int i = 0;
614 char *dir = get_tmp_dir(__LINE__);
616 struct reftable_write_options cfg = { 0 };
617 struct reftable_stack *st = NULL;
618 int err;
619 struct reftable_ref_record refs[2] = { { NULL } };
620 struct reftable_log_record logs[2] = { { NULL } };
621 int N = ARRAY_SIZE(refs);
622 struct reftable_ref_record dest = { NULL };
623 struct reftable_log_record log_dest = { NULL };
626 err = reftable_new_stack(&st, dir, cfg);
627 EXPECT_ERR(err);
629 /* even entries add the refs, odd entries delete them. */
630 for (i = 0; i < N; i++) {
631 const char *buf = "branch";
632 refs[i].refname = xstrdup(buf);
633 refs[i].update_index = i + 1;
634 if (i % 2 == 0) {
635 refs[i].value_type = REFTABLE_REF_VAL1;
636 set_test_hash(refs[i].value.val1, i);
639 logs[i].refname = xstrdup(buf);
640 /* update_index is part of the key. */
641 logs[i].update_index = 42;
642 if (i % 2 == 0) {
643 logs[i].value_type = REFTABLE_LOG_UPDATE;
644 set_test_hash(logs[i].value.update.new_hash, i);
645 logs[i].value.update.email =
646 xstrdup("identity@invalid");
649 for (i = 0; i < N; i++) {
650 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
651 EXPECT_ERR(err);
654 for (i = 0; i < N; i++) {
655 struct write_log_arg arg = {
656 .log = &logs[i],
657 .update_index = reftable_stack_next_update_index(st),
659 int err = reftable_stack_add(st, &write_test_log, &arg);
660 EXPECT_ERR(err);
663 err = reftable_stack_read_ref(st, "branch", &dest);
664 EXPECT(err == 1);
665 reftable_ref_record_release(&dest);
667 err = reftable_stack_read_log(st, "branch", &log_dest);
668 EXPECT(err == 1);
669 reftable_log_record_release(&log_dest);
671 err = reftable_stack_compact_all(st, NULL);
672 EXPECT_ERR(err);
674 err = reftable_stack_read_ref(st, "branch", &dest);
675 EXPECT(err == 1);
677 err = reftable_stack_read_log(st, "branch", &log_dest);
678 EXPECT(err == 1);
679 reftable_ref_record_release(&dest);
680 reftable_log_record_release(&log_dest);
682 /* cleanup */
683 reftable_stack_destroy(st);
684 for (i = 0; i < N; i++) {
685 reftable_ref_record_release(&refs[i]);
686 reftable_log_record_release(&logs[i]);
688 clear_dir(dir);
691 static void test_reftable_stack_hash_id(void)
693 char *dir = get_tmp_dir(__LINE__);
695 struct reftable_write_options cfg = { 0 };
696 struct reftable_stack *st = NULL;
697 int err;
699 struct reftable_ref_record ref = {
700 .refname = "master",
701 .value_type = REFTABLE_REF_SYMREF,
702 .value.symref = "target",
703 .update_index = 1,
705 struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
706 struct reftable_stack *st32 = NULL;
707 struct reftable_write_options cfg_default = { 0 };
708 struct reftable_stack *st_default = NULL;
709 struct reftable_ref_record dest = { NULL };
711 err = reftable_new_stack(&st, dir, cfg);
712 EXPECT_ERR(err);
714 err = reftable_stack_add(st, &write_test_ref, &ref);
715 EXPECT_ERR(err);
717 /* can't read it with the wrong hash ID. */
718 err = reftable_new_stack(&st32, dir, cfg32);
719 EXPECT(err == REFTABLE_FORMAT_ERROR);
721 /* check that we can read it back with default config too. */
722 err = reftable_new_stack(&st_default, dir, cfg_default);
723 EXPECT_ERR(err);
725 err = reftable_stack_read_ref(st_default, "master", &dest);
726 EXPECT_ERR(err);
728 EXPECT(reftable_ref_record_equal(&ref, &dest, GIT_SHA1_RAWSZ));
729 reftable_ref_record_release(&dest);
730 reftable_stack_destroy(st);
731 reftable_stack_destroy(st_default);
732 clear_dir(dir);
735 static void test_suggest_compaction_segment(void)
737 uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 };
738 struct segment min =
739 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
740 EXPECT(min.start == 1);
741 EXPECT(min.end == 10);
744 static void test_suggest_compaction_segment_nothing(void)
746 uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
747 struct segment result =
748 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
749 EXPECT(result.start == result.end);
752 static void test_reflog_expire(void)
754 char *dir = get_tmp_dir(__LINE__);
756 struct reftable_write_options cfg = { 0 };
757 struct reftable_stack *st = NULL;
758 struct reftable_log_record logs[20] = { { NULL } };
759 int N = ARRAY_SIZE(logs) - 1;
760 int i = 0;
761 int err;
762 struct reftable_log_expiry_config expiry = {
763 .time = 10,
765 struct reftable_log_record log = { NULL };
768 err = reftable_new_stack(&st, dir, cfg);
769 EXPECT_ERR(err);
771 for (i = 1; i <= N; i++) {
772 char buf[256];
773 snprintf(buf, sizeof(buf), "branch%02d", i);
775 logs[i].refname = xstrdup(buf);
776 logs[i].update_index = i;
777 logs[i].value_type = REFTABLE_LOG_UPDATE;
778 logs[i].value.update.time = i;
779 logs[i].value.update.email = xstrdup("identity@invalid");
780 set_test_hash(logs[i].value.update.new_hash, i);
783 for (i = 1; i <= N; i++) {
784 struct write_log_arg arg = {
785 .log = &logs[i],
786 .update_index = reftable_stack_next_update_index(st),
788 int err = reftable_stack_add(st, &write_test_log, &arg);
789 EXPECT_ERR(err);
792 err = reftable_stack_compact_all(st, NULL);
793 EXPECT_ERR(err);
795 err = reftable_stack_compact_all(st, &expiry);
796 EXPECT_ERR(err);
798 err = reftable_stack_read_log(st, logs[9].refname, &log);
799 EXPECT(err == 1);
801 err = reftable_stack_read_log(st, logs[11].refname, &log);
802 EXPECT_ERR(err);
804 expiry.min_update_index = 15;
805 err = reftable_stack_compact_all(st, &expiry);
806 EXPECT_ERR(err);
808 err = reftable_stack_read_log(st, logs[14].refname, &log);
809 EXPECT(err == 1);
811 err = reftable_stack_read_log(st, logs[16].refname, &log);
812 EXPECT_ERR(err);
814 /* cleanup */
815 reftable_stack_destroy(st);
816 for (i = 0; i <= N; i++) {
817 reftable_log_record_release(&logs[i]);
819 clear_dir(dir);
820 reftable_log_record_release(&log);
823 static int write_nothing(struct reftable_writer *wr, void *arg)
825 reftable_writer_set_limits(wr, 1, 1);
826 return 0;
829 static void test_empty_add(void)
831 struct reftable_write_options cfg = { 0 };
832 struct reftable_stack *st = NULL;
833 int err;
834 char *dir = get_tmp_dir(__LINE__);
836 struct reftable_stack *st2 = NULL;
839 err = reftable_new_stack(&st, dir, cfg);
840 EXPECT_ERR(err);
842 err = reftable_stack_add(st, &write_nothing, NULL);
843 EXPECT_ERR(err);
845 err = reftable_new_stack(&st2, dir, cfg);
846 EXPECT_ERR(err);
847 clear_dir(dir);
848 reftable_stack_destroy(st);
849 reftable_stack_destroy(st2);
852 static int fastlog2(uint64_t sz)
854 int l = 0;
855 if (sz == 0)
856 return 0;
857 for (; sz; sz /= 2)
858 l++;
859 return l - 1;
862 static void test_reftable_stack_auto_compaction(void)
864 struct reftable_write_options cfg = {
865 .disable_auto_compact = 1,
867 struct reftable_stack *st = NULL;
868 char *dir = get_tmp_dir(__LINE__);
870 int err, i;
871 int N = 100;
873 err = reftable_new_stack(&st, dir, cfg);
874 EXPECT_ERR(err);
876 for (i = 0; i < N; i++) {
877 char name[100];
878 struct reftable_ref_record ref = {
879 .refname = name,
880 .update_index = reftable_stack_next_update_index(st),
881 .value_type = REFTABLE_REF_SYMREF,
882 .value.symref = "master",
884 snprintf(name, sizeof(name), "branch%04d", i);
886 err = reftable_stack_add(st, &write_test_ref, &ref);
887 EXPECT_ERR(err);
889 err = reftable_stack_auto_compact(st);
890 EXPECT_ERR(err);
891 EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
894 EXPECT(reftable_stack_compaction_stats(st)->entries_written <
895 (uint64_t)(N * fastlog2(N)));
897 reftable_stack_destroy(st);
898 clear_dir(dir);
901 static void test_reftable_stack_add_performs_auto_compaction(void)
903 struct reftable_write_options cfg = { 0 };
904 struct reftable_stack *st = NULL;
905 struct strbuf refname = STRBUF_INIT;
906 char *dir = get_tmp_dir(__LINE__);
907 int err, i, n = 20;
909 err = reftable_new_stack(&st, dir, cfg);
910 EXPECT_ERR(err);
912 for (i = 0; i <= n; i++) {
913 struct reftable_ref_record ref = {
914 .update_index = reftable_stack_next_update_index(st),
915 .value_type = REFTABLE_REF_SYMREF,
916 .value.symref = "master",
920 * Disable auto-compaction for all but the last runs. Like this
921 * we can ensure that we indeed honor this setting and have
922 * better control over when exactly auto compaction runs.
924 st->config.disable_auto_compact = i != n;
926 strbuf_reset(&refname);
927 strbuf_addf(&refname, "branch-%04d", i);
928 ref.refname = refname.buf;
930 err = reftable_stack_add(st, &write_test_ref, &ref);
931 EXPECT_ERR(err);
934 * The stack length should grow continuously for all runs where
935 * auto compaction is disabled. When enabled, we should merge
936 * all tables in the stack.
938 if (i != n)
939 EXPECT(st->merged->stack_len == i + 1);
940 else
941 EXPECT(st->merged->stack_len == 1);
944 reftable_stack_destroy(st);
945 strbuf_release(&refname);
946 clear_dir(dir);
949 static void test_reftable_stack_compaction_concurrent(void)
951 struct reftable_write_options cfg = { 0 };
952 struct reftable_stack *st1 = NULL, *st2 = NULL;
953 char *dir = get_tmp_dir(__LINE__);
955 int err, i;
956 int N = 3;
958 err = reftable_new_stack(&st1, dir, cfg);
959 EXPECT_ERR(err);
961 for (i = 0; i < N; i++) {
962 char name[100];
963 struct reftable_ref_record ref = {
964 .refname = name,
965 .update_index = reftable_stack_next_update_index(st1),
966 .value_type = REFTABLE_REF_SYMREF,
967 .value.symref = "master",
969 snprintf(name, sizeof(name), "branch%04d", i);
971 err = reftable_stack_add(st1, &write_test_ref, &ref);
972 EXPECT_ERR(err);
975 err = reftable_new_stack(&st2, dir, cfg);
976 EXPECT_ERR(err);
978 err = reftable_stack_compact_all(st1, NULL);
979 EXPECT_ERR(err);
981 reftable_stack_destroy(st1);
982 reftable_stack_destroy(st2);
984 EXPECT(count_dir_entries(dir) == 2);
985 clear_dir(dir);
988 static void unclean_stack_close(struct reftable_stack *st)
990 /* break abstraction boundary to simulate unclean shutdown. */
991 int i = 0;
992 for (; i < st->readers_len; i++) {
993 reftable_reader_free(st->readers[i]);
995 st->readers_len = 0;
996 FREE_AND_NULL(st->readers);
999 static void test_reftable_stack_compaction_concurrent_clean(void)
1001 struct reftable_write_options cfg = { 0 };
1002 struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
1003 char *dir = get_tmp_dir(__LINE__);
1005 int err, i;
1006 int N = 3;
1008 err = reftable_new_stack(&st1, dir, cfg);
1009 EXPECT_ERR(err);
1011 for (i = 0; i < N; i++) {
1012 char name[100];
1013 struct reftable_ref_record ref = {
1014 .refname = name,
1015 .update_index = reftable_stack_next_update_index(st1),
1016 .value_type = REFTABLE_REF_SYMREF,
1017 .value.symref = "master",
1019 snprintf(name, sizeof(name), "branch%04d", i);
1021 err = reftable_stack_add(st1, &write_test_ref, &ref);
1022 EXPECT_ERR(err);
1025 err = reftable_new_stack(&st2, dir, cfg);
1026 EXPECT_ERR(err);
1028 err = reftable_stack_compact_all(st1, NULL);
1029 EXPECT_ERR(err);
1031 unclean_stack_close(st1);
1032 unclean_stack_close(st2);
1034 err = reftable_new_stack(&st3, dir, cfg);
1035 EXPECT_ERR(err);
1037 err = reftable_stack_clean(st3);
1038 EXPECT_ERR(err);
1039 EXPECT(count_dir_entries(dir) == 2);
1041 reftable_stack_destroy(st1);
1042 reftable_stack_destroy(st2);
1043 reftable_stack_destroy(st3);
1045 clear_dir(dir);
1048 int stack_test_main(int argc, const char *argv[])
1050 RUN_TEST(test_empty_add);
1051 RUN_TEST(test_names_equal);
1052 RUN_TEST(test_parse_names);
1053 RUN_TEST(test_read_file);
1054 RUN_TEST(test_reflog_expire);
1055 RUN_TEST(test_reftable_stack_add);
1056 RUN_TEST(test_reftable_stack_add_one);
1057 RUN_TEST(test_reftable_stack_auto_compaction);
1058 RUN_TEST(test_reftable_stack_add_performs_auto_compaction);
1059 RUN_TEST(test_reftable_stack_compaction_concurrent);
1060 RUN_TEST(test_reftable_stack_compaction_concurrent_clean);
1061 RUN_TEST(test_reftable_stack_hash_id);
1062 RUN_TEST(test_reftable_stack_lock_failure);
1063 RUN_TEST(test_reftable_stack_log_normalize);
1064 RUN_TEST(test_reftable_stack_tombstone);
1065 RUN_TEST(test_reftable_stack_transaction_api);
1066 RUN_TEST(test_reftable_stack_transaction_api_performs_auto_compaction);
1067 RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
1068 RUN_TEST(test_reftable_stack_update_index_check);
1069 RUN_TEST(test_reftable_stack_uptodate);
1070 RUN_TEST(test_suggest_compaction_segment);
1071 RUN_TEST(test_suggest_compaction_segment_nothing);
1072 return 0;