]> git.ipfire.org Git - thirdparty/git.git/blob - reftable/stack_test.c
Merge branch 'hn/reftable-coverity-fixes'
[thirdparty/git.git] / reftable / stack_test.c
1 /*
2 Copyright 2020 Google LLC
3
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 */
8
9 #include "stack.h"
10
11 #include "system.h"
12
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"
21
22 #include <sys/types.h>
23 #include <dirent.h>
24
25 static void clear_dir(const char *dirname)
26 {
27 struct strbuf path = STRBUF_INIT;
28 strbuf_addstr(&path, dirname);
29 remove_dir_recursively(&path, 0);
30 strbuf_release(&path);
31 }
32
33 static int count_dir_entries(const char *dirname)
34 {
35 DIR *dir = opendir(dirname);
36 int len = 0;
37 struct dirent *d;
38 if (dir == NULL)
39 return 0;
40
41 while ((d = readdir(dir))) {
42 if (!strcmp(d->d_name, "..") || !strcmp(d->d_name, "."))
43 continue;
44 len++;
45 }
46 closedir(dir);
47 return len;
48 }
49
50 /*
51 * Work linenumber into the tempdir, so we can see which tests forget to
52 * cleanup.
53 */
54 static char *get_tmp_template(int linenumber)
55 {
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;
61 }
62
63 static char *get_tmp_dir(int linenumber)
64 {
65 char *dir = get_tmp_template(linenumber);
66 EXPECT(mkdtemp(dir));
67 return dir;
68 }
69
70 static void test_read_file(void)
71 {
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;
79
80 EXPECT(fd > 0);
81 n = write(fd, out, strlen(out));
82 EXPECT(n == strlen(out));
83 err = close(fd);
84 EXPECT(err >= 0);
85
86 err = read_lines(fn, &names);
87 EXPECT_ERR(err);
88
89 for (i = 0; names[i]; i++) {
90 EXPECT(0 == strcmp(want[i], names[i]));
91 }
92 free_names(names);
93 (void) remove(fn);
94 }
95
96 static void test_parse_names(void)
97 {
98 char buf[] = "line\n";
99 char **names = NULL;
100 parse_names(buf, strlen(buf), &names);
101
102 EXPECT(NULL != names[0]);
103 EXPECT(0 == strcmp(names[0], "line"));
104 EXPECT(NULL == names[1]);
105 free_names(names);
106 }
107
108 static void test_names_equal(void)
109 {
110 char *a[] = { "a", "b", "c", NULL };
111 char *b[] = { "a", "b", "d", NULL };
112 char *c[] = { "a", "b", NULL };
113
114 EXPECT(names_equal(a, a));
115 EXPECT(!names_equal(a, b));
116 EXPECT(!names_equal(a, c));
117 }
118
119 static int write_test_ref(struct reftable_writer *wr, void *arg)
120 {
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);
124 }
125
126 struct write_log_arg {
127 struct reftable_log_record *log;
128 uint64_t update_index;
129 };
130
131 static int write_test_log(struct reftable_writer *wr, void *arg)
132 {
133 struct write_log_arg *wla = arg;
134
135 reftable_writer_set_limits(wr, wla->update_index, wla->update_index);
136 return reftable_writer_add_log(wr, wla->log);
137 }
138
139 static void test_reftable_stack_add_one(void)
140 {
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,
146 };
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",
154 };
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);
159
160 err = reftable_stack_add(st, &write_test_ref, &ref);
161 EXPECT_ERR(err);
162
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);
167
168 printf("testing print functionality:\n");
169 err = reftable_stack_print_directory(dir, GIT_SHA1_FORMAT_ID);
170 EXPECT_ERR(err);
171
172 err = reftable_stack_print_directory(dir, GIT_SHA256_FORMAT_ID);
173 EXPECT(err == REFTABLE_FORMAT_ERROR);
174
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);
181
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
193
194 reftable_ref_record_release(&dest);
195 reftable_stack_destroy(st);
196 strbuf_release(&scratch);
197 clear_dir(dir);
198 umask(mask);
199 }
200
201 static void test_reftable_stack_uptodate(void)
202 {
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__);
207
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",
214 };
215 struct reftable_ref_record ref2 = {
216 .refname = "branch2",
217 .update_index = 2,
218 .value_type = REFTABLE_REF_SYMREF,
219 .value.symref = "master",
220 };
221
222
223 /* simulate multi-process access to the same stack
224 by creating two stacks for the same directory.
225 */
226 err = reftable_new_stack(&st1, dir, cfg);
227 EXPECT_ERR(err);
228
229 err = reftable_new_stack(&st2, dir, cfg);
230 EXPECT_ERR(err);
231
232 err = reftable_stack_add(st1, &write_test_ref, &ref1);
233 EXPECT_ERR(err);
234
235 err = reftable_stack_add(st2, &write_test_ref, &ref2);
236 EXPECT(err == REFTABLE_LOCK_ERROR);
237
238 err = reftable_stack_reload(st2);
239 EXPECT_ERR(err);
240
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);
246 }
247
248 static void test_reftable_stack_transaction_api(void)
249 {
250 char *dir = get_tmp_dir(__LINE__);
251
252 struct reftable_write_options cfg = { 0 };
253 struct reftable_stack *st = NULL;
254 int err;
255 struct reftable_addition *add = NULL;
256
257 struct reftable_ref_record ref = {
258 .refname = "HEAD",
259 .update_index = 1,
260 .value_type = REFTABLE_REF_SYMREF,
261 .value.symref = "master",
262 };
263 struct reftable_ref_record dest = { NULL };
264
265
266 err = reftable_new_stack(&st, dir, cfg);
267 EXPECT_ERR(err);
268
269 reftable_addition_destroy(add);
270
271 err = reftable_stack_new_addition(&add, st);
272 EXPECT_ERR(err);
273
274 err = reftable_addition_add(add, &write_test_ref, &ref);
275 EXPECT_ERR(err);
276
277 err = reftable_addition_commit(add);
278 EXPECT_ERR(err);
279
280 reftable_addition_destroy(add);
281
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));
286
287 reftable_ref_record_release(&dest);
288 reftable_stack_destroy(st);
289 clear_dir(dir);
290 }
291
292 static void test_reftable_stack_validate_refname(void)
293 {
294 struct reftable_write_options cfg = { 0 };
295 struct reftable_stack *st = NULL;
296 int err;
297 char *dir = get_tmp_dir(__LINE__);
298
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",
305 };
306 char *additions[] = { "a", "a/b/c" };
307
308 err = reftable_new_stack(&st, dir, cfg);
309 EXPECT_ERR(err);
310
311 err = reftable_stack_add(st, &write_test_ref, &ref);
312 EXPECT_ERR(err);
313
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",
320 };
321
322 err = reftable_stack_add(st, &write_test_ref, &ref);
323 EXPECT(err == REFTABLE_NAME_CONFLICT);
324 }
325
326 reftable_stack_destroy(st);
327 clear_dir(dir);
328 }
329
330 static int write_error(struct reftable_writer *wr, void *arg)
331 {
332 return *((int *)arg);
333 }
334
335 static void test_reftable_stack_update_index_check(void)
336 {
337 char *dir = get_tmp_dir(__LINE__);
338
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",
347 };
348 struct reftable_ref_record ref2 = {
349 .refname = "name2",
350 .update_index = 1,
351 .value_type = REFTABLE_REF_SYMREF,
352 .value.symref = "master",
353 };
354
355 err = reftable_new_stack(&st, dir, cfg);
356 EXPECT_ERR(err);
357
358 err = reftable_stack_add(st, &write_test_ref, &ref1);
359 EXPECT_ERR(err);
360
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);
365 }
366
367 static void test_reftable_stack_lock_failure(void)
368 {
369 char *dir = get_tmp_dir(__LINE__);
370
371 struct reftable_write_options cfg = { 0 };
372 struct reftable_stack *st = NULL;
373 int err, i;
374
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);
380 }
381
382 reftable_stack_destroy(st);
383 clear_dir(dir);
384 }
385
386 static void test_reftable_stack_add(void)
387 {
388 int i = 0;
389 int err = 0;
390 struct reftable_write_options cfg = {
391 .exact_log_message = 1,
392 };
393 struct reftable_stack *st = NULL;
394 char *dir = get_tmp_dir(__LINE__);
395
396 struct reftable_ref_record refs[2] = { { NULL } };
397 struct reftable_log_record logs[2] = { { NULL } };
398 int N = ARRAY_SIZE(refs);
399
400
401 err = reftable_new_stack(&st, dir, cfg);
402 EXPECT_ERR(err);
403 st->disable_auto_compact = 1;
404
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);
413
414 logs[i].refname = xstrdup(buf);
415 logs[i].update_index = N + i + 1;
416 logs[i].value_type = REFTABLE_LOG_UPDATE;
417
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);
421 }
422
423 for (i = 0; i < N; i++) {
424 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
425 EXPECT_ERR(err);
426 }
427
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),
432 };
433 int err = reftable_stack_add(st, &write_test_log, &arg);
434 EXPECT_ERR(err);
435 }
436
437 err = reftable_stack_compact_all(st, NULL);
438 EXPECT_ERR(err);
439
440 for (i = 0; i < N; i++) {
441 struct reftable_ref_record dest = { NULL };
442
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);
448 }
449
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);
457 }
458
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]);
464 }
465 clear_dir(dir);
466 }
467
468 static void test_reftable_stack_log_normalize(void)
469 {
470 int err = 0;
471 struct reftable_write_options cfg = {
472 0,
473 };
474 struct reftable_stack *st = NULL;
475 char *dir = get_tmp_dir(__LINE__);
476
477 uint8_t h1[GIT_SHA1_RAWSZ] = { 0x01 }, h2[GIT_SHA1_RAWSZ] = { 0x02 };
478
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,
488 };
489 struct write_log_arg arg = {
490 .log = &input,
491 .update_index = 1,
492 };
493
494 err = reftable_new_stack(&st, dir, cfg);
495 EXPECT_ERR(err);
496
497 input.value.update.message = "one\ntwo";
498 err = reftable_stack_add(st, &write_test_log, &arg);
499 EXPECT(err == REFTABLE_API_ERROR);
500
501 input.value.update.message = "one";
502 err = reftable_stack_add(st, &write_test_log, &arg);
503 EXPECT_ERR(err);
504
505 err = reftable_stack_read_log(st, input.refname, &dest);
506 EXPECT_ERR(err);
507 EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
508
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"));
516
517 /* cleanup */
518 reftable_stack_destroy(st);
519 reftable_log_record_release(&dest);
520 clear_dir(dir);
521 }
522
523 static void test_reftable_stack_tombstone(void)
524 {
525 int i = 0;
526 char *dir = get_tmp_dir(__LINE__);
527
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 };
536
537
538 err = reftable_new_stack(&st, dir, cfg);
539 EXPECT_ERR(err);
540
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);
550 }
551
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");
562 }
563 }
564 for (i = 0; i < N; i++) {
565 int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
566 EXPECT_ERR(err);
567 }
568
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),
573 };
574 int err = reftable_stack_add(st, &write_test_log, &arg);
575 EXPECT_ERR(err);
576 }
577
578 err = reftable_stack_read_ref(st, "branch", &dest);
579 EXPECT(err == 1);
580 reftable_ref_record_release(&dest);
581
582 err = reftable_stack_read_log(st, "branch", &log_dest);
583 EXPECT(err == 1);
584 reftable_log_record_release(&log_dest);
585
586 err = reftable_stack_compact_all(st, NULL);
587 EXPECT_ERR(err);
588
589 err = reftable_stack_read_ref(st, "branch", &dest);
590 EXPECT(err == 1);
591
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);
596
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]);
602 }
603 clear_dir(dir);
604 }
605
606 static void test_reftable_stack_hash_id(void)
607 {
608 char *dir = get_tmp_dir(__LINE__);
609
610 struct reftable_write_options cfg = { 0 };
611 struct reftable_stack *st = NULL;
612 int err;
613
614 struct reftable_ref_record ref = {
615 .refname = "master",
616 .value_type = REFTABLE_REF_SYMREF,
617 .value.symref = "target",
618 .update_index = 1,
619 };
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 };
625
626 err = reftable_new_stack(&st, dir, cfg);
627 EXPECT_ERR(err);
628
629 err = reftable_stack_add(st, &write_test_ref, &ref);
630 EXPECT_ERR(err);
631
632 /* can't read it with the wrong hash ID. */
633 err = reftable_new_stack(&st32, dir, cfg32);
634 EXPECT(err == REFTABLE_FORMAT_ERROR);
635
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);
639
640 err = reftable_stack_read_ref(st_default, "master", &dest);
641 EXPECT_ERR(err);
642
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);
648 }
649
650 static void test_log2(void)
651 {
652 EXPECT(1 == fastlog2(3));
653 EXPECT(2 == fastlog2(4));
654 EXPECT(2 == fastlog2(5));
655 }
656
657 static void test_sizes_to_segments(void)
658 {
659 uint64_t sizes[] = { 2, 3, 4, 5, 7, 9 };
660 /* .................0 1 2 3 4 5 */
661
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);
668
669 EXPECT(segs[1].log == 2);
670 EXPECT(segs[1].start == 2);
671 EXPECT(segs[1].end == 5);
672 reftable_free(segs);
673 }
674
675 static void test_sizes_to_segments_empty(void)
676 {
677 int seglen = 0;
678 struct segment *segs = sizes_to_segments(&seglen, NULL, 0);
679 EXPECT(seglen == 0);
680 reftable_free(segs);
681 }
682
683 static void test_sizes_to_segments_all_equal(void)
684 {
685 uint64_t sizes[] = { 5, 5 };
686
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);
694 }
695
696 static void test_suggest_compaction_segment(void)
697 {
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);
704 }
705
706 static void test_suggest_compaction_segment_nothing(void)
707 {
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);
712 }
713
714 static void test_reflog_expire(void)
715 {
716 char *dir = get_tmp_dir(__LINE__);
717
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,
726 };
727 struct reftable_log_record log = { NULL };
728
729
730 err = reftable_new_stack(&st, dir, cfg);
731 EXPECT_ERR(err);
732
733 for (i = 1; i <= N; i++) {
734 char buf[256];
735 snprintf(buf, sizeof(buf), "branch%02d", i);
736
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);
744 }
745
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),
750 };
751 int err = reftable_stack_add(st, &write_test_log, &arg);
752 EXPECT_ERR(err);
753 }
754
755 err = reftable_stack_compact_all(st, NULL);
756 EXPECT_ERR(err);
757
758 err = reftable_stack_compact_all(st, &expiry);
759 EXPECT_ERR(err);
760
761 err = reftable_stack_read_log(st, logs[9].refname, &log);
762 EXPECT(err == 1);
763
764 err = reftable_stack_read_log(st, logs[11].refname, &log);
765 EXPECT_ERR(err);
766
767 expiry.min_update_index = 15;
768 err = reftable_stack_compact_all(st, &expiry);
769 EXPECT_ERR(err);
770
771 err = reftable_stack_read_log(st, logs[14].refname, &log);
772 EXPECT(err == 1);
773
774 err = reftable_stack_read_log(st, logs[16].refname, &log);
775 EXPECT_ERR(err);
776
777 /* cleanup */
778 reftable_stack_destroy(st);
779 for (i = 0; i <= N; i++) {
780 reftable_log_record_release(&logs[i]);
781 }
782 clear_dir(dir);
783 reftable_log_record_release(&log);
784 }
785
786 static int write_nothing(struct reftable_writer *wr, void *arg)
787 {
788 reftable_writer_set_limits(wr, 1, 1);
789 return 0;
790 }
791
792 static void test_empty_add(void)
793 {
794 struct reftable_write_options cfg = { 0 };
795 struct reftable_stack *st = NULL;
796 int err;
797 char *dir = get_tmp_dir(__LINE__);
798
799 struct reftable_stack *st2 = NULL;
800
801
802 err = reftable_new_stack(&st, dir, cfg);
803 EXPECT_ERR(err);
804
805 err = reftable_stack_add(st, &write_nothing, NULL);
806 EXPECT_ERR(err);
807
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);
813 }
814
815 static void test_reftable_stack_auto_compaction(void)
816 {
817 struct reftable_write_options cfg = { 0 };
818 struct reftable_stack *st = NULL;
819 char *dir = get_tmp_dir(__LINE__);
820
821 int err, i;
822 int N = 100;
823
824 err = reftable_new_stack(&st, dir, cfg);
825 EXPECT_ERR(err);
826
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",
835 };
836 snprintf(name, sizeof(name), "branch%04d", i);
837
838 err = reftable_stack_add(st, &write_test_ref, &ref);
839 EXPECT_ERR(err);
840
841 err = reftable_stack_auto_compact(st);
842 EXPECT_ERR(err);
843 EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
844 }
845
846 EXPECT(reftable_stack_compaction_stats(st)->entries_written <
847 (uint64_t)(N * fastlog2(N)));
848
849 reftable_stack_destroy(st);
850 clear_dir(dir);
851 }
852
853 static void test_reftable_stack_compaction_concurrent(void)
854 {
855 struct reftable_write_options cfg = { 0 };
856 struct reftable_stack *st1 = NULL, *st2 = NULL;
857 char *dir = get_tmp_dir(__LINE__);
858
859 int err, i;
860 int N = 3;
861
862 err = reftable_new_stack(&st1, dir, cfg);
863 EXPECT_ERR(err);
864
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",
872 };
873 snprintf(name, sizeof(name), "branch%04d", i);
874
875 err = reftable_stack_add(st1, &write_test_ref, &ref);
876 EXPECT_ERR(err);
877 }
878
879 err = reftable_new_stack(&st2, dir, cfg);
880 EXPECT_ERR(err);
881
882 err = reftable_stack_compact_all(st1, NULL);
883 EXPECT_ERR(err);
884
885 reftable_stack_destroy(st1);
886 reftable_stack_destroy(st2);
887
888 EXPECT(count_dir_entries(dir) == 2);
889 clear_dir(dir);
890 }
891
892 static void unclean_stack_close(struct reftable_stack *st)
893 {
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]);
898 }
899 st->readers_len = 0;
900 FREE_AND_NULL(st->readers);
901 }
902
903 static void test_reftable_stack_compaction_concurrent_clean(void)
904 {
905 struct reftable_write_options cfg = { 0 };
906 struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
907 char *dir = get_tmp_dir(__LINE__);
908
909 int err, i;
910 int N = 3;
911
912 err = reftable_new_stack(&st1, dir, cfg);
913 EXPECT_ERR(err);
914
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",
922 };
923 snprintf(name, sizeof(name), "branch%04d", i);
924
925 err = reftable_stack_add(st1, &write_test_ref, &ref);
926 EXPECT_ERR(err);
927 }
928
929 err = reftable_new_stack(&st2, dir, cfg);
930 EXPECT_ERR(err);
931
932 err = reftable_stack_compact_all(st1, NULL);
933 EXPECT_ERR(err);
934
935 unclean_stack_close(st1);
936 unclean_stack_close(st2);
937
938 err = reftable_new_stack(&st3, dir, cfg);
939 EXPECT_ERR(err);
940
941 err = reftable_stack_clean(st3);
942 EXPECT_ERR(err);
943 EXPECT(count_dir_entries(dir) == 2);
944
945 reftable_stack_destroy(st1);
946 reftable_stack_destroy(st2);
947 reftable_stack_destroy(st3);
948
949 clear_dir(dir);
950 }
951
952 int stack_test_main(int argc, const char *argv[])
953 {
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;
979 }