]>
Commit | Line | Data |
---|---|---|
ffc97f1a 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 "system.h" | |
10 | ||
11 | #include "basics.h" | |
12 | #include "block.h" | |
13 | #include "blocksource.h" | |
ffc97f1a HWN |
14 | #include "reader.h" |
15 | #include "record.h" | |
16 | #include "test_framework.h" | |
17 | #include "reftable-tests.h" | |
18 | #include "reftable-writer.h" | |
19 | ||
20 | static const int update_index = 5; | |
21 | ||
22 | static void test_buffer(void) | |
23 | { | |
24 | struct strbuf buf = STRBUF_INIT; | |
25 | struct reftable_block_source source = { NULL }; | |
26 | struct reftable_block out = { NULL }; | |
27 | int n; | |
28 | uint8_t in[] = "hello"; | |
29 | strbuf_add(&buf, in, sizeof(in)); | |
30 | block_source_from_strbuf(&source, &buf); | |
31 | EXPECT(block_source_size(&source) == 6); | |
32 | n = block_source_read_block(&source, &out, 0, sizeof(in)); | |
33 | EXPECT(n == sizeof(in)); | |
34 | EXPECT(!memcmp(in, out.data, n)); | |
35 | reftable_block_done(&out); | |
36 | ||
37 | n = block_source_read_block(&source, &out, 1, 2); | |
38 | EXPECT(n == 2); | |
39 | EXPECT(!memcmp(out.data, "el", 2)); | |
40 | ||
41 | reftable_block_done(&out); | |
42 | block_source_close(&source); | |
43 | strbuf_release(&buf); | |
44 | } | |
45 | ||
46 | static void write_table(char ***names, struct strbuf *buf, int N, | |
47 | int block_size, uint32_t hash_id) | |
48 | { | |
49 | struct reftable_write_options opts = { | |
50 | .block_size = block_size, | |
51 | .hash_id = hash_id, | |
52 | }; | |
53 | struct reftable_writer *w = | |
54 | reftable_new_writer(&strbuf_add_void, buf, &opts); | |
55 | struct reftable_ref_record ref = { NULL }; | |
56 | int i = 0, n; | |
57 | struct reftable_log_record log = { NULL }; | |
58 | const struct reftable_stats *stats = NULL; | |
59 | *names = reftable_calloc(sizeof(char *) * (N + 1)); | |
60 | reftable_writer_set_limits(w, update_index, update_index); | |
61 | for (i = 0; i < N; i++) { | |
ffc97f1a HWN |
62 | char name[100]; |
63 | int n; | |
64 | ||
ffc97f1a HWN |
65 | snprintf(name, sizeof(name), "refs/heads/branch%02d", i); |
66 | ||
67 | ref.refname = name; | |
68 | ref.update_index = update_index; | |
69 | ref.value_type = REFTABLE_REF_VAL1; | |
7af607c5 | 70 | set_test_hash(ref.value.val1, i); |
ffc97f1a HWN |
71 | (*names)[i] = xstrdup(name); |
72 | ||
73 | n = reftable_writer_add_ref(w, &ref); | |
74 | EXPECT(n == 0); | |
75 | } | |
76 | ||
77 | for (i = 0; i < N; i++) { | |
78 | uint8_t hash[GIT_SHA256_RAWSZ] = { 0 }; | |
79 | char name[100]; | |
80 | int n; | |
81 | ||
82 | set_test_hash(hash, i); | |
83 | ||
84 | snprintf(name, sizeof(name), "refs/heads/branch%02d", i); | |
85 | ||
86 | log.refname = name; | |
87 | log.update_index = update_index; | |
88 | log.value_type = REFTABLE_LOG_UPDATE; | |
89 | log.value.update.new_hash = hash; | |
90 | log.value.update.message = "message"; | |
91 | ||
92 | n = reftable_writer_add_log(w, &log); | |
93 | EXPECT(n == 0); | |
94 | } | |
95 | ||
96 | n = reftable_writer_close(w); | |
97 | EXPECT(n == 0); | |
98 | ||
73a4c188 | 99 | stats = reftable_writer_stats(w); |
ffc97f1a HWN |
100 | for (i = 0; i < stats->ref_stats.blocks; i++) { |
101 | int off = i * opts.block_size; | |
102 | if (off == 0) { | |
103 | off = header_size( | |
104 | (hash_id == GIT_SHA256_FORMAT_ID) ? 2 : 1); | |
105 | } | |
106 | EXPECT(buf->buf[off] == 'r'); | |
107 | } | |
108 | ||
109 | EXPECT(stats->log_stats.blocks > 0); | |
110 | reftable_writer_free(w); | |
111 | } | |
112 | ||
113 | static void test_log_buffer_size(void) | |
114 | { | |
115 | struct strbuf buf = STRBUF_INIT; | |
116 | struct reftable_write_options opts = { | |
117 | .block_size = 4096, | |
118 | }; | |
119 | int err; | |
120 | int i; | |
121 | struct reftable_log_record | |
122 | log = { .refname = "refs/heads/master", | |
123 | .update_index = 0xa, | |
124 | .value_type = REFTABLE_LOG_UPDATE, | |
125 | .value = { .update = { | |
126 | .name = "Han-Wen Nienhuys", | |
127 | .email = "hanwen@google.com", | |
128 | .tz_offset = 100, | |
129 | .time = 0x5e430672, | |
130 | .message = "commit: 9\n", | |
131 | } } }; | |
132 | struct reftable_writer *w = | |
133 | reftable_new_writer(&strbuf_add_void, &buf, &opts); | |
134 | ||
135 | /* This tests buffer extension for log compression. Must use a random | |
136 | hash, to ensure that the compressed part is larger than the original. | |
137 | */ | |
138 | uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ]; | |
139 | for (i = 0; i < GIT_SHA1_RAWSZ; i++) { | |
9abda981 PS |
140 | hash1[i] = (uint8_t)(git_rand() % 256); |
141 | hash2[i] = (uint8_t)(git_rand() % 256); | |
ffc97f1a HWN |
142 | } |
143 | log.value.update.old_hash = hash1; | |
144 | log.value.update.new_hash = hash2; | |
145 | reftable_writer_set_limits(w, update_index, update_index); | |
146 | err = reftable_writer_add_log(w, &log); | |
147 | EXPECT_ERR(err); | |
148 | err = reftable_writer_close(w); | |
149 | EXPECT_ERR(err); | |
150 | reftable_writer_free(w); | |
151 | strbuf_release(&buf); | |
152 | } | |
153 | ||
0dd44584 HWN |
154 | static void test_log_overflow(void) |
155 | { | |
156 | struct strbuf buf = STRBUF_INIT; | |
157 | char msg[256] = { 0 }; | |
158 | struct reftable_write_options opts = { | |
159 | .block_size = ARRAY_SIZE(msg), | |
160 | }; | |
161 | int err; | |
162 | struct reftable_log_record | |
163 | log = { .refname = "refs/heads/master", | |
164 | .update_index = 0xa, | |
165 | .value_type = REFTABLE_LOG_UPDATE, | |
166 | .value = { .update = { | |
167 | .name = "Han-Wen Nienhuys", | |
168 | .email = "hanwen@google.com", | |
169 | .tz_offset = 100, | |
170 | .time = 0x5e430672, | |
171 | .message = msg, | |
172 | } } }; | |
173 | struct reftable_writer *w = | |
174 | reftable_new_writer(&strbuf_add_void, &buf, &opts); | |
175 | ||
176 | uint8_t hash1[GIT_SHA1_RAWSZ] = {1}, hash2[GIT_SHA1_RAWSZ] = { 2 }; | |
177 | ||
178 | memset(msg, 'x', sizeof(msg) - 1); | |
179 | log.value.update.old_hash = hash1; | |
180 | log.value.update.new_hash = hash2; | |
181 | reftable_writer_set_limits(w, update_index, update_index); | |
182 | err = reftable_writer_add_log(w, &log); | |
183 | EXPECT(err == REFTABLE_ENTRY_TOO_BIG_ERROR); | |
184 | reftable_writer_free(w); | |
185 | strbuf_release(&buf); | |
186 | } | |
187 | ||
ffc97f1a HWN |
188 | static void test_log_write_read(void) |
189 | { | |
190 | int N = 2; | |
191 | char **names = reftable_calloc(sizeof(char *) * (N + 1)); | |
192 | int err; | |
193 | struct reftable_write_options opts = { | |
194 | .block_size = 256, | |
195 | }; | |
196 | struct reftable_ref_record ref = { NULL }; | |
197 | int i = 0; | |
198 | struct reftable_log_record log = { NULL }; | |
199 | int n; | |
200 | struct reftable_iterator it = { NULL }; | |
201 | struct reftable_reader rd = { NULL }; | |
202 | struct reftable_block_source source = { NULL }; | |
203 | struct strbuf buf = STRBUF_INIT; | |
204 | struct reftable_writer *w = | |
205 | reftable_new_writer(&strbuf_add_void, &buf, &opts); | |
206 | const struct reftable_stats *stats = NULL; | |
207 | reftable_writer_set_limits(w, 0, N); | |
208 | for (i = 0; i < N; i++) { | |
209 | char name[256]; | |
210 | struct reftable_ref_record ref = { NULL }; | |
211 | snprintf(name, sizeof(name), "b%02d%0*d", i, 130, 7); | |
212 | names[i] = xstrdup(name); | |
213 | ref.refname = name; | |
214 | ref.update_index = i; | |
215 | ||
216 | err = reftable_writer_add_ref(w, &ref); | |
217 | EXPECT_ERR(err); | |
218 | } | |
219 | for (i = 0; i < N; i++) { | |
220 | uint8_t hash1[GIT_SHA1_RAWSZ], hash2[GIT_SHA1_RAWSZ]; | |
221 | struct reftable_log_record log = { NULL }; | |
222 | set_test_hash(hash1, i); | |
223 | set_test_hash(hash2, i + 1); | |
224 | ||
225 | log.refname = names[i]; | |
226 | log.update_index = i; | |
227 | log.value_type = REFTABLE_LOG_UPDATE; | |
228 | log.value.update.old_hash = hash1; | |
229 | log.value.update.new_hash = hash2; | |
230 | ||
231 | err = reftable_writer_add_log(w, &log); | |
232 | EXPECT_ERR(err); | |
233 | } | |
234 | ||
235 | n = reftable_writer_close(w); | |
236 | EXPECT(n == 0); | |
237 | ||
73a4c188 | 238 | stats = reftable_writer_stats(w); |
ffc97f1a HWN |
239 | EXPECT(stats->log_stats.blocks > 0); |
240 | reftable_writer_free(w); | |
241 | w = NULL; | |
242 | ||
243 | block_source_from_strbuf(&source, &buf); | |
244 | ||
245 | err = init_reader(&rd, &source, "file.log"); | |
246 | EXPECT_ERR(err); | |
247 | ||
248 | err = reftable_reader_seek_ref(&rd, &it, names[N - 1]); | |
249 | EXPECT_ERR(err); | |
250 | ||
251 | err = reftable_iterator_next_ref(&it, &ref); | |
252 | EXPECT_ERR(err); | |
253 | ||
254 | /* end of iteration. */ | |
255 | err = reftable_iterator_next_ref(&it, &ref); | |
256 | EXPECT(0 < err); | |
257 | ||
258 | reftable_iterator_destroy(&it); | |
259 | reftable_ref_record_release(&ref); | |
260 | ||
261 | err = reftable_reader_seek_log(&rd, &it, ""); | |
262 | EXPECT_ERR(err); | |
263 | ||
264 | i = 0; | |
265 | while (1) { | |
266 | int err = reftable_iterator_next_log(&it, &log); | |
267 | if (err > 0) { | |
268 | break; | |
269 | } | |
270 | ||
271 | EXPECT_ERR(err); | |
272 | EXPECT_STREQ(names[i], log.refname); | |
273 | EXPECT(i == log.update_index); | |
274 | i++; | |
275 | reftable_log_record_release(&log); | |
276 | } | |
277 | ||
278 | EXPECT(i == N); | |
279 | reftable_iterator_destroy(&it); | |
280 | ||
281 | /* cleanup. */ | |
282 | strbuf_release(&buf); | |
283 | free_names(names); | |
284 | reader_close(&rd); | |
285 | } | |
286 | ||
24d4d38c HWN |
287 | static void test_log_zlib_corruption(void) |
288 | { | |
289 | struct reftable_write_options opts = { | |
290 | .block_size = 256, | |
291 | }; | |
292 | struct reftable_iterator it = { 0 }; | |
293 | struct reftable_reader rd = { 0 }; | |
294 | struct reftable_block_source source = { 0 }; | |
295 | struct strbuf buf = STRBUF_INIT; | |
296 | struct reftable_writer *w = | |
297 | reftable_new_writer(&strbuf_add_void, &buf, &opts); | |
298 | const struct reftable_stats *stats = NULL; | |
299 | uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 }; | |
300 | uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 }; | |
301 | char message[100] = { 0 }; | |
302 | int err, i, n; | |
303 | ||
304 | struct reftable_log_record log = { | |
305 | .refname = "refname", | |
306 | .value_type = REFTABLE_LOG_UPDATE, | |
307 | .value = { | |
308 | .update = { | |
309 | .new_hash = hash1, | |
310 | .old_hash = hash2, | |
311 | .name = "My Name", | |
312 | .email = "myname@invalid", | |
313 | .message = message, | |
314 | }, | |
315 | }, | |
316 | }; | |
317 | ||
318 | for (i = 0; i < sizeof(message) - 1; i++) | |
9abda981 | 319 | message[i] = (uint8_t)(git_rand() % 64 + ' '); |
24d4d38c HWN |
320 | |
321 | reftable_writer_set_limits(w, 1, 1); | |
322 | ||
323 | err = reftable_writer_add_log(w, &log); | |
324 | EXPECT_ERR(err); | |
325 | ||
326 | n = reftable_writer_close(w); | |
327 | EXPECT(n == 0); | |
328 | ||
73a4c188 | 329 | stats = reftable_writer_stats(w); |
24d4d38c HWN |
330 | EXPECT(stats->log_stats.blocks > 0); |
331 | reftable_writer_free(w); | |
332 | w = NULL; | |
333 | ||
334 | /* corrupt the data. */ | |
335 | buf.buf[50] ^= 0x99; | |
336 | ||
337 | block_source_from_strbuf(&source, &buf); | |
338 | ||
339 | err = init_reader(&rd, &source, "file.log"); | |
340 | EXPECT_ERR(err); | |
341 | ||
342 | err = reftable_reader_seek_log(&rd, &it, "refname"); | |
343 | EXPECT(err == REFTABLE_ZLIB_ERROR); | |
344 | ||
345 | reftable_iterator_destroy(&it); | |
346 | ||
347 | /* cleanup. */ | |
348 | strbuf_release(&buf); | |
349 | reader_close(&rd); | |
350 | } | |
351 | ||
ffc97f1a HWN |
352 | static void test_table_read_write_sequential(void) |
353 | { | |
354 | char **names; | |
355 | struct strbuf buf = STRBUF_INIT; | |
356 | int N = 50; | |
357 | struct reftable_iterator it = { NULL }; | |
358 | struct reftable_block_source source = { NULL }; | |
359 | struct reftable_reader rd = { NULL }; | |
360 | int err = 0; | |
361 | int j = 0; | |
362 | ||
363 | write_table(&names, &buf, N, 256, GIT_SHA1_FORMAT_ID); | |
364 | ||
365 | block_source_from_strbuf(&source, &buf); | |
366 | ||
367 | err = init_reader(&rd, &source, "file.ref"); | |
368 | EXPECT_ERR(err); | |
369 | ||
370 | err = reftable_reader_seek_ref(&rd, &it, ""); | |
371 | EXPECT_ERR(err); | |
372 | ||
373 | while (1) { | |
374 | struct reftable_ref_record ref = { NULL }; | |
375 | int r = reftable_iterator_next_ref(&it, &ref); | |
376 | EXPECT(r >= 0); | |
377 | if (r > 0) { | |
378 | break; | |
379 | } | |
380 | EXPECT(0 == strcmp(names[j], ref.refname)); | |
381 | EXPECT(update_index == ref.update_index); | |
382 | ||
383 | j++; | |
384 | reftable_ref_record_release(&ref); | |
385 | } | |
386 | EXPECT(j == N); | |
387 | reftable_iterator_destroy(&it); | |
388 | strbuf_release(&buf); | |
389 | free_names(names); | |
390 | ||
391 | reader_close(&rd); | |
392 | } | |
393 | ||
394 | static void test_table_write_small_table(void) | |
395 | { | |
396 | char **names; | |
397 | struct strbuf buf = STRBUF_INIT; | |
398 | int N = 1; | |
399 | write_table(&names, &buf, N, 4096, GIT_SHA1_FORMAT_ID); | |
400 | EXPECT(buf.len < 200); | |
401 | strbuf_release(&buf); | |
402 | free_names(names); | |
403 | } | |
404 | ||
405 | static void test_table_read_api(void) | |
406 | { | |
407 | char **names; | |
408 | struct strbuf buf = STRBUF_INIT; | |
409 | int N = 50; | |
410 | struct reftable_reader rd = { NULL }; | |
411 | struct reftable_block_source source = { NULL }; | |
412 | int err; | |
413 | int i; | |
414 | struct reftable_log_record log = { NULL }; | |
415 | struct reftable_iterator it = { NULL }; | |
416 | ||
417 | write_table(&names, &buf, N, 256, GIT_SHA1_FORMAT_ID); | |
418 | ||
419 | block_source_from_strbuf(&source, &buf); | |
420 | ||
421 | err = init_reader(&rd, &source, "file.ref"); | |
422 | EXPECT_ERR(err); | |
423 | ||
424 | err = reftable_reader_seek_ref(&rd, &it, names[0]); | |
425 | EXPECT_ERR(err); | |
426 | ||
427 | err = reftable_iterator_next_log(&it, &log); | |
428 | EXPECT(err == REFTABLE_API_ERROR); | |
429 | ||
430 | strbuf_release(&buf); | |
431 | for (i = 0; i < N; i++) { | |
432 | reftable_free(names[i]); | |
433 | } | |
434 | reftable_iterator_destroy(&it); | |
435 | reftable_free(names); | |
436 | reader_close(&rd); | |
437 | strbuf_release(&buf); | |
438 | } | |
439 | ||
440 | static void test_table_read_write_seek(int index, int hash_id) | |
441 | { | |
442 | char **names; | |
443 | struct strbuf buf = STRBUF_INIT; | |
444 | int N = 50; | |
445 | struct reftable_reader rd = { NULL }; | |
446 | struct reftable_block_source source = { NULL }; | |
447 | int err; | |
448 | int i = 0; | |
449 | ||
450 | struct reftable_iterator it = { NULL }; | |
451 | struct strbuf pastLast = STRBUF_INIT; | |
452 | struct reftable_ref_record ref = { NULL }; | |
453 | ||
454 | write_table(&names, &buf, N, 256, hash_id); | |
455 | ||
456 | block_source_from_strbuf(&source, &buf); | |
457 | ||
458 | err = init_reader(&rd, &source, "file.ref"); | |
459 | EXPECT_ERR(err); | |
460 | EXPECT(hash_id == reftable_reader_hash_id(&rd)); | |
461 | ||
462 | if (!index) { | |
463 | rd.ref_offsets.index_offset = 0; | |
464 | } else { | |
465 | EXPECT(rd.ref_offsets.index_offset > 0); | |
466 | } | |
467 | ||
468 | for (i = 1; i < N; i++) { | |
469 | int err = reftable_reader_seek_ref(&rd, &it, names[i]); | |
470 | EXPECT_ERR(err); | |
471 | err = reftable_iterator_next_ref(&it, &ref); | |
472 | EXPECT_ERR(err); | |
473 | EXPECT(0 == strcmp(names[i], ref.refname)); | |
474 | EXPECT(REFTABLE_REF_VAL1 == ref.value_type); | |
475 | EXPECT(i == ref.value.val1[0]); | |
476 | ||
477 | reftable_ref_record_release(&ref); | |
478 | reftable_iterator_destroy(&it); | |
479 | } | |
480 | ||
481 | strbuf_addstr(&pastLast, names[N - 1]); | |
482 | strbuf_addstr(&pastLast, "/"); | |
483 | ||
484 | err = reftable_reader_seek_ref(&rd, &it, pastLast.buf); | |
485 | if (err == 0) { | |
486 | struct reftable_ref_record ref = { NULL }; | |
487 | int err = reftable_iterator_next_ref(&it, &ref); | |
488 | EXPECT(err > 0); | |
489 | } else { | |
490 | EXPECT(err > 0); | |
491 | } | |
492 | ||
493 | strbuf_release(&pastLast); | |
494 | reftable_iterator_destroy(&it); | |
495 | ||
496 | strbuf_release(&buf); | |
497 | for (i = 0; i < N; i++) { | |
498 | reftable_free(names[i]); | |
499 | } | |
500 | reftable_free(names); | |
501 | reader_close(&rd); | |
502 | } | |
503 | ||
504 | static void test_table_read_write_seek_linear(void) | |
505 | { | |
506 | test_table_read_write_seek(0, GIT_SHA1_FORMAT_ID); | |
507 | } | |
508 | ||
509 | static void test_table_read_write_seek_linear_sha256(void) | |
510 | { | |
511 | test_table_read_write_seek(0, GIT_SHA256_FORMAT_ID); | |
512 | } | |
513 | ||
514 | static void test_table_read_write_seek_index(void) | |
515 | { | |
516 | test_table_read_write_seek(1, GIT_SHA1_FORMAT_ID); | |
517 | } | |
518 | ||
519 | static void test_table_refs_for(int indexed) | |
520 | { | |
521 | int N = 50; | |
522 | char **want_names = reftable_calloc(sizeof(char *) * (N + 1)); | |
523 | int want_names_len = 0; | |
524 | uint8_t want_hash[GIT_SHA1_RAWSZ]; | |
525 | ||
526 | struct reftable_write_options opts = { | |
527 | .block_size = 256, | |
528 | }; | |
529 | struct reftable_ref_record ref = { NULL }; | |
530 | int i = 0; | |
531 | int n; | |
532 | int err; | |
533 | struct reftable_reader rd; | |
534 | struct reftable_block_source source = { NULL }; | |
535 | ||
536 | struct strbuf buf = STRBUF_INIT; | |
537 | struct reftable_writer *w = | |
538 | reftable_new_writer(&strbuf_add_void, &buf, &opts); | |
539 | ||
540 | struct reftable_iterator it = { NULL }; | |
541 | int j; | |
542 | ||
543 | set_test_hash(want_hash, 4); | |
544 | ||
545 | for (i = 0; i < N; i++) { | |
546 | uint8_t hash[GIT_SHA1_RAWSZ]; | |
547 | char fill[51] = { 0 }; | |
548 | char name[100]; | |
ffc97f1a HWN |
549 | struct reftable_ref_record ref = { NULL }; |
550 | ||
551 | memset(hash, i, sizeof(hash)); | |
552 | memset(fill, 'x', 50); | |
553 | /* Put the variable part in the start */ | |
554 | snprintf(name, sizeof(name), "br%02d%s", i, fill); | |
555 | name[40] = 0; | |
556 | ref.refname = name; | |
557 | ||
ffc97f1a | 558 | ref.value_type = REFTABLE_REF_VAL2; |
b31e3cc6 PS |
559 | set_test_hash(ref.value.val2.value, i / 4); |
560 | set_test_hash(ref.value.val2.target_value, 3 + i / 4); | |
ffc97f1a HWN |
561 | |
562 | /* 80 bytes / entry, so 3 entries per block. Yields 17 | |
563 | */ | |
564 | /* blocks. */ | |
565 | n = reftable_writer_add_ref(w, &ref); | |
566 | EXPECT(n == 0); | |
567 | ||
b31e3cc6 PS |
568 | if (!memcmp(ref.value.val2.value, want_hash, GIT_SHA1_RAWSZ) || |
569 | !memcmp(ref.value.val2.target_value, want_hash, GIT_SHA1_RAWSZ)) { | |
ffc97f1a HWN |
570 | want_names[want_names_len++] = xstrdup(name); |
571 | } | |
572 | } | |
573 | ||
574 | n = reftable_writer_close(w); | |
575 | EXPECT(n == 0); | |
576 | ||
577 | reftable_writer_free(w); | |
578 | w = NULL; | |
579 | ||
580 | block_source_from_strbuf(&source, &buf); | |
581 | ||
582 | err = init_reader(&rd, &source, "file.ref"); | |
583 | EXPECT_ERR(err); | |
584 | if (!indexed) { | |
585 | rd.obj_offsets.is_present = 0; | |
586 | } | |
587 | ||
588 | err = reftable_reader_seek_ref(&rd, &it, ""); | |
589 | EXPECT_ERR(err); | |
590 | reftable_iterator_destroy(&it); | |
591 | ||
592 | err = reftable_reader_refs_for(&rd, &it, want_hash); | |
593 | EXPECT_ERR(err); | |
594 | ||
595 | j = 0; | |
596 | while (1) { | |
597 | int err = reftable_iterator_next_ref(&it, &ref); | |
598 | EXPECT(err >= 0); | |
599 | if (err > 0) { | |
600 | break; | |
601 | } | |
602 | ||
603 | EXPECT(j < want_names_len); | |
604 | EXPECT(0 == strcmp(ref.refname, want_names[j])); | |
605 | j++; | |
606 | reftable_ref_record_release(&ref); | |
607 | } | |
608 | EXPECT(j == want_names_len); | |
609 | ||
610 | strbuf_release(&buf); | |
611 | free_names(want_names); | |
612 | reftable_iterator_destroy(&it); | |
613 | reader_close(&rd); | |
614 | } | |
615 | ||
616 | static void test_table_refs_for_no_index(void) | |
617 | { | |
618 | test_table_refs_for(0); | |
619 | } | |
620 | ||
621 | static void test_table_refs_for_obj_index(void) | |
622 | { | |
623 | test_table_refs_for(1); | |
624 | } | |
625 | ||
626 | static void test_write_empty_table(void) | |
627 | { | |
628 | struct reftable_write_options opts = { 0 }; | |
629 | struct strbuf buf = STRBUF_INIT; | |
630 | struct reftable_writer *w = | |
631 | reftable_new_writer(&strbuf_add_void, &buf, &opts); | |
632 | struct reftable_block_source source = { NULL }; | |
633 | struct reftable_reader *rd = NULL; | |
634 | struct reftable_ref_record rec = { NULL }; | |
635 | struct reftable_iterator it = { NULL }; | |
636 | int err; | |
637 | ||
638 | reftable_writer_set_limits(w, 1, 1); | |
639 | ||
640 | err = reftable_writer_close(w); | |
641 | EXPECT(err == REFTABLE_EMPTY_TABLE_ERROR); | |
642 | reftable_writer_free(w); | |
643 | ||
644 | EXPECT(buf.len == header_size(1) + footer_size(1)); | |
645 | ||
646 | block_source_from_strbuf(&source, &buf); | |
647 | ||
648 | err = reftable_new_reader(&rd, &source, "filename"); | |
649 | EXPECT_ERR(err); | |
650 | ||
651 | err = reftable_reader_seek_ref(rd, &it, ""); | |
652 | EXPECT_ERR(err); | |
653 | ||
654 | err = reftable_iterator_next_ref(&it, &rec); | |
655 | EXPECT(err > 0); | |
656 | ||
657 | reftable_iterator_destroy(&it); | |
658 | reftable_reader_free(rd); | |
659 | strbuf_release(&buf); | |
660 | } | |
661 | ||
b4007fcc HWN |
662 | static void test_write_object_id_min_length(void) |
663 | { | |
664 | struct reftable_write_options opts = { | |
665 | .block_size = 75, | |
666 | }; | |
667 | struct strbuf buf = STRBUF_INIT; | |
668 | struct reftable_writer *w = | |
669 | reftable_new_writer(&strbuf_add_void, &buf, &opts); | |
b4007fcc HWN |
670 | struct reftable_ref_record ref = { |
671 | .update_index = 1, | |
672 | .value_type = REFTABLE_REF_VAL1, | |
7af607c5 | 673 | .value.val1 = {42}, |
b4007fcc HWN |
674 | }; |
675 | int err; | |
676 | int i; | |
677 | ||
678 | reftable_writer_set_limits(w, 1, 1); | |
679 | ||
680 | /* Write the same hash in many refs. If there is only 1 hash, the | |
681 | * disambiguating prefix is length 0 */ | |
682 | for (i = 0; i < 256; i++) { | |
683 | char name[256]; | |
684 | snprintf(name, sizeof(name), "ref%05d", i); | |
685 | ref.refname = name; | |
686 | err = reftable_writer_add_ref(w, &ref); | |
687 | EXPECT_ERR(err); | |
688 | } | |
689 | ||
690 | err = reftable_writer_close(w); | |
691 | EXPECT_ERR(err); | |
73a4c188 | 692 | EXPECT(reftable_writer_stats(w)->object_id_len == 2); |
b4007fcc HWN |
693 | reftable_writer_free(w); |
694 | strbuf_release(&buf); | |
695 | } | |
696 | ||
3c443a02 HWN |
697 | static void test_write_object_id_length(void) |
698 | { | |
699 | struct reftable_write_options opts = { | |
700 | .block_size = 75, | |
701 | }; | |
702 | struct strbuf buf = STRBUF_INIT; | |
703 | struct reftable_writer *w = | |
704 | reftable_new_writer(&strbuf_add_void, &buf, &opts); | |
3c443a02 HWN |
705 | struct reftable_ref_record ref = { |
706 | .update_index = 1, | |
707 | .value_type = REFTABLE_REF_VAL1, | |
7af607c5 | 708 | .value.val1 = {42}, |
3c443a02 HWN |
709 | }; |
710 | int err; | |
711 | int i; | |
712 | ||
713 | reftable_writer_set_limits(w, 1, 1); | |
714 | ||
715 | /* Write the same hash in many refs. If there is only 1 hash, the | |
716 | * disambiguating prefix is length 0 */ | |
717 | for (i = 0; i < 256; i++) { | |
718 | char name[256]; | |
719 | snprintf(name, sizeof(name), "ref%05d", i); | |
720 | ref.refname = name; | |
721 | ref.value.val1[15] = i; | |
722 | err = reftable_writer_add_ref(w, &ref); | |
723 | EXPECT_ERR(err); | |
724 | } | |
725 | ||
726 | err = reftable_writer_close(w); | |
727 | EXPECT_ERR(err); | |
73a4c188 | 728 | EXPECT(reftable_writer_stats(w)->object_id_len == 16); |
3c443a02 HWN |
729 | reftable_writer_free(w); |
730 | strbuf_release(&buf); | |
731 | } | |
732 | ||
14076591 HWN |
733 | static void test_write_empty_key(void) |
734 | { | |
735 | struct reftable_write_options opts = { 0 }; | |
736 | struct strbuf buf = STRBUF_INIT; | |
737 | struct reftable_writer *w = | |
738 | reftable_new_writer(&strbuf_add_void, &buf, &opts); | |
739 | struct reftable_ref_record ref = { | |
740 | .refname = "", | |
741 | .update_index = 1, | |
742 | .value_type = REFTABLE_REF_DELETION, | |
743 | }; | |
744 | int err; | |
745 | ||
746 | reftable_writer_set_limits(w, 1, 1); | |
747 | err = reftable_writer_add_ref(w, &ref); | |
748 | EXPECT(err == REFTABLE_API_ERROR); | |
749 | ||
750 | err = reftable_writer_close(w); | |
751 | EXPECT(err == REFTABLE_EMPTY_TABLE_ERROR); | |
752 | reftable_writer_free(w); | |
753 | strbuf_release(&buf); | |
754 | } | |
755 | ||
ffc97f1a HWN |
756 | static void test_write_key_order(void) |
757 | { | |
758 | struct reftable_write_options opts = { 0 }; | |
759 | struct strbuf buf = STRBUF_INIT; | |
760 | struct reftable_writer *w = | |
761 | reftable_new_writer(&strbuf_add_void, &buf, &opts); | |
762 | struct reftable_ref_record refs[2] = { | |
763 | { | |
764 | .refname = "b", | |
765 | .update_index = 1, | |
766 | .value_type = REFTABLE_REF_SYMREF, | |
767 | .value = { | |
768 | .symref = "target", | |
769 | }, | |
770 | }, { | |
771 | .refname = "a", | |
772 | .update_index = 1, | |
773 | .value_type = REFTABLE_REF_SYMREF, | |
774 | .value = { | |
775 | .symref = "target", | |
776 | }, | |
777 | } | |
778 | }; | |
779 | int err; | |
780 | ||
781 | reftable_writer_set_limits(w, 1, 1); | |
782 | err = reftable_writer_add_ref(w, &refs[0]); | |
783 | EXPECT_ERR(err); | |
784 | err = reftable_writer_add_ref(w, &refs[1]); | |
ffc97f1a HWN |
785 | EXPECT(err == REFTABLE_API_ERROR); |
786 | reftable_writer_close(w); | |
787 | reftable_writer_free(w); | |
788 | strbuf_release(&buf); | |
789 | } | |
790 | ||
ddac9659 PS |
791 | static void test_write_multiple_indices(void) |
792 | { | |
793 | struct reftable_write_options opts = { | |
794 | .block_size = 100, | |
795 | }; | |
796 | struct strbuf writer_buf = STRBUF_INIT, buf = STRBUF_INIT; | |
797 | struct reftable_block_source source = { 0 }; | |
798 | struct reftable_iterator it = { 0 }; | |
799 | const struct reftable_stats *stats; | |
800 | struct reftable_writer *writer; | |
801 | struct reftable_reader *reader; | |
802 | int err, i; | |
803 | ||
804 | writer = reftable_new_writer(&strbuf_add_void, &writer_buf, &opts); | |
805 | reftable_writer_set_limits(writer, 1, 1); | |
806 | for (i = 0; i < 100; i++) { | |
ddac9659 PS |
807 | struct reftable_ref_record ref = { |
808 | .update_index = 1, | |
809 | .value_type = REFTABLE_REF_VAL1, | |
7af607c5 | 810 | .value.val1 = {i}, |
ddac9659 PS |
811 | }; |
812 | ||
813 | strbuf_reset(&buf); | |
814 | strbuf_addf(&buf, "refs/heads/%04d", i); | |
815 | ref.refname = buf.buf, | |
816 | ||
817 | err = reftable_writer_add_ref(writer, &ref); | |
818 | EXPECT_ERR(err); | |
819 | } | |
820 | ||
821 | for (i = 0; i < 100; i++) { | |
822 | unsigned char hash[GIT_SHA1_RAWSZ] = {i}; | |
823 | struct reftable_log_record log = { | |
824 | .update_index = 1, | |
825 | .value_type = REFTABLE_LOG_UPDATE, | |
826 | .value.update = { | |
827 | .old_hash = hash, | |
828 | .new_hash = hash, | |
829 | }, | |
830 | }; | |
831 | ||
832 | strbuf_reset(&buf); | |
833 | strbuf_addf(&buf, "refs/heads/%04d", i); | |
834 | log.refname = buf.buf, | |
835 | ||
836 | err = reftable_writer_add_log(writer, &log); | |
837 | EXPECT_ERR(err); | |
838 | } | |
839 | ||
840 | reftable_writer_close(writer); | |
841 | ||
842 | /* | |
843 | * The written data should be sufficiently large to result in indices | |
844 | * for each of the block types. | |
845 | */ | |
846 | stats = reftable_writer_stats(writer); | |
847 | EXPECT(stats->ref_stats.index_offset > 0); | |
848 | EXPECT(stats->obj_stats.index_offset > 0); | |
849 | EXPECT(stats->log_stats.index_offset > 0); | |
850 | ||
851 | block_source_from_strbuf(&source, &writer_buf); | |
852 | err = reftable_new_reader(&reader, &source, "filename"); | |
853 | EXPECT_ERR(err); | |
854 | ||
855 | /* | |
856 | * Seeking the log uses the log index now. In case there is any | |
857 | * confusion regarding indices we would notice here. | |
858 | */ | |
859 | err = reftable_reader_seek_log(reader, &it, ""); | |
860 | EXPECT_ERR(err); | |
861 | ||
862 | reftable_iterator_destroy(&it); | |
863 | reftable_writer_free(writer); | |
864 | reftable_reader_free(reader); | |
865 | strbuf_release(&writer_buf); | |
866 | strbuf_release(&buf); | |
867 | } | |
868 | ||
ffc97f1a HWN |
869 | static void test_corrupt_table_empty(void) |
870 | { | |
871 | struct strbuf buf = STRBUF_INIT; | |
872 | struct reftable_block_source source = { NULL }; | |
873 | struct reftable_reader rd = { NULL }; | |
874 | int err; | |
875 | ||
876 | block_source_from_strbuf(&source, &buf); | |
877 | err = init_reader(&rd, &source, "file.log"); | |
878 | EXPECT(err == REFTABLE_FORMAT_ERROR); | |
879 | } | |
880 | ||
881 | static void test_corrupt_table(void) | |
882 | { | |
883 | uint8_t zeros[1024] = { 0 }; | |
884 | struct strbuf buf = STRBUF_INIT; | |
885 | struct reftable_block_source source = { NULL }; | |
886 | struct reftable_reader rd = { NULL }; | |
887 | int err; | |
888 | strbuf_add(&buf, zeros, sizeof(zeros)); | |
889 | ||
890 | block_source_from_strbuf(&source, &buf); | |
891 | err = init_reader(&rd, &source, "file.log"); | |
892 | EXPECT(err == REFTABLE_FORMAT_ERROR); | |
893 | strbuf_release(&buf); | |
894 | } | |
895 | ||
896 | int readwrite_test_main(int argc, const char *argv[]) | |
897 | { | |
24d4d38c | 898 | RUN_TEST(test_log_zlib_corruption); |
ffc97f1a HWN |
899 | RUN_TEST(test_corrupt_table); |
900 | RUN_TEST(test_corrupt_table_empty); | |
901 | RUN_TEST(test_log_write_read); | |
902 | RUN_TEST(test_write_key_order); | |
903 | RUN_TEST(test_table_read_write_seek_linear_sha256); | |
904 | RUN_TEST(test_log_buffer_size); | |
905 | RUN_TEST(test_table_write_small_table); | |
906 | RUN_TEST(test_buffer); | |
907 | RUN_TEST(test_table_read_api); | |
908 | RUN_TEST(test_table_read_write_sequential); | |
909 | RUN_TEST(test_table_read_write_seek_linear); | |
910 | RUN_TEST(test_table_read_write_seek_index); | |
911 | RUN_TEST(test_table_refs_for_no_index); | |
912 | RUN_TEST(test_table_refs_for_obj_index); | |
14076591 | 913 | RUN_TEST(test_write_empty_key); |
ffc97f1a | 914 | RUN_TEST(test_write_empty_table); |
0dd44584 | 915 | RUN_TEST(test_log_overflow); |
3c443a02 | 916 | RUN_TEST(test_write_object_id_length); |
b4007fcc | 917 | RUN_TEST(test_write_object_id_min_length); |
ddac9659 | 918 | RUN_TEST(test_write_multiple_indices); |
ffc97f1a HWN |
919 | return 0; |
920 | } |