Git fork
at reftables-rust 595 lines 16 kB view raw
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(&copy, typ), 0); 22 reftable_record_copy_from(&copy, rec, REFTABLE_HASH_SIZE_SHA1); 23 /* do it twice to catch memory leaks */ 24 reftable_record_copy_from(&copy, rec, REFTABLE_HASH_SIZE_SHA1); 25 cl_assert(reftable_record_equal(rec, &copy, 26 REFTABLE_HASH_SIZE_SHA1) != 0); 27 28 reftable_record_release(&copy); 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}