The sixteenth batch
[alt-git.git] / reftable / stack_test.c
blobe3c11e6a6eebb099629aafa23037de0b10caacba
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 const 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 int write_test_ref(struct reftable_writer *wr, void *arg)
107 struct reftable_ref_record *ref = arg;
108 reftable_writer_set_limits(wr, ref->update_index, ref->update_index);
109 return reftable_writer_add_ref(wr, ref);
112 struct write_log_arg {
113 struct reftable_log_record *log;
114 uint64_t update_index;
117 static int write_test_log(struct reftable_writer *wr, void *arg)
119 struct write_log_arg *wla = arg;
121 reftable_writer_set_limits(wr, wla->update_index, wla->update_index);
122 return reftable_writer_add_log(wr, wla->log);
125 static void test_reftable_stack_add_one(void)
127 char *dir = get_tmp_dir(__LINE__);
128 struct strbuf scratch = STRBUF_INIT;
129 int mask = umask(002);
130 struct reftable_write_options opts = {
131 .default_permissions = 0660,
133 struct reftable_stack *st = NULL;
134 int err;
135 struct reftable_ref_record ref = {
136 .refname = (char *) "HEAD",
137 .update_index = 1,
138 .value_type = REFTABLE_REF_SYMREF,
139 .value.symref = (char *) "master",
141 struct reftable_ref_record dest = { NULL };
142 struct stat stat_result = { 0 };
143 err = reftable_new_stack(&st, dir, &opts);
144 EXPECT_ERR(err);
146 err = reftable_stack_add(st, &write_test_ref, &ref);
147 EXPECT_ERR(err);
149 err = reftable_stack_read_ref(st, ref.refname, &dest);
150 EXPECT_ERR(err);
151 EXPECT(0 == strcmp("master", dest.value.symref));
152 EXPECT(st->readers_len > 0);
154 printf("testing print functionality:\n");
155 err = reftable_stack_print_directory(dir, GIT_SHA1_FORMAT_ID);
156 EXPECT_ERR(err);
158 err = reftable_stack_print_directory(dir, GIT_SHA256_FORMAT_ID);
159 EXPECT(err == REFTABLE_FORMAT_ERROR);
161 #ifndef GIT_WINDOWS_NATIVE
162 strbuf_addstr(&scratch, dir);
163 strbuf_addstr(&scratch, "/tables.list");
164 err = stat(scratch.buf, &stat_result);
165 EXPECT(!err);
166 EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
168 strbuf_reset(&scratch);
169 strbuf_addstr(&scratch, dir);
170 strbuf_addstr(&scratch, "/");
171 /* do not try at home; not an external API for reftable. */
172 strbuf_addstr(&scratch, st->readers[0]->name);
173 err = stat(scratch.buf, &stat_result);
174 EXPECT(!err);
175 EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
176 #else
177 (void) stat_result;
178 #endif
180 reftable_ref_record_release(&dest);
181 reftable_stack_destroy(st);
182 strbuf_release(&scratch);
183 clear_dir(dir);
184 umask(mask);
187 static void test_reftable_stack_uptodate(void)
189 struct reftable_write_options opts = { 0 };
190 struct reftable_stack *st1 = NULL;
191 struct reftable_stack *st2 = NULL;
192 char *dir = get_tmp_dir(__LINE__);
194 int err;
195 struct reftable_ref_record ref1 = {
196 .refname = (char *) "HEAD",
197 .update_index = 1,
198 .value_type = REFTABLE_REF_SYMREF,
199 .value.symref = (char *) "master",
201 struct reftable_ref_record ref2 = {
202 .refname = (char *) "branch2",
203 .update_index = 2,
204 .value_type = REFTABLE_REF_SYMREF,
205 .value.symref = (char *) "master",
209 /* simulate multi-process access to the same stack
210 by creating two stacks for the same directory.
212 err = reftable_new_stack(&st1, dir, &opts);
213 EXPECT_ERR(err);
215 err = reftable_new_stack(&st2, dir, &opts);
216 EXPECT_ERR(err);
218 err = reftable_stack_add(st1, &write_test_ref, &ref1);
219 EXPECT_ERR(err);
221 err = reftable_stack_add(st2, &write_test_ref, &ref2);
222 EXPECT(err == REFTABLE_OUTDATED_ERROR);
224 err = reftable_stack_reload(st2);
225 EXPECT_ERR(err);
227 err = reftable_stack_add(st2, &write_test_ref, &ref2);
228 EXPECT_ERR(err);
229 reftable_stack_destroy(st1);
230 reftable_stack_destroy(st2);
231 clear_dir(dir);
234 static void test_reftable_stack_transaction_api(void)
236 char *dir = get_tmp_dir(__LINE__);
237 struct reftable_write_options opts = { 0 };
238 struct reftable_stack *st = NULL;
239 int err;
240 struct reftable_addition *add = NULL;
242 struct reftable_ref_record ref = {
243 .refname = (char *) "HEAD",
244 .update_index = 1,
245 .value_type = REFTABLE_REF_SYMREF,
246 .value.symref = (char *) "master",
248 struct reftable_ref_record dest = { NULL };
250 err = reftable_new_stack(&st, dir, &opts);
251 EXPECT_ERR(err);
253 reftable_addition_destroy(add);
255 err = reftable_stack_new_addition(&add, st);
256 EXPECT_ERR(err);
258 err = reftable_addition_add(add, &write_test_ref, &ref);
259 EXPECT_ERR(err);
261 err = reftable_addition_commit(add);
262 EXPECT_ERR(err);
264 reftable_addition_destroy(add);
266 err = reftable_stack_read_ref(st, ref.refname, &dest);
267 EXPECT_ERR(err);
268 EXPECT(REFTABLE_REF_SYMREF == dest.value_type);
269 EXPECT(0 == strcmp("master", dest.value.symref));
271 reftable_ref_record_release(&dest);
272 reftable_stack_destroy(st);
273 clear_dir(dir);
276 static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
278 char *dir = get_tmp_dir(__LINE__);
279 struct reftable_write_options opts = {0};
280 struct reftable_addition *add = NULL;
281 struct reftable_stack *st = NULL;
282 int i, n = 20, err;
284 err = reftable_new_stack(&st, dir, &opts);
285 EXPECT_ERR(err);
287 for (i = 0; i <= n; i++) {
288 struct reftable_ref_record ref = {
289 .update_index = reftable_stack_next_update_index(st),
290 .value_type = REFTABLE_REF_SYMREF,
291 .value.symref = (char *) "master",
293 char name[100];
295 snprintf(name, sizeof(name), "branch%04d", i);
296 ref.refname = name;
299 * Disable auto-compaction for all but the last runs. Like this
300 * we can ensure that we indeed honor this setting and have
301 * better control over when exactly auto compaction runs.
303 st->opts.disable_auto_compact = i != n;
305 err = reftable_stack_new_addition(&add, st);
306 EXPECT_ERR(err);
308 err = reftable_addition_add(add, &write_test_ref, &ref);
309 EXPECT_ERR(err);
311 err = reftable_addition_commit(add);
312 EXPECT_ERR(err);
314 reftable_addition_destroy(add);
317 * The stack length should grow continuously for all runs where
318 * auto compaction is disabled. When enabled, we should merge
319 * all tables in the stack.
321 if (i != n)
322 EXPECT(st->merged->stack_len == i + 1);
323 else
324 EXPECT(st->merged->stack_len == 1);
327 reftable_stack_destroy(st);
328 clear_dir(dir);
331 static void test_reftable_stack_auto_compaction_fails_gracefully(void)
333 struct reftable_ref_record ref = {
334 .refname = (char *) "refs/heads/master",
335 .update_index = 1,
336 .value_type = REFTABLE_REF_VAL1,
337 .value.val1 = {0x01},
339 struct reftable_write_options opts = {0};
340 struct reftable_stack *st;
341 struct strbuf table_path = STRBUF_INIT;
342 char *dir = get_tmp_dir(__LINE__);
343 int err;
345 err = reftable_new_stack(&st, dir, &opts);
346 EXPECT_ERR(err);
348 err = reftable_stack_add(st, write_test_ref, &ref);
349 EXPECT_ERR(err);
350 EXPECT(st->merged->stack_len == 1);
351 EXPECT(st->stats.attempts == 0);
352 EXPECT(st->stats.failures == 0);
355 * Lock the newly written table such that it cannot be compacted.
356 * Adding a new table to the stack should not be impacted by this, even
357 * though auto-compaction will now fail.
359 strbuf_addf(&table_path, "%s/%s.lock", dir, st->readers[0]->name);
360 write_file_buf(table_path.buf, "", 0);
362 ref.update_index = 2;
363 err = reftable_stack_add(st, write_test_ref, &ref);
364 EXPECT_ERR(err);
365 EXPECT(st->merged->stack_len == 2);
366 EXPECT(st->stats.attempts == 1);
367 EXPECT(st->stats.failures == 1);
369 reftable_stack_destroy(st);
370 strbuf_release(&table_path);
371 clear_dir(dir);
374 static int write_error(struct reftable_writer *wr, void *arg)
376 return *((int *)arg);
379 static void test_reftable_stack_update_index_check(void)
381 char *dir = get_tmp_dir(__LINE__);
382 struct reftable_write_options opts = { 0 };
383 struct reftable_stack *st = NULL;
384 int err;
385 struct reftable_ref_record ref1 = {
386 .refname = (char *) "name1",
387 .update_index = 1,
388 .value_type = REFTABLE_REF_SYMREF,
389 .value.symref = (char *) "master",
391 struct reftable_ref_record ref2 = {
392 .refname = (char *) "name2",
393 .update_index = 1,
394 .value_type = REFTABLE_REF_SYMREF,
395 .value.symref = (char *) "master",
398 err = reftable_new_stack(&st, dir, &opts);
399 EXPECT_ERR(err);
401 err = reftable_stack_add(st, &write_test_ref, &ref1);
402 EXPECT_ERR(err);
404 err = reftable_stack_add(st, &write_test_ref, &ref2);
405 EXPECT(err == REFTABLE_API_ERROR);
406 reftable_stack_destroy(st);
407 clear_dir(dir);
410 static void test_reftable_stack_lock_failure(void)
412 char *dir = get_tmp_dir(__LINE__);
413 struct reftable_write_options opts = { 0 };
414 struct reftable_stack *st = NULL;
415 int err, i;
417 err = reftable_new_stack(&st, dir, &opts);
418 EXPECT_ERR(err);
419 for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) {
420 err = reftable_stack_add(st, &write_error, &i);
421 EXPECT(err == i);
424 reftable_stack_destroy(st);
425 clear_dir(dir);
428 static void test_reftable_stack_add(void)
430 int i = 0;
431 int err = 0;
432 struct reftable_write_options opts = {
433 .exact_log_message = 1,
434 .default_permissions = 0660,
435 .disable_auto_compact = 1,
437 struct reftable_stack *st = NULL;
438 char *dir = get_tmp_dir(__LINE__);
439 struct reftable_ref_record refs[2] = { { NULL } };
440 struct reftable_log_record logs[2] = { { NULL } };
441 struct strbuf path = STRBUF_INIT;
442 struct stat stat_result;
443 int N = ARRAY_SIZE(refs);
445 err = reftable_new_stack(&st, dir, &opts);
446 EXPECT_ERR(err);
448 for (i = 0; i < N; i++) {
449 char buf[256];
450 snprintf(buf, sizeof(buf), "branch%02d", i);
451 refs[i].refname = xstrdup(buf);
452 refs[i].update_index = i + 1;
453 refs[i].value_type = REFTABLE_REF_VAL1;
454 set_test_hash(refs[i].value.val1, i);
456 logs[i].refname = xstrdup(buf);
457 logs[i].update_index = N + i + 1;
458 logs[i].value_type = REFTABLE_LOG_UPDATE;
459 logs[i].value.update.email = xstrdup("identity@invalid");
460 set_test_hash(logs[i].value.update.new_hash, i);
463 for (i = 0; i < N; i++) {
464 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
465 EXPECT_ERR(err);
468 for (i = 0; i < N; i++) {
469 struct write_log_arg arg = {
470 .log = &logs[i],
471 .update_index = reftable_stack_next_update_index(st),
473 int err = reftable_stack_add(st, &write_test_log, &arg);
474 EXPECT_ERR(err);
477 err = reftable_stack_compact_all(st, NULL);
478 EXPECT_ERR(err);
480 for (i = 0; i < N; i++) {
481 struct reftable_ref_record dest = { NULL };
483 int err = reftable_stack_read_ref(st, refs[i].refname, &dest);
484 EXPECT_ERR(err);
485 EXPECT(reftable_ref_record_equal(&dest, refs + i,
486 GIT_SHA1_RAWSZ));
487 reftable_ref_record_release(&dest);
490 for (i = 0; i < N; i++) {
491 struct reftable_log_record dest = { NULL };
492 int err = reftable_stack_read_log(st, refs[i].refname, &dest);
493 EXPECT_ERR(err);
494 EXPECT(reftable_log_record_equal(&dest, logs + i,
495 GIT_SHA1_RAWSZ));
496 reftable_log_record_release(&dest);
499 #ifndef GIT_WINDOWS_NATIVE
500 strbuf_addstr(&path, dir);
501 strbuf_addstr(&path, "/tables.list");
502 err = stat(path.buf, &stat_result);
503 EXPECT(!err);
504 EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
506 strbuf_reset(&path);
507 strbuf_addstr(&path, dir);
508 strbuf_addstr(&path, "/");
509 /* do not try at home; not an external API for reftable. */
510 strbuf_addstr(&path, st->readers[0]->name);
511 err = stat(path.buf, &stat_result);
512 EXPECT(!err);
513 EXPECT((stat_result.st_mode & 0777) == opts.default_permissions);
514 #else
515 (void) stat_result;
516 #endif
518 /* cleanup */
519 reftable_stack_destroy(st);
520 for (i = 0; i < N; i++) {
521 reftable_ref_record_release(&refs[i]);
522 reftable_log_record_release(&logs[i]);
524 strbuf_release(&path);
525 clear_dir(dir);
528 static void test_reftable_stack_log_normalize(void)
530 int err = 0;
531 struct reftable_write_options opts = {
534 struct reftable_stack *st = NULL;
535 char *dir = get_tmp_dir(__LINE__);
536 struct reftable_log_record input = {
537 .refname = (char *) "branch",
538 .update_index = 1,
539 .value_type = REFTABLE_LOG_UPDATE,
540 .value = {
541 .update = {
542 .new_hash = { 1 },
543 .old_hash = { 2 },
547 struct reftable_log_record dest = {
548 .update_index = 0,
550 struct write_log_arg arg = {
551 .log = &input,
552 .update_index = 1,
555 err = reftable_new_stack(&st, dir, &opts);
556 EXPECT_ERR(err);
558 input.value.update.message = (char *) "one\ntwo";
559 err = reftable_stack_add(st, &write_test_log, &arg);
560 EXPECT(err == REFTABLE_API_ERROR);
562 input.value.update.message = (char *) "one";
563 err = reftable_stack_add(st, &write_test_log, &arg);
564 EXPECT_ERR(err);
566 err = reftable_stack_read_log(st, input.refname, &dest);
567 EXPECT_ERR(err);
568 EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
570 input.value.update.message = (char *) "two\n";
571 arg.update_index = 2;
572 err = reftable_stack_add(st, &write_test_log, &arg);
573 EXPECT_ERR(err);
574 err = reftable_stack_read_log(st, input.refname, &dest);
575 EXPECT_ERR(err);
576 EXPECT(0 == strcmp(dest.value.update.message, "two\n"));
578 /* cleanup */
579 reftable_stack_destroy(st);
580 reftable_log_record_release(&dest);
581 clear_dir(dir);
584 static void test_reftable_stack_tombstone(void)
586 int i = 0;
587 char *dir = get_tmp_dir(__LINE__);
588 struct reftable_write_options opts = { 0 };
589 struct reftable_stack *st = NULL;
590 int err;
591 struct reftable_ref_record refs[2] = { { NULL } };
592 struct reftable_log_record logs[2] = { { NULL } };
593 int N = ARRAY_SIZE(refs);
594 struct reftable_ref_record dest = { NULL };
595 struct reftable_log_record log_dest = { NULL };
597 err = reftable_new_stack(&st, dir, &opts);
598 EXPECT_ERR(err);
600 /* even entries add the refs, odd entries delete them. */
601 for (i = 0; i < N; i++) {
602 const char *buf = "branch";
603 refs[i].refname = xstrdup(buf);
604 refs[i].update_index = i + 1;
605 if (i % 2 == 0) {
606 refs[i].value_type = REFTABLE_REF_VAL1;
607 set_test_hash(refs[i].value.val1, i);
610 logs[i].refname = xstrdup(buf);
611 /* update_index is part of the key. */
612 logs[i].update_index = 42;
613 if (i % 2 == 0) {
614 logs[i].value_type = REFTABLE_LOG_UPDATE;
615 set_test_hash(logs[i].value.update.new_hash, i);
616 logs[i].value.update.email =
617 xstrdup("identity@invalid");
620 for (i = 0; i < N; i++) {
621 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
622 EXPECT_ERR(err);
625 for (i = 0; i < N; i++) {
626 struct write_log_arg arg = {
627 .log = &logs[i],
628 .update_index = reftable_stack_next_update_index(st),
630 int err = reftable_stack_add(st, &write_test_log, &arg);
631 EXPECT_ERR(err);
634 err = reftable_stack_read_ref(st, "branch", &dest);
635 EXPECT(err == 1);
636 reftable_ref_record_release(&dest);
638 err = reftable_stack_read_log(st, "branch", &log_dest);
639 EXPECT(err == 1);
640 reftable_log_record_release(&log_dest);
642 err = reftable_stack_compact_all(st, NULL);
643 EXPECT_ERR(err);
645 err = reftable_stack_read_ref(st, "branch", &dest);
646 EXPECT(err == 1);
648 err = reftable_stack_read_log(st, "branch", &log_dest);
649 EXPECT(err == 1);
650 reftable_ref_record_release(&dest);
651 reftable_log_record_release(&log_dest);
653 /* cleanup */
654 reftable_stack_destroy(st);
655 for (i = 0; i < N; i++) {
656 reftable_ref_record_release(&refs[i]);
657 reftable_log_record_release(&logs[i]);
659 clear_dir(dir);
662 static void test_reftable_stack_hash_id(void)
664 char *dir = get_tmp_dir(__LINE__);
665 struct reftable_write_options opts = { 0 };
666 struct reftable_stack *st = NULL;
667 int err;
669 struct reftable_ref_record ref = {
670 .refname = (char *) "master",
671 .value_type = REFTABLE_REF_SYMREF,
672 .value.symref = (char *) "target",
673 .update_index = 1,
675 struct reftable_write_options opts32 = { .hash_id = GIT_SHA256_FORMAT_ID };
676 struct reftable_stack *st32 = NULL;
677 struct reftable_write_options opts_default = { 0 };
678 struct reftable_stack *st_default = NULL;
679 struct reftable_ref_record dest = { NULL };
681 err = reftable_new_stack(&st, dir, &opts);
682 EXPECT_ERR(err);
684 err = reftable_stack_add(st, &write_test_ref, &ref);
685 EXPECT_ERR(err);
687 /* can't read it with the wrong hash ID. */
688 err = reftable_new_stack(&st32, dir, &opts32);
689 EXPECT(err == REFTABLE_FORMAT_ERROR);
691 /* check that we can read it back with default opts too. */
692 err = reftable_new_stack(&st_default, dir, &opts_default);
693 EXPECT_ERR(err);
695 err = reftable_stack_read_ref(st_default, "master", &dest);
696 EXPECT_ERR(err);
698 EXPECT(reftable_ref_record_equal(&ref, &dest, GIT_SHA1_RAWSZ));
699 reftable_ref_record_release(&dest);
700 reftable_stack_destroy(st);
701 reftable_stack_destroy(st_default);
702 clear_dir(dir);
705 static void test_suggest_compaction_segment(void)
707 uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 };
708 struct segment min =
709 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
710 EXPECT(min.start == 1);
711 EXPECT(min.end == 10);
714 static void test_suggest_compaction_segment_nothing(void)
716 uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
717 struct segment result =
718 suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
719 EXPECT(result.start == result.end);
722 static void test_reflog_expire(void)
724 char *dir = get_tmp_dir(__LINE__);
725 struct reftable_write_options opts = { 0 };
726 struct reftable_stack *st = NULL;
727 struct reftable_log_record logs[20] = { { NULL } };
728 int N = ARRAY_SIZE(logs) - 1;
729 int i = 0;
730 int err;
731 struct reftable_log_expiry_config expiry = {
732 .time = 10,
734 struct reftable_log_record log = { NULL };
736 err = reftable_new_stack(&st, dir, &opts);
737 EXPECT_ERR(err);
739 for (i = 1; i <= N; i++) {
740 char buf[256];
741 snprintf(buf, sizeof(buf), "branch%02d", i);
743 logs[i].refname = xstrdup(buf);
744 logs[i].update_index = i;
745 logs[i].value_type = REFTABLE_LOG_UPDATE;
746 logs[i].value.update.time = i;
747 logs[i].value.update.email = xstrdup("identity@invalid");
748 set_test_hash(logs[i].value.update.new_hash, i);
751 for (i = 1; i <= N; i++) {
752 struct write_log_arg arg = {
753 .log = &logs[i],
754 .update_index = reftable_stack_next_update_index(st),
756 int err = reftable_stack_add(st, &write_test_log, &arg);
757 EXPECT_ERR(err);
760 err = reftable_stack_compact_all(st, NULL);
761 EXPECT_ERR(err);
763 err = reftable_stack_compact_all(st, &expiry);
764 EXPECT_ERR(err);
766 err = reftable_stack_read_log(st, logs[9].refname, &log);
767 EXPECT(err == 1);
769 err = reftable_stack_read_log(st, logs[11].refname, &log);
770 EXPECT_ERR(err);
772 expiry.min_update_index = 15;
773 err = reftable_stack_compact_all(st, &expiry);
774 EXPECT_ERR(err);
776 err = reftable_stack_read_log(st, logs[14].refname, &log);
777 EXPECT(err == 1);
779 err = reftable_stack_read_log(st, logs[16].refname, &log);
780 EXPECT_ERR(err);
782 /* cleanup */
783 reftable_stack_destroy(st);
784 for (i = 0; i <= N; i++) {
785 reftable_log_record_release(&logs[i]);
787 clear_dir(dir);
788 reftable_log_record_release(&log);
791 static int write_nothing(struct reftable_writer *wr, void *arg)
793 reftable_writer_set_limits(wr, 1, 1);
794 return 0;
797 static void test_empty_add(void)
799 struct reftable_write_options opts = { 0 };
800 struct reftable_stack *st = NULL;
801 int err;
802 char *dir = get_tmp_dir(__LINE__);
803 struct reftable_stack *st2 = NULL;
805 err = reftable_new_stack(&st, dir, &opts);
806 EXPECT_ERR(err);
808 err = reftable_stack_add(st, &write_nothing, NULL);
809 EXPECT_ERR(err);
811 err = reftable_new_stack(&st2, dir, &opts);
812 EXPECT_ERR(err);
813 clear_dir(dir);
814 reftable_stack_destroy(st);
815 reftable_stack_destroy(st2);
818 static int fastlog2(uint64_t sz)
820 int l = 0;
821 if (sz == 0)
822 return 0;
823 for (; sz; sz /= 2)
824 l++;
825 return l - 1;
828 static void test_reftable_stack_auto_compaction(void)
830 struct reftable_write_options opts = {
831 .disable_auto_compact = 1,
833 struct reftable_stack *st = NULL;
834 char *dir = get_tmp_dir(__LINE__);
835 int err, i;
836 int N = 100;
838 err = reftable_new_stack(&st, dir, &opts);
839 EXPECT_ERR(err);
841 for (i = 0; i < N; i++) {
842 char name[100];
843 struct reftable_ref_record ref = {
844 .refname = name,
845 .update_index = reftable_stack_next_update_index(st),
846 .value_type = REFTABLE_REF_SYMREF,
847 .value.symref = (char *) "master",
849 snprintf(name, sizeof(name), "branch%04d", i);
851 err = reftable_stack_add(st, &write_test_ref, &ref);
852 EXPECT_ERR(err);
854 err = reftable_stack_auto_compact(st);
855 EXPECT_ERR(err);
856 EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
859 EXPECT(reftable_stack_compaction_stats(st)->entries_written <
860 (uint64_t)(N * fastlog2(N)));
862 reftable_stack_destroy(st);
863 clear_dir(dir);
866 static void test_reftable_stack_add_performs_auto_compaction(void)
868 struct reftable_write_options opts = { 0 };
869 struct reftable_stack *st = NULL;
870 struct strbuf refname = STRBUF_INIT;
871 char *dir = get_tmp_dir(__LINE__);
872 int err, i, n = 20;
874 err = reftable_new_stack(&st, dir, &opts);
875 EXPECT_ERR(err);
877 for (i = 0; i <= n; i++) {
878 struct reftable_ref_record ref = {
879 .update_index = reftable_stack_next_update_index(st),
880 .value_type = REFTABLE_REF_SYMREF,
881 .value.symref = (char *) "master",
885 * Disable auto-compaction for all but the last runs. Like this
886 * we can ensure that we indeed honor this setting and have
887 * better control over when exactly auto compaction runs.
889 st->opts.disable_auto_compact = i != n;
891 strbuf_reset(&refname);
892 strbuf_addf(&refname, "branch-%04d", i);
893 ref.refname = refname.buf;
895 err = reftable_stack_add(st, &write_test_ref, &ref);
896 EXPECT_ERR(err);
899 * The stack length should grow continuously for all runs where
900 * auto compaction is disabled. When enabled, we should merge
901 * all tables in the stack.
903 if (i != n)
904 EXPECT(st->merged->stack_len == i + 1);
905 else
906 EXPECT(st->merged->stack_len == 1);
909 reftable_stack_destroy(st);
910 strbuf_release(&refname);
911 clear_dir(dir);
914 static void test_reftable_stack_compaction_concurrent(void)
916 struct reftable_write_options opts = { 0 };
917 struct reftable_stack *st1 = NULL, *st2 = NULL;
918 char *dir = get_tmp_dir(__LINE__);
919 int err, i;
920 int N = 3;
922 err = reftable_new_stack(&st1, dir, &opts);
923 EXPECT_ERR(err);
925 for (i = 0; i < N; i++) {
926 char name[100];
927 struct reftable_ref_record ref = {
928 .refname = name,
929 .update_index = reftable_stack_next_update_index(st1),
930 .value_type = REFTABLE_REF_SYMREF,
931 .value.symref = (char *) "master",
933 snprintf(name, sizeof(name), "branch%04d", i);
935 err = reftable_stack_add(st1, &write_test_ref, &ref);
936 EXPECT_ERR(err);
939 err = reftable_new_stack(&st2, dir, &opts);
940 EXPECT_ERR(err);
942 err = reftable_stack_compact_all(st1, NULL);
943 EXPECT_ERR(err);
945 reftable_stack_destroy(st1);
946 reftable_stack_destroy(st2);
948 EXPECT(count_dir_entries(dir) == 2);
949 clear_dir(dir);
952 static void unclean_stack_close(struct reftable_stack *st)
954 /* break abstraction boundary to simulate unclean shutdown. */
955 int i = 0;
956 for (; i < st->readers_len; i++) {
957 reftable_reader_free(st->readers[i]);
959 st->readers_len = 0;
960 FREE_AND_NULL(st->readers);
963 static void test_reftable_stack_compaction_concurrent_clean(void)
965 struct reftable_write_options opts = { 0 };
966 struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
967 char *dir = get_tmp_dir(__LINE__);
968 int err, i;
969 int N = 3;
971 err = reftable_new_stack(&st1, dir, &opts);
972 EXPECT_ERR(err);
974 for (i = 0; i < N; i++) {
975 char name[100];
976 struct reftable_ref_record ref = {
977 .refname = name,
978 .update_index = reftable_stack_next_update_index(st1),
979 .value_type = REFTABLE_REF_SYMREF,
980 .value.symref = (char *) "master",
982 snprintf(name, sizeof(name), "branch%04d", i);
984 err = reftable_stack_add(st1, &write_test_ref, &ref);
985 EXPECT_ERR(err);
988 err = reftable_new_stack(&st2, dir, &opts);
989 EXPECT_ERR(err);
991 err = reftable_stack_compact_all(st1, NULL);
992 EXPECT_ERR(err);
994 unclean_stack_close(st1);
995 unclean_stack_close(st2);
997 err = reftable_new_stack(&st3, dir, &opts);
998 EXPECT_ERR(err);
1000 err = reftable_stack_clean(st3);
1001 EXPECT_ERR(err);
1002 EXPECT(count_dir_entries(dir) == 2);
1004 reftable_stack_destroy(st1);
1005 reftable_stack_destroy(st2);
1006 reftable_stack_destroy(st3);
1008 clear_dir(dir);
1011 int stack_test_main(int argc, const char *argv[])
1013 RUN_TEST(test_empty_add);
1014 RUN_TEST(test_read_file);
1015 RUN_TEST(test_reflog_expire);
1016 RUN_TEST(test_reftable_stack_add);
1017 RUN_TEST(test_reftable_stack_add_one);
1018 RUN_TEST(test_reftable_stack_auto_compaction);
1019 RUN_TEST(test_reftable_stack_add_performs_auto_compaction);
1020 RUN_TEST(test_reftable_stack_compaction_concurrent);
1021 RUN_TEST(test_reftable_stack_compaction_concurrent_clean);
1022 RUN_TEST(test_reftable_stack_hash_id);
1023 RUN_TEST(test_reftable_stack_lock_failure);
1024 RUN_TEST(test_reftable_stack_log_normalize);
1025 RUN_TEST(test_reftable_stack_tombstone);
1026 RUN_TEST(test_reftable_stack_transaction_api);
1027 RUN_TEST(test_reftable_stack_transaction_api_performs_auto_compaction);
1028 RUN_TEST(test_reftable_stack_auto_compaction_fails_gracefully);
1029 RUN_TEST(test_reftable_stack_update_index_check);
1030 RUN_TEST(test_reftable_stack_uptodate);
1031 RUN_TEST(test_suggest_compaction_segment);
1032 RUN_TEST(test_suggest_compaction_segment_nothing);
1033 return 0;