Merge branch 'rj/launch-editor-error-message'
[alt-git.git] / reftable / stack_test.c
blob1df3ffce5261f2cf63c3719198e0e2d71df13bb8
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 void test_reftable_stack_validate_refname(void)
401 struct reftable_write_options cfg = { 0 };
402 struct reftable_stack *st = NULL;
403 int err;
404 char *dir = get_tmp_dir(__LINE__);
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",
413 char *additions[] = { "a", "a/b/c" };
415 err = reftable_new_stack(&st, dir, cfg);
416 EXPECT_ERR(err);
418 err = reftable_stack_add(st, &write_test_ref, &ref);
419 EXPECT_ERR(err);
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",
429 err = reftable_stack_add(st, &write_test_ref, &ref);
430 EXPECT(err == REFTABLE_NAME_CONFLICT);
433 reftable_stack_destroy(st);
434 clear_dir(dir);
437 static int write_error(struct reftable_writer *wr, void *arg)
439 return *((int *)arg);
442 static void test_reftable_stack_update_index_check(void)
444 char *dir = get_tmp_dir(__LINE__);
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",
455 struct reftable_ref_record ref2 = {
456 .refname = "name2",
457 .update_index = 1,
458 .value_type = REFTABLE_REF_SYMREF,
459 .value.symref = "master",
462 err = reftable_new_stack(&st, dir, cfg);
463 EXPECT_ERR(err);
465 err = reftable_stack_add(st, &write_test_ref, &ref1);
466 EXPECT_ERR(err);
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);
474 static void test_reftable_stack_lock_failure(void)
476 char *dir = get_tmp_dir(__LINE__);
478 struct reftable_write_options cfg = { 0 };
479 struct reftable_stack *st = NULL;
480 int err, i;
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);
489 reftable_stack_destroy(st);
490 clear_dir(dir);
493 static void test_reftable_stack_add(void)
495 int i = 0;
496 int err = 0;
497 struct reftable_write_options cfg = {
498 .exact_log_message = 1,
499 .default_permissions = 0660,
500 .disable_auto_compact = 1,
502 struct reftable_stack *st = NULL;
503 char *dir = get_tmp_dir(__LINE__);
504 struct reftable_ref_record refs[2] = { { NULL } };
505 struct reftable_log_record logs[2] = { { NULL } };
506 struct strbuf path = STRBUF_INIT;
507 struct stat stat_result;
508 int N = ARRAY_SIZE(refs);
510 err = reftable_new_stack(&st, dir, cfg);
511 EXPECT_ERR(err);
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);
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);
528 for (i = 0; i < N; i++) {
529 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
530 EXPECT_ERR(err);
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),
538 int err = reftable_stack_add(st, &write_test_log, &arg);
539 EXPECT_ERR(err);
542 err = reftable_stack_compact_all(st, NULL);
543 EXPECT_ERR(err);
545 for (i = 0; i < N; i++) {
546 struct reftable_ref_record dest = { NULL };
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);
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);
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);
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
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]);
589 strbuf_release(&path);
590 clear_dir(dir);
593 static void test_reftable_stack_log_normalize(void)
595 int err = 0;
596 struct reftable_write_options cfg = {
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 },
612 struct reftable_log_record dest = {
613 .update_index = 0,
615 struct write_log_arg arg = {
616 .log = &input,
617 .update_index = 1,
620 err = reftable_new_stack(&st, dir, cfg);
621 EXPECT_ERR(err);
623 input.value.update.message = "one\ntwo";
624 err = reftable_stack_add(st, &write_test_log, &arg);
625 EXPECT(err == REFTABLE_API_ERROR);
627 input.value.update.message = "one";
628 err = reftable_stack_add(st, &write_test_log, &arg);
629 EXPECT_ERR(err);
631 err = reftable_stack_read_log(st, input.refname, &dest);
632 EXPECT_ERR(err);
633 EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
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"));
643 /* cleanup */
644 reftable_stack_destroy(st);
645 reftable_log_record_release(&dest);
646 clear_dir(dir);
649 static void test_reftable_stack_tombstone(void)
651 int i = 0;
652 char *dir = get_tmp_dir(__LINE__);
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 };
664 err = reftable_new_stack(&st, dir, cfg);
665 EXPECT_ERR(err);
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);
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");
687 for (i = 0; i < N; i++) {
688 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
689 EXPECT_ERR(err);
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),
697 int err = reftable_stack_add(st, &write_test_log, &arg);
698 EXPECT_ERR(err);
701 err = reftable_stack_read_ref(st, "branch", &dest);
702 EXPECT(err == 1);
703 reftable_ref_record_release(&dest);
705 err = reftable_stack_read_log(st, "branch", &log_dest);
706 EXPECT(err == 1);
707 reftable_log_record_release(&log_dest);
709 err = reftable_stack_compact_all(st, NULL);
710 EXPECT_ERR(err);
712 err = reftable_stack_read_ref(st, "branch", &dest);
713 EXPECT(err == 1);
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);
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]);
726 clear_dir(dir);
729 static void test_reftable_stack_hash_id(void)
731 char *dir = get_tmp_dir(__LINE__);
733 struct reftable_write_options cfg = { 0 };
734 struct reftable_stack *st = NULL;
735 int err;
737 struct reftable_ref_record ref = {
738 .refname = "master",
739 .value_type = REFTABLE_REF_SYMREF,
740 .value.symref = "target",
741 .update_index = 1,
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 };
749 err = reftable_new_stack(&st, dir, cfg);
750 EXPECT_ERR(err);
752 err = reftable_stack_add(st, &write_test_ref, &ref);
753 EXPECT_ERR(err);
755 /* can't read it with the wrong hash ID. */
756 err = reftable_new_stack(&st32, dir, cfg32);
757 EXPECT(err == REFTABLE_FORMAT_ERROR);
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);
763 err = reftable_stack_read_ref(st_default, "master", &dest);
764 EXPECT_ERR(err);
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);
773 static void test_suggest_compaction_segment(void)
775 uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 };
776 struct segment min =
777 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
778 EXPECT(min.start == 1);
779 EXPECT(min.end == 10);
782 static void test_suggest_compaction_segment_nothing(void)
784 uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
785 struct segment result =
786 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
787 EXPECT(result.start == result.end);
790 static void test_reflog_expire(void)
792 char *dir = get_tmp_dir(__LINE__);
794 struct reftable_write_options cfg = { 0 };
795 struct reftable_stack *st = NULL;
796 struct reftable_log_record logs[20] = { { NULL } };
797 int N = ARRAY_SIZE(logs) - 1;
798 int i = 0;
799 int err;
800 struct reftable_log_expiry_config expiry = {
801 .time = 10,
803 struct reftable_log_record log = { NULL };
806 err = reftable_new_stack(&st, dir, cfg);
807 EXPECT_ERR(err);
809 for (i = 1; i <= N; i++) {
810 char buf[256];
811 snprintf(buf, sizeof(buf), "branch%02d", i);
813 logs[i].refname = xstrdup(buf);
814 logs[i].update_index = i;
815 logs[i].value_type = REFTABLE_LOG_UPDATE;
816 logs[i].value.update.time = i;
817 logs[i].value.update.email = xstrdup("identity@invalid");
818 set_test_hash(logs[i].value.update.new_hash, i);
821 for (i = 1; i <= N; i++) {
822 struct write_log_arg arg = {
823 .log = &logs[i],
824 .update_index = reftable_stack_next_update_index(st),
826 int err = reftable_stack_add(st, &write_test_log, &arg);
827 EXPECT_ERR(err);
830 err = reftable_stack_compact_all(st, NULL);
831 EXPECT_ERR(err);
833 err = reftable_stack_compact_all(st, &expiry);
834 EXPECT_ERR(err);
836 err = reftable_stack_read_log(st, logs[9].refname, &log);
837 EXPECT(err == 1);
839 err = reftable_stack_read_log(st, logs[11].refname, &log);
840 EXPECT_ERR(err);
842 expiry.min_update_index = 15;
843 err = reftable_stack_compact_all(st, &expiry);
844 EXPECT_ERR(err);
846 err = reftable_stack_read_log(st, logs[14].refname, &log);
847 EXPECT(err == 1);
849 err = reftable_stack_read_log(st, logs[16].refname, &log);
850 EXPECT_ERR(err);
852 /* cleanup */
853 reftable_stack_destroy(st);
854 for (i = 0; i <= N; i++) {
855 reftable_log_record_release(&logs[i]);
857 clear_dir(dir);
858 reftable_log_record_release(&log);
861 static int write_nothing(struct reftable_writer *wr, void *arg)
863 reftable_writer_set_limits(wr, 1, 1);
864 return 0;
867 static void test_empty_add(void)
869 struct reftable_write_options cfg = { 0 };
870 struct reftable_stack *st = NULL;
871 int err;
872 char *dir = get_tmp_dir(__LINE__);
874 struct reftable_stack *st2 = NULL;
877 err = reftable_new_stack(&st, dir, cfg);
878 EXPECT_ERR(err);
880 err = reftable_stack_add(st, &write_nothing, NULL);
881 EXPECT_ERR(err);
883 err = reftable_new_stack(&st2, dir, cfg);
884 EXPECT_ERR(err);
885 clear_dir(dir);
886 reftable_stack_destroy(st);
887 reftable_stack_destroy(st2);
890 static int fastlog2(uint64_t sz)
892 int l = 0;
893 if (sz == 0)
894 return 0;
895 for (; sz; sz /= 2)
896 l++;
897 return l - 1;
900 static void test_reftable_stack_auto_compaction(void)
902 struct reftable_write_options cfg = {
903 .disable_auto_compact = 1,
905 struct reftable_stack *st = NULL;
906 char *dir = get_tmp_dir(__LINE__);
908 int err, i;
909 int N = 100;
911 err = reftable_new_stack(&st, dir, cfg);
912 EXPECT_ERR(err);
914 for (i = 0; i < N; i++) {
915 char name[100];
916 struct reftable_ref_record ref = {
917 .refname = name,
918 .update_index = reftable_stack_next_update_index(st),
919 .value_type = REFTABLE_REF_SYMREF,
920 .value.symref = "master",
922 snprintf(name, sizeof(name), "branch%04d", i);
924 err = reftable_stack_add(st, &write_test_ref, &ref);
925 EXPECT_ERR(err);
927 err = reftable_stack_auto_compact(st);
928 EXPECT_ERR(err);
929 EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
932 EXPECT(reftable_stack_compaction_stats(st)->entries_written <
933 (uint64_t)(N * fastlog2(N)));
935 reftable_stack_destroy(st);
936 clear_dir(dir);
939 static void test_reftable_stack_add_performs_auto_compaction(void)
941 struct reftable_write_options cfg = { 0 };
942 struct reftable_stack *st = NULL;
943 struct strbuf refname = STRBUF_INIT;
944 char *dir = get_tmp_dir(__LINE__);
945 int err, i, n = 20;
947 err = reftable_new_stack(&st, dir, cfg);
948 EXPECT_ERR(err);
950 for (i = 0; i <= n; i++) {
951 struct reftable_ref_record ref = {
952 .update_index = reftable_stack_next_update_index(st),
953 .value_type = REFTABLE_REF_SYMREF,
954 .value.symref = "master",
958 * Disable auto-compaction for all but the last runs. Like this
959 * we can ensure that we indeed honor this setting and have
960 * better control over when exactly auto compaction runs.
962 st->config.disable_auto_compact = i != n;
964 strbuf_reset(&refname);
965 strbuf_addf(&refname, "branch-%04d", i);
966 ref.refname = refname.buf;
968 err = reftable_stack_add(st, &write_test_ref, &ref);
969 EXPECT_ERR(err);
972 * The stack length should grow continuously for all runs where
973 * auto compaction is disabled. When enabled, we should merge
974 * all tables in the stack.
976 if (i != n)
977 EXPECT(st->merged->stack_len == i + 1);
978 else
979 EXPECT(st->merged->stack_len == 1);
982 reftable_stack_destroy(st);
983 strbuf_release(&refname);
984 clear_dir(dir);
987 static void test_reftable_stack_compaction_concurrent(void)
989 struct reftable_write_options cfg = { 0 };
990 struct reftable_stack *st1 = NULL, *st2 = NULL;
991 char *dir = get_tmp_dir(__LINE__);
993 int err, i;
994 int N = 3;
996 err = reftable_new_stack(&st1, dir, cfg);
997 EXPECT_ERR(err);
999 for (i = 0; i < N; i++) {
1000 char name[100];
1001 struct reftable_ref_record ref = {
1002 .refname = name,
1003 .update_index = reftable_stack_next_update_index(st1),
1004 .value_type = REFTABLE_REF_SYMREF,
1005 .value.symref = "master",
1007 snprintf(name, sizeof(name), "branch%04d", i);
1009 err = reftable_stack_add(st1, &write_test_ref, &ref);
1010 EXPECT_ERR(err);
1013 err = reftable_new_stack(&st2, dir, cfg);
1014 EXPECT_ERR(err);
1016 err = reftable_stack_compact_all(st1, NULL);
1017 EXPECT_ERR(err);
1019 reftable_stack_destroy(st1);
1020 reftable_stack_destroy(st2);
1022 EXPECT(count_dir_entries(dir) == 2);
1023 clear_dir(dir);
1026 static void unclean_stack_close(struct reftable_stack *st)
1028 /* break abstraction boundary to simulate unclean shutdown. */
1029 int i = 0;
1030 for (; i < st->readers_len; i++) {
1031 reftable_reader_free(st->readers[i]);
1033 st->readers_len = 0;
1034 FREE_AND_NULL(st->readers);
1037 static void test_reftable_stack_compaction_concurrent_clean(void)
1039 struct reftable_write_options cfg = { 0 };
1040 struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
1041 char *dir = get_tmp_dir(__LINE__);
1043 int err, i;
1044 int N = 3;
1046 err = reftable_new_stack(&st1, dir, cfg);
1047 EXPECT_ERR(err);
1049 for (i = 0; i < N; i++) {
1050 char name[100];
1051 struct reftable_ref_record ref = {
1052 .refname = name,
1053 .update_index = reftable_stack_next_update_index(st1),
1054 .value_type = REFTABLE_REF_SYMREF,
1055 .value.symref = "master",
1057 snprintf(name, sizeof(name), "branch%04d", i);
1059 err = reftable_stack_add(st1, &write_test_ref, &ref);
1060 EXPECT_ERR(err);
1063 err = reftable_new_stack(&st2, dir, cfg);
1064 EXPECT_ERR(err);
1066 err = reftable_stack_compact_all(st1, NULL);
1067 EXPECT_ERR(err);
1069 unclean_stack_close(st1);
1070 unclean_stack_close(st2);
1072 err = reftable_new_stack(&st3, dir, cfg);
1073 EXPECT_ERR(err);
1075 err = reftable_stack_clean(st3);
1076 EXPECT_ERR(err);
1077 EXPECT(count_dir_entries(dir) == 2);
1079 reftable_stack_destroy(st1);
1080 reftable_stack_destroy(st2);
1081 reftable_stack_destroy(st3);
1083 clear_dir(dir);
1086 int stack_test_main(int argc, const char *argv[])
1088 RUN_TEST(test_empty_add);
1089 RUN_TEST(test_names_equal);
1090 RUN_TEST(test_parse_names);
1091 RUN_TEST(test_read_file);
1092 RUN_TEST(test_reflog_expire);
1093 RUN_TEST(test_reftable_stack_add);
1094 RUN_TEST(test_reftable_stack_add_one);
1095 RUN_TEST(test_reftable_stack_auto_compaction);
1096 RUN_TEST(test_reftable_stack_add_performs_auto_compaction);
1097 RUN_TEST(test_reftable_stack_compaction_concurrent);
1098 RUN_TEST(test_reftable_stack_compaction_concurrent_clean);
1099 RUN_TEST(test_reftable_stack_hash_id);
1100 RUN_TEST(test_reftable_stack_lock_failure);
1101 RUN_TEST(test_reftable_stack_log_normalize);
1102 RUN_TEST(test_reftable_stack_tombstone);
1103 RUN_TEST(test_reftable_stack_transaction_api);
1104 RUN_TEST(test_reftable_stack_transaction_api_performs_auto_compaction);
1105 RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
1106 RUN_TEST(test_reftable_stack_update_index_check);
1107 RUN_TEST(test_reftable_stack_uptodate);
1108 RUN_TEST(test_reftable_stack_validate_refname);
1109 RUN_TEST(test_suggest_compaction_segment);
1110 RUN_TEST(test_suggest_compaction_segment_nothing);
1111 return 0;