]>
Commit | Line | Data |
---|---|---|
e48d4272 HWN |
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" | |
cd1799de | 20 | #include "reader.h" |
e48d4272 HWN |
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; | |
72a4ea71 | 38 | if (!dir) |
e48d4272 HWN |
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); | |
f5f6a6cd | 93 | (void) remove(fn); |
e48d4272 HWN |
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__); | |
cd1799de HWN |
142 | struct strbuf scratch = STRBUF_INIT; |
143 | int mask = umask(002); | |
144 | struct reftable_write_options cfg = { | |
145 | .default_permissions = 0660, | |
146 | }; | |
e48d4272 HWN |
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 }; | |
cd1799de | 156 | struct stat stat_result = { 0 }; |
e48d4272 HWN |
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)); | |
cd1799de | 166 | EXPECT(st->readers_len > 0); |
e48d4272 HWN |
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 | ||
cd1799de HWN |
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 | ||
e48d4272 HWN |
194 | reftable_ref_record_release(&dest); |
195 | reftable_stack_destroy(st); | |
cd1799de | 196 | strbuf_release(&scratch); |
e48d4272 | 197 | clear_dir(dir); |
cd1799de | 198 | umask(mask); |
e48d4272 HWN |
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); | |
f7445865 | 842 | EXPECT_ERR(err); |
e48d4272 HWN |
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 | } |