Git fork
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 "unit-test.h"
10#include "lib-reftable.h"
11#include "reftable/basics.h"
12#include "reftable/constants.h"
13#include "reftable/record.h"
14
15static void t_copy(struct reftable_record *rec)
16{
17 struct reftable_record copy;
18 uint8_t typ;
19
20 typ = reftable_record_type(rec);
21 cl_assert_equal_i(reftable_record_init(©, typ), 0);
22 reftable_record_copy_from(©, rec, REFTABLE_HASH_SIZE_SHA1);
23 /* do it twice to catch memory leaks */
24 reftable_record_copy_from(©, rec, REFTABLE_HASH_SIZE_SHA1);
25 cl_assert(reftable_record_equal(rec, ©,
26 REFTABLE_HASH_SIZE_SHA1) != 0);
27
28 reftable_record_release(©);
29}
30
31void test_reftable_record__varint_roundtrip(void)
32{
33 uint64_t inputs[] = { 0,
34 1,
35 27,
36 127,
37 128,
38 257,
39 4096,
40 ((uint64_t)1 << 63),
41 ((uint64_t)1 << 63) + ((uint64_t)1 << 63) - 1 };
42
43 for (size_t i = 0; i < ARRAY_SIZE(inputs); i++) {
44 uint8_t dest[10];
45
46 struct string_view out = {
47 .buf = dest,
48 .len = sizeof(dest),
49 };
50 uint64_t in = inputs[i];
51 int n = put_var_int(&out, in);
52 uint64_t got = 0;
53
54 cl_assert(n > 0);
55 out.len = n;
56 n = get_var_int(&got, &out);
57 cl_assert(n > 0);
58
59 cl_assert_equal_i(got, in);
60 }
61}
62
63void test_reftable_record__varint_overflow(void)
64{
65 unsigned char buf[] = {
66 0xFF, 0xFF, 0xFF, 0xFF,
67 0xFF, 0xFF, 0xFF, 0xFF,
68 0xFF, 0x00,
69 };
70 struct string_view view = {
71 .buf = buf,
72 .len = sizeof(buf),
73 };
74 uint64_t value;
75 cl_assert_equal_i(get_var_int(&value, &view), -1);
76}
77
78static void set_hash(uint8_t *h, int j)
79{
80 for (size_t i = 0; i < hash_size(REFTABLE_HASH_SHA1); i++)
81 h[i] = (j >> i) & 0xff;
82}
83
84void test_reftable_record__ref_record_comparison(void)
85{
86 struct reftable_record in[3] = {
87 {
88 .type = REFTABLE_BLOCK_TYPE_REF,
89 .u.ref.refname = (char *) "refs/heads/master",
90 .u.ref.value_type = REFTABLE_REF_VAL1,
91 },
92 {
93 .type = REFTABLE_BLOCK_TYPE_REF,
94 .u.ref.refname = (char *) "refs/heads/master",
95 .u.ref.value_type = REFTABLE_REF_DELETION,
96 },
97 {
98 .type = REFTABLE_BLOCK_TYPE_REF,
99 .u.ref.refname = (char *) "HEAD",
100 .u.ref.value_type = REFTABLE_REF_SYMREF,
101 .u.ref.value.symref = (char *) "refs/heads/master",
102 },
103 };
104 int cmp;
105
106 cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) == 0);
107 cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0);
108 cl_assert(!cmp);
109
110 cl_assert(reftable_record_equal(&in[1], &in[2],
111 REFTABLE_HASH_SIZE_SHA1) == 0);
112 cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);
113 cl_assert(cmp > 0);
114
115 in[1].u.ref.value_type = in[0].u.ref.value_type;
116 cl_assert(reftable_record_equal(&in[0], &in[1],
117 REFTABLE_HASH_SIZE_SHA1) != 0);
118 cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0);
119 cl_assert(!cmp);
120}
121
122void test_reftable_record__ref_record_compare_name(void)
123{
124 struct reftable_ref_record recs[3] = {
125 {
126 .refname = (char *) "refs/heads/a"
127 },
128 {
129 .refname = (char *) "refs/heads/b"
130 },
131 {
132 .refname = (char *) "refs/heads/a"
133 },
134 };
135
136 cl_assert(reftable_ref_record_compare_name(&recs[0],
137 &recs[1]) < 0);
138 cl_assert(reftable_ref_record_compare_name(&recs[1],
139 &recs[0]) > 0);
140 cl_assert_equal_i(reftable_ref_record_compare_name(&recs[0],
141 &recs[2]), 0);
142}
143
144void test_reftable_record__ref_record_roundtrip(void)
145{
146 struct reftable_buf scratch = REFTABLE_BUF_INIT;
147
148 for (int i = REFTABLE_REF_DELETION; i < REFTABLE_NR_REF_VALUETYPES; i++) {
149 struct reftable_record in = {
150 .type = REFTABLE_BLOCK_TYPE_REF,
151 .u.ref.value_type = i,
152 };
153 struct reftable_record out = { .type = REFTABLE_BLOCK_TYPE_REF };
154 struct reftable_buf key = REFTABLE_BUF_INIT;
155 uint8_t buffer[1024] = { 0 };
156 struct string_view dest = {
157 .buf = buffer,
158 .len = sizeof(buffer),
159 };
160 int n, m;
161
162 in.u.ref.value_type = i;
163 switch (i) {
164 case REFTABLE_REF_DELETION:
165 break;
166 case REFTABLE_REF_VAL1:
167 set_hash(in.u.ref.value.val1, 1);
168 break;
169 case REFTABLE_REF_VAL2:
170 set_hash(in.u.ref.value.val2.value, 1);
171 set_hash(in.u.ref.value.val2.target_value, 2);
172 break;
173 case REFTABLE_REF_SYMREF:
174 in.u.ref.value.symref = xstrdup("target");
175 break;
176 }
177 in.u.ref.refname = xstrdup("refs/heads/master");
178
179 t_copy(&in);
180
181 cl_assert_equal_i(reftable_record_val_type(&in), i);
182 cl_assert_equal_i(reftable_record_is_deletion(&in),
183 i == REFTABLE_REF_DELETION);
184
185 reftable_record_key(&in, &key);
186 n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);
187 cl_assert(n > 0);
188
189 /* decode into a non-zero reftable_record to test for leaks. */
190 m = reftable_record_decode(&out, key, i, dest, REFTABLE_HASH_SIZE_SHA1, &scratch);
191 cl_assert_equal_i(n, m);
192
193 cl_assert(reftable_ref_record_equal(&in.u.ref,
194 &out.u.ref,
195 REFTABLE_HASH_SIZE_SHA1) != 0);
196 reftable_record_release(&in);
197
198 reftable_buf_release(&key);
199 reftable_record_release(&out);
200 }
201
202 reftable_buf_release(&scratch);
203}
204
205void test_reftable_record__log_record_comparison(void)
206{
207 struct reftable_record in[3] = {
208 {
209 .type = REFTABLE_BLOCK_TYPE_LOG,
210 .u.log.refname = (char *) "refs/heads/master",
211 .u.log.update_index = 42,
212 },
213 {
214 .type = REFTABLE_BLOCK_TYPE_LOG,
215 .u.log.refname = (char *) "refs/heads/master",
216 .u.log.update_index = 22,
217 },
218 {
219 .type = REFTABLE_BLOCK_TYPE_LOG,
220 .u.log.refname = (char *) "refs/heads/main",
221 .u.log.update_index = 22,
222 },
223 };
224 int cmp;
225
226 cl_assert_equal_i(reftable_record_equal(&in[0], &in[1],
227 REFTABLE_HASH_SIZE_SHA1), 0);
228 cl_assert_equal_i(reftable_record_equal(&in[1], &in[2],
229 REFTABLE_HASH_SIZE_SHA1), 0);
230 cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);
231 cl_assert(cmp > 0);
232 /* comparison should be reversed for equal keys, because
233 * comparison is now performed on the basis of update indices */
234 cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0);
235 cl_assert(cmp < 0);
236
237 in[1].u.log.update_index = in[0].u.log.update_index;
238 cl_assert(reftable_record_equal(&in[0], &in[1],
239 REFTABLE_HASH_SIZE_SHA1) != 0);
240 cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0);
241}
242
243void test_reftable_record__log_record_compare_key(void)
244{
245 struct reftable_log_record logs[3] = {
246 {
247 .refname = (char *) "refs/heads/a",
248 .update_index = 1,
249 },
250 {
251 .refname = (char *) "refs/heads/b",
252 .update_index = 2,
253 },
254 {
255 .refname = (char *) "refs/heads/a",
256 .update_index = 3,
257 },
258 };
259
260 cl_assert(reftable_log_record_compare_key(&logs[0],
261 &logs[1]) < 0);
262 cl_assert(reftable_log_record_compare_key(&logs[1],
263 &logs[0]) > 0);
264
265 logs[1].update_index = logs[0].update_index;
266 cl_assert(reftable_log_record_compare_key(&logs[0],
267 &logs[1]) < 0);
268
269 cl_assert(reftable_log_record_compare_key(&logs[0],
270 &logs[2]) > 0);
271 cl_assert(reftable_log_record_compare_key(&logs[2],
272 &logs[0]) < 0);
273 logs[2].update_index = logs[0].update_index;
274 cl_assert_equal_i(reftable_log_record_compare_key(&logs[0], &logs[2]), 0);
275}
276
277void test_reftable_record__log_record_roundtrip(void)
278{
279 struct reftable_log_record in[] = {
280 {
281 .refname = xstrdup("refs/heads/master"),
282 .update_index = 42,
283 .value_type = REFTABLE_LOG_UPDATE,
284 .value = {
285 .update = {
286 .name = xstrdup("han-wen"),
287 .email = xstrdup("hanwen@google.com"),
288 .message = xstrdup("test"),
289 .time = 1577123507,
290 .tz_offset = 100,
291 },
292 }
293 },
294 {
295 .refname = xstrdup("refs/heads/master"),
296 .update_index = 22,
297 .value_type = REFTABLE_LOG_DELETION,
298 },
299 {
300 .refname = xstrdup("branch"),
301 .update_index = 33,
302 .value_type = REFTABLE_LOG_UPDATE,
303 }
304 };
305 struct reftable_buf scratch = REFTABLE_BUF_INIT;
306 set_hash(in[0].value.update.new_hash, 1);
307 set_hash(in[0].value.update.old_hash, 2);
308 set_hash(in[2].value.update.new_hash, 3);
309 set_hash(in[2].value.update.old_hash, 4);
310
311 cl_assert_equal_i(reftable_log_record_is_deletion(&in[0]), 0);
312 cl_assert(reftable_log_record_is_deletion(&in[1]) != 0);
313 cl_assert_equal_i(reftable_log_record_is_deletion(&in[2]), 0);
314
315 for (size_t i = 0; i < ARRAY_SIZE(in); i++) {
316 struct reftable_record rec = { .type = REFTABLE_BLOCK_TYPE_LOG };
317 struct reftable_buf key = REFTABLE_BUF_INIT;
318 uint8_t buffer[1024] = { 0 };
319 struct string_view dest = {
320 .buf = buffer,
321 .len = sizeof(buffer),
322 };
323 /* populate out, to check for leaks. */
324 struct reftable_record out = {
325 .type = REFTABLE_BLOCK_TYPE_LOG,
326 .u.log = {
327 .refname = xstrdup("old name"),
328 .value_type = REFTABLE_LOG_UPDATE,
329 .value = {
330 .update = {
331 .name = xstrdup("old name"),
332 .email = xstrdup("old@email"),
333 .message = xstrdup("old message"),
334 },
335 },
336 },
337 };
338 int n, m, valtype;
339
340 rec.u.log = in[i];
341
342 t_copy(&rec);
343
344 reftable_record_key(&rec, &key);
345
346 n = reftable_record_encode(&rec, dest, REFTABLE_HASH_SIZE_SHA1);
347 cl_assert(n >= 0);
348 valtype = reftable_record_val_type(&rec);
349 m = reftable_record_decode(&out, key, valtype, dest,
350 REFTABLE_HASH_SIZE_SHA1, &scratch);
351 cl_assert_equal_i(n, m);
352
353 cl_assert(reftable_log_record_equal(&in[i], &out.u.log,
354 REFTABLE_HASH_SIZE_SHA1) != 0);
355 reftable_log_record_release(&in[i]);
356 reftable_buf_release(&key);
357 reftable_record_release(&out);
358 }
359
360 reftable_buf_release(&scratch);
361}
362
363void test_reftable_record__key_roundtrip(void)
364{
365 uint8_t buffer[1024] = { 0 };
366 struct string_view dest = {
367 .buf = buffer,
368 .len = sizeof(buffer),
369 };
370 struct reftable_buf last_key = REFTABLE_BUF_INIT;
371 struct reftable_buf key = REFTABLE_BUF_INIT;
372 struct reftable_buf roundtrip = REFTABLE_BUF_INIT;
373 int restart;
374 uint8_t extra;
375 int n, m;
376 uint8_t rt_extra;
377
378 cl_assert_equal_i(reftable_buf_addstr(&last_key,
379 "refs/heads/master"), 0);
380 cl_assert_equal_i(reftable_buf_addstr(&key,
381 "refs/tags/bla"), 0);
382 extra = 6;
383 n = reftable_encode_key(&restart, dest, last_key, key, extra);
384 cl_assert(!restart);
385 cl_assert(n > 0);
386
387 cl_assert_equal_i(reftable_buf_addstr(&roundtrip,
388 "refs/heads/master"), 0);
389 m = reftable_decode_key(&roundtrip, &rt_extra, dest);
390 cl_assert_equal_i(n, m);
391 cl_assert_equal_i(reftable_buf_cmp(&key, &roundtrip), 0);
392 cl_assert_equal_i(rt_extra, extra);
393
394 reftable_buf_release(&last_key);
395 reftable_buf_release(&key);
396 reftable_buf_release(&roundtrip);
397}
398
399void test_reftable_record__obj_record_comparison(void)
400{
401
402 uint8_t id_bytes[] = { 0, 1, 2, 3, 4, 5, 6 };
403 uint64_t offsets[] = { 0, 16, 32, 48, 64, 80, 96, 112};
404 struct reftable_record in[3] = {
405 {
406 .type = REFTABLE_BLOCK_TYPE_OBJ,
407 .u.obj.hash_prefix = id_bytes,
408 .u.obj.hash_prefix_len = 7,
409 .u.obj.offsets = offsets,
410 .u.obj.offset_len = 8,
411 },
412 {
413 .type = REFTABLE_BLOCK_TYPE_OBJ,
414 .u.obj.hash_prefix = id_bytes,
415 .u.obj.hash_prefix_len = 7,
416 .u.obj.offsets = offsets,
417 .u.obj.offset_len = 5,
418 },
419 {
420 .type = REFTABLE_BLOCK_TYPE_OBJ,
421 .u.obj.hash_prefix = id_bytes,
422 .u.obj.hash_prefix_len = 5,
423 },
424 };
425 int cmp;
426
427 cl_assert_equal_i(reftable_record_equal(&in[0], &in[1],
428 REFTABLE_HASH_SIZE_SHA1), 0);
429 cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0);
430 cl_assert(!cmp);
431
432 cl_assert_equal_i(reftable_record_equal(&in[1], &in[2],
433 REFTABLE_HASH_SIZE_SHA1), 0);
434 cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);
435 cl_assert(cmp > 0);
436
437 in[1].u.obj.offset_len = in[0].u.obj.offset_len;
438 cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) != 0);
439 cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0);
440 cl_assert(!cmp);
441}
442
443void test_reftable_record__obj_record_roundtrip(void)
444{
445 uint8_t testHash1[REFTABLE_HASH_SIZE_SHA1] = { 1, 2, 3, 4, 0 };
446 uint64_t till9[] = { 1, 2, 3, 4, 500, 600, 700, 800, 9000 };
447 struct reftable_obj_record recs[3] = {
448 {
449 .hash_prefix = testHash1,
450 .hash_prefix_len = 5,
451 .offsets = till9,
452 .offset_len = 3,
453 },
454 {
455 .hash_prefix = testHash1,
456 .hash_prefix_len = 5,
457 .offsets = till9,
458 .offset_len = 9,
459 },
460 {
461 .hash_prefix = testHash1,
462 .hash_prefix_len = 5,
463 },
464 };
465 struct reftable_buf scratch = REFTABLE_BUF_INIT;
466
467 for (size_t i = 0; i < ARRAY_SIZE(recs); i++) {
468 uint8_t buffer[1024] = { 0 };
469 struct string_view dest = {
470 .buf = buffer,
471 .len = sizeof(buffer),
472 };
473 struct reftable_record in = {
474 .type = REFTABLE_BLOCK_TYPE_OBJ,
475 .u = {
476 .obj = recs[i],
477 },
478 };
479 struct reftable_buf key = REFTABLE_BUF_INIT;
480 struct reftable_record out = { .type = REFTABLE_BLOCK_TYPE_OBJ };
481 int n, m;
482 uint8_t extra;
483
484 cl_assert_equal_i(reftable_record_is_deletion(&in), 0);
485 t_copy(&in);
486 reftable_record_key(&in, &key);
487 n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);
488 cl_assert(n > 0);
489 extra = reftable_record_val_type(&in);
490 m = reftable_record_decode(&out, key, extra, dest,
491 REFTABLE_HASH_SIZE_SHA1, &scratch);
492 cl_assert_equal_i(n, m);
493
494 cl_assert(reftable_record_equal(&in, &out,
495 REFTABLE_HASH_SIZE_SHA1) != 0);
496 reftable_buf_release(&key);
497 reftable_record_release(&out);
498 }
499
500 reftable_buf_release(&scratch);
501}
502
503void test_reftable_record__index_record_comparison(void)
504{
505 struct reftable_record in[3] = {
506 {
507 .type = REFTABLE_BLOCK_TYPE_INDEX,
508 .u.idx.offset = 22,
509 .u.idx.last_key = REFTABLE_BUF_INIT,
510 },
511 {
512 .type = REFTABLE_BLOCK_TYPE_INDEX,
513 .u.idx.offset = 32,
514 .u.idx.last_key = REFTABLE_BUF_INIT,
515 },
516 {
517 .type = REFTABLE_BLOCK_TYPE_INDEX,
518 .u.idx.offset = 32,
519 .u.idx.last_key = REFTABLE_BUF_INIT,
520 },
521 };
522 int cmp;
523
524 cl_assert_equal_i(reftable_buf_addstr(&in[0].u.idx.last_key,
525 "refs/heads/master"), 0);
526 cl_assert_equal_i(reftable_buf_addstr(&in[1].u.idx.last_key, "refs/heads/master"), 0);
527 cl_assert(reftable_buf_addstr(&in[2].u.idx.last_key,
528 "refs/heads/branch") == 0);
529
530 cl_assert_equal_i(reftable_record_equal(&in[0], &in[1],
531 REFTABLE_HASH_SIZE_SHA1), 0);
532 cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0);
533 cl_assert(!cmp);
534
535 cl_assert_equal_i(reftable_record_equal(&in[1], &in[2],
536 REFTABLE_HASH_SIZE_SHA1), 0);
537 cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);
538 cl_assert(cmp > 0);
539
540 in[1].u.idx.offset = in[0].u.idx.offset;
541 cl_assert(reftable_record_equal(&in[0], &in[1],
542 REFTABLE_HASH_SIZE_SHA1) != 0);
543 cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0);
544 cl_assert(!cmp);
545
546 for (size_t i = 0; i < ARRAY_SIZE(in); i++)
547 reftable_record_release(&in[i]);
548}
549
550void test_reftable_record__index_record_roundtrip(void)
551{
552 struct reftable_record in = {
553 .type = REFTABLE_BLOCK_TYPE_INDEX,
554 .u.idx = {
555 .offset = 42,
556 .last_key = REFTABLE_BUF_INIT,
557 },
558 };
559 uint8_t buffer[1024] = { 0 };
560 struct string_view dest = {
561 .buf = buffer,
562 .len = sizeof(buffer),
563 };
564 struct reftable_buf scratch = REFTABLE_BUF_INIT;
565 struct reftable_buf key = REFTABLE_BUF_INIT;
566 struct reftable_record out = {
567 .type = REFTABLE_BLOCK_TYPE_INDEX,
568 .u.idx = { .last_key = REFTABLE_BUF_INIT },
569 };
570 int n, m;
571 uint8_t extra;
572
573 cl_assert_equal_i(reftable_buf_addstr(&in.u.idx.last_key,
574 "refs/heads/master"), 0);
575 reftable_record_key(&in, &key);
576 t_copy(&in);
577
578 cl_assert_equal_i(reftable_record_is_deletion(&in), 0);
579 cl_assert_equal_i(reftable_buf_cmp(&key, &in.u.idx.last_key), 0);
580 n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);
581 cl_assert(n > 0);
582
583 extra = reftable_record_val_type(&in);
584 m = reftable_record_decode(&out, key, extra, dest,
585 REFTABLE_HASH_SIZE_SHA1, &scratch);
586 cl_assert_equal_i(m, n);
587
588 cl_assert(reftable_record_equal(&in, &out,
589 REFTABLE_HASH_SIZE_SHA1) != 0);
590
591 reftable_record_release(&out);
592 reftable_buf_release(&key);
593 reftable_buf_release(&scratch);
594 reftable_buf_release(&in.u.idx.last_key);
595}