1 /* $NetBSD: timespecops.c,v 1.3 2020/05/25 20:47:36 christos Exp $ */ 2 3 #include "config.h" 4 5 #include "ntp_types.h" 6 #include "ntp_fp.h" 7 #include "timespecops.h" 8 9 #include "unity.h" 10 11 #include <math.h> 12 #include <string.h> 13 14 15 #define TEST_ASSERT_EQUAL_timespec(a, b) { \ 16 TEST_ASSERT_EQUAL_MESSAGE(a.tv_sec, b.tv_sec, "Field tv_sec"); \ 17 TEST_ASSERT_EQUAL_MESSAGE(a.tv_nsec, b.tv_nsec, "Field tv_nsec"); \ 18 } 19 20 21 #define TEST_ASSERT_EQUAL_l_fp(a, b) { \ 22 TEST_ASSERT_EQUAL_MESSAGE(a.l_i, b.l_i, "Field l_i"); \ 23 TEST_ASSERT_EQUAL_UINT_MESSAGE(a.l_uf, b.l_uf, "Field l_uf"); \ 24 } 25 26 27 static u_int32 my_tick_to_tsf(u_int32 ticks); 28 static u_int32 my_tsf_to_tick(u_int32 tsf); 29 30 31 // that's it... 32 struct lfpfracdata { 33 long nsec; 34 u_int32 frac; 35 }; 36 37 38 void setUp(void); 39 void test_Helpers1(void); 40 void test_Normalise(void); 41 void test_SignNoFrac(void); 42 void test_SignWithFrac(void); 43 void test_CmpFracEQ(void); 44 void test_CmpFracGT(void); 45 void test_CmpFracLT(void); 46 void test_AddFullNorm(void); 47 void test_AddFullOflow1(void); 48 void test_AddNsecNorm(void); 49 void test_AddNsecOflow1(void); 50 void test_SubFullNorm(void); 51 void test_SubFullOflow(void); 52 void test_SubNsecNorm(void); 53 void test_SubNsecOflow(void); 54 void test_Neg(void); 55 void test_AbsNoFrac(void); 56 void test_AbsWithFrac(void); 57 void test_Helpers2(void); 58 void test_ToLFPbittest(void); 59 void test_ToLFPrelPos(void); 60 void test_ToLFPrelNeg(void); 61 void test_ToLFPabs(void); 62 void test_FromLFPbittest(void); 63 void test_FromLFPrelPos(void); 64 void test_FromLFPrelNeg(void); 65 void test_LFProundtrip(void); 66 void test_ToString(void); 67 68 const bool timespec_isValid(struct timespec V); 69 struct timespec timespec_init(time_t hi, long lo); 70 l_fp l_fp_init(int32 i, u_int32 f); 71 bool AssertFpClose(const l_fp m, const l_fp n, const l_fp limit); 72 bool AssertTimespecClose(const struct timespec m, 73 const struct timespec n, 74 const struct timespec limit); 75 76 77 //***************************MY CUSTOM FUNCTIONS*************************** 78 79 80 void 81 setUp(void) 82 { 83 init_lib(); 84 85 return; 86 } 87 88 89 const bool 90 timespec_isValid(struct timespec V) 91 { 92 93 return V.tv_nsec >= 0 && V.tv_nsec < 1000000000; 94 } 95 96 97 struct timespec 98 timespec_init(time_t hi, long lo) 99 { 100 struct timespec V; 101 102 V.tv_sec = hi; 103 V.tv_nsec = lo; 104 105 return V; 106 } 107 108 109 l_fp 110 l_fp_init(int32 i, u_int32 f) 111 { 112 l_fp temp; 113 114 temp.l_i = i; 115 temp.l_uf = f; 116 117 return temp; 118 } 119 120 121 bool 122 AssertFpClose(const l_fp m, const l_fp n, const l_fp limit) 123 { 124 l_fp diff; 125 126 if (L_ISGEQ(&m, &n)) { 127 diff = m; 128 L_SUB(&diff, &n); 129 } else { 130 diff = n; 131 L_SUB(&diff, &m); 132 } 133 if (L_ISGEQ(&limit, &diff)) { 134 return TRUE; 135 } 136 else { 137 printf("m_expr which is %s \nand\nn_expr which is %s\nare not close; diff=%susec\n", 138 lfptoa(&m, 10), lfptoa(&n, 10), lfptoa(&diff, 10)); 139 return FALSE; 140 } 141 } 142 143 144 bool 145 AssertTimespecClose(const struct timespec m, const struct timespec n, 146 const struct timespec limit) 147 { 148 struct timespec diff; 149 150 diff = abs_tspec(sub_tspec(m, n)); 151 if (cmp_tspec(limit, diff) >= 0) 152 return TRUE; 153 154 printf("m_expr which is %lld.%09lu \nand\n" 155 "n_expr which is %lld.%09lu\nare not close; diff=%lld.%09lunsec\n", 156 (long long)m.tv_sec, m.tv_nsec, 157 (long long)n.tv_sec, n.tv_nsec, 158 (long long)diff.tv_sec, diff.tv_nsec); 159 return FALSE; 160 } 161 162 //----------------------------------------------- 163 164 static const struct lfpfracdata fdata[] = { 165 { 0, 0x00000000 }, { 2218896, 0x00916ae6 }, 166 { 16408100, 0x0433523d }, { 125000000, 0x20000000 }, 167 { 250000000, 0x40000000 }, { 287455871, 0x4996b53d }, 168 { 375000000, 0x60000000 }, { 500000000, 0x80000000 }, 169 { 518978897, 0x84dbcd0e }, { 563730222, 0x90509fb3 }, 170 { 563788007, 0x9054692c }, { 583289882, 0x95527c57 }, 171 { 607074509, 0x9b693c2a }, { 625000000, 0xa0000000 }, 172 { 645184059, 0xa52ac851 }, { 676497788, 0xad2ef583 }, 173 { 678910895, 0xadcd1abb }, { 679569625, 0xadf84663 }, 174 { 690926741, 0xb0e0932d }, { 705656483, 0xb4a5e73d }, 175 { 723553854, 0xb93ad34c }, { 750000000, 0xc0000000 }, 176 { 763550253, 0xc3780785 }, { 775284917, 0xc6791284 }, 177 { 826190764, 0xd3813ce8 }, { 875000000, 0xe0000000 }, 178 { 956805507, 0xf4f134a9 }, { 982570733, 0xfb89c16c } 179 }; 180 181 182 u_int32 183 my_tick_to_tsf(u_int32 ticks) 184 { 185 // convert nanoseconds to l_fp fractional units, using double 186 // precision float calculations or, if available, 64bit integer 187 // arithmetic. This should give the precise fraction, rounded to 188 // the nearest representation. 189 190 #ifdef HAVE_U_INT64 191 return (u_int32)((( ((u_int64)(ticks)) << 32) + 500000000) / 1000000000); 192 #else 193 return (u_int32)((double(ticks)) * 4.294967296 + 0.5); 194 #endif 195 // And before you ask: if ticks >= 1000000000, the result is 196 // truncated nonsense, so don't use it out-of-bounds. 197 } 198 199 200 u_int32 201 my_tsf_to_tick(u_int32 tsf) 202 { 203 204 // Inverse operation: converts fraction to microseconds. 205 #ifdef HAVE_U_INT64 206 return (u_int32)(( ((u_int64)(tsf)) * 1000000000 + 0x80000000) >> 32); 207 #else 208 return (u_int32)(double(tsf) / 4.294967296 + 0.5); 209 #endif 210 // Beware: The result might be 10^9 due to rounding! 211 } 212 213 214 215 // --------------------------------------------------------------------- 216 // test support stuff -- part 1 217 // --------------------------------------------------------------------- 218 219 void 220 test_Helpers1(void) 221 { 222 struct timespec x; 223 224 for (x.tv_sec = -2; x.tv_sec < 3; x.tv_sec++) { 225 x.tv_nsec = -1; 226 TEST_ASSERT_FALSE(timespec_isValid(x)); 227 x.tv_nsec = 0; 228 TEST_ASSERT_TRUE(timespec_isValid(x)); 229 x.tv_nsec = 999999999; 230 TEST_ASSERT_TRUE(timespec_isValid(x)); 231 x.tv_nsec = 1000000000; 232 TEST_ASSERT_FALSE(timespec_isValid(x)); 233 } 234 235 return; 236 } 237 238 239 //---------------------------------------------------------------------- 240 // test normalisation 241 //---------------------------------------------------------------------- 242 243 void 244 test_Normalise(void) 245 { 246 long ns; 247 248 for ( ns = -2000000000; ns <= 2000000000; ns += 10000000) { 249 struct timespec x = timespec_init(0, ns); 250 251 x = normalize_tspec(x); 252 TEST_ASSERT_TRUE(timespec_isValid(x)); 253 } 254 255 return; 256 } 257 258 //---------------------------------------------------------------------- 259 // test classification 260 //---------------------------------------------------------------------- 261 262 void 263 test_SignNoFrac(void) 264 { 265 // sign test, no fraction 266 int i; 267 268 for (i = -4; i <= 4; ++i) { 269 struct timespec a = timespec_init(i, 0); 270 int E = (i > 0) - (i < 0); 271 int r = test_tspec(a); 272 273 TEST_ASSERT_EQUAL(E, r); 274 } 275 276 return; 277 } 278 279 280 void 281 test_SignWithFrac(void) 282 { 283 // sign test, with fraction 284 int i; 285 286 for (i = -4; i <= 4; ++i) { 287 struct timespec a = timespec_init(i, 10); 288 int E = (i >= 0) - (i < 0); 289 int r = test_tspec(a); 290 291 TEST_ASSERT_EQUAL(E, r); 292 } 293 294 return; 295 } 296 297 //---------------------------------------------------------------------- 298 // test compare 299 //---------------------------------------------------------------------- 300 void 301 test_CmpFracEQ(void) 302 { 303 // fractions are equal 304 int i, j; 305 for (i = -4; i <= 4; ++i) 306 for (j = -4; j <= 4; ++j) { 307 struct timespec a = timespec_init( i , 200); 308 struct timespec b = timespec_init( j , 200); 309 int E = (i > j) - (i < j); 310 int r = cmp_tspec_denorm(a, b); 311 312 TEST_ASSERT_EQUAL(E, r); 313 } 314 315 return; 316 } 317 318 319 void 320 test_CmpFracGT(void) 321 { 322 // fraction a bigger fraction b 323 int i, j; 324 325 for (i = -4; i <= 4; ++i) 326 for (j = -4; j <= 4; ++j) { 327 struct timespec a = timespec_init(i, 999999800); 328 struct timespec b = timespec_init(j, 200); 329 int E = (i >= j) - (i < j); 330 int r = cmp_tspec_denorm(a, b); 331 332 TEST_ASSERT_EQUAL(E, r); 333 } 334 335 return; 336 } 337 338 339 void 340 test_CmpFracLT(void) 341 { 342 // fraction a less fraction b 343 int i, j; 344 345 for (i = -4; i <= 4; ++i) 346 for (j = -4; j <= 4; ++j) { 347 struct timespec a = timespec_init(i, 200); 348 struct timespec b = timespec_init(j, 999999800); 349 int E = (i > j) - (i <= j); 350 int r = cmp_tspec_denorm(a, b); 351 352 TEST_ASSERT_EQUAL(E, r); 353 } 354 355 return; 356 } 357 358 //---------------------------------------------------------------------- 359 // Test addition (sum) 360 //---------------------------------------------------------------------- 361 362 void 363 test_AddFullNorm(void) 364 { 365 int i, j; 366 367 for (i = -4; i <= 4; ++i) 368 for (j = -4; j <= 4; ++j) { 369 struct timespec a = timespec_init(i, 200); 370 struct timespec b = timespec_init(j, 400); 371 struct timespec E = timespec_init(i + j, 200 + 400); 372 struct timespec c; 373 374 c = add_tspec(a, b); 375 TEST_ASSERT_EQUAL_timespec(E, c); 376 } 377 378 return; 379 } 380 381 382 void 383 test_AddFullOflow1(void) 384 { 385 int i, j; 386 387 for (i = -4; i <= 4; ++i) 388 for (j = -4; j <= 4; ++j) { 389 struct timespec a = timespec_init(i, 200); 390 struct timespec b = timespec_init(j, 999999900); 391 struct timespec E = timespec_init(i + j + 1, 100); 392 struct timespec c; 393 394 c = add_tspec(a, b); 395 TEST_ASSERT_EQUAL_timespec(E, c); 396 } 397 398 return; 399 } 400 401 402 void 403 test_AddNsecNorm(void) { 404 int i; 405 406 for (i = -4; i <= 4; ++i) { 407 struct timespec a = timespec_init(i, 200); 408 struct timespec E = timespec_init(i, 600); 409 struct timespec c; 410 411 c = add_tspec_ns(a, 600 - 200); 412 TEST_ASSERT_EQUAL_timespec(E, c); 413 } 414 415 return; 416 } 417 418 419 void 420 test_AddNsecOflow1(void) 421 { 422 int i; 423 424 for (i = -4; i <= 4; ++i) { 425 struct timespec a = timespec_init(i, 200); 426 struct timespec E = timespec_init(i + 1, 100); 427 struct timespec c; 428 429 c = add_tspec_ns(a, NANOSECONDS - 100); 430 TEST_ASSERT_EQUAL_timespec(E, c); 431 } 432 433 return; 434 } 435 436 //---------------------------------------------------------------------- 437 // test subtraction (difference) 438 //---------------------------------------------------------------------- 439 440 void 441 test_SubFullNorm(void) 442 { 443 int i, j; 444 445 for (i = -4; i <= 4; ++i) 446 for (j = -4; j <= 4; ++j) { 447 struct timespec a = timespec_init( i , 600); 448 struct timespec b = timespec_init( j , 400); 449 struct timespec E = timespec_init(i-j, 200); 450 struct timespec c; 451 452 c = sub_tspec(a, b); 453 TEST_ASSERT_EQUAL_timespec(E, c); 454 } 455 456 return; 457 } 458 459 460 void 461 test_SubFullOflow(void) 462 { 463 int i, j; 464 465 for (i = -4; i <= 4; ++i) 466 for (j = -4; j <= 4; ++j) { 467 struct timespec a = timespec_init(i, 100); 468 struct timespec b = timespec_init(j, 999999900); 469 struct timespec E = timespec_init(i - j - 1, 200); 470 struct timespec c; 471 472 c = sub_tspec(a, b); 473 TEST_ASSERT_EQUAL_timespec(E, c); 474 } 475 476 return; 477 } 478 479 480 void 481 test_SubNsecNorm(void) 482 { 483 int i; 484 485 for (i = -4; i <= 4; ++i) { 486 struct timespec a = timespec_init(i, 600); 487 struct timespec E = timespec_init(i, 200); 488 struct timespec c; 489 490 c = sub_tspec_ns(a, 600 - 200); 491 TEST_ASSERT_EQUAL_timespec(E, c); 492 } 493 494 return; 495 } 496 497 498 void 499 test_SubNsecOflow(void) 500 { 501 int i; 502 503 for (i = -4; i <= 4; ++i) { 504 struct timespec a = timespec_init( i , 100); 505 struct timespec E = timespec_init(i-1, 200); 506 struct timespec c; 507 508 c = sub_tspec_ns(a, NANOSECONDS - 100); 509 TEST_ASSERT_EQUAL_timespec(E, c); 510 } 511 512 return; 513 } 514 515 //---------------------------------------------------------------------- 516 // test negation 517 //---------------------------------------------------------------------- 518 519 520 void 521 test_Neg(void) 522 { 523 int i; 524 525 for (i = -4; i <= 4; ++i) { 526 struct timespec a = timespec_init(i, 100); 527 struct timespec b; 528 struct timespec c; 529 530 b = neg_tspec(a); 531 c = add_tspec(a, b); 532 TEST_ASSERT_EQUAL(0, test_tspec(c)); 533 } 534 535 return; 536 } 537 538 //---------------------------------------------------------------------- 539 // test abs value 540 //---------------------------------------------------------------------- 541 542 void 543 test_AbsNoFrac(void) 544 { 545 int i; 546 547 for (i = -4; i <= 4; ++i) { 548 struct timespec a = timespec_init(i , 0); 549 struct timespec b; 550 551 b = abs_tspec(a); 552 TEST_ASSERT_EQUAL((i != 0), test_tspec(b)); 553 } 554 555 return; 556 } 557 558 559 void 560 test_AbsWithFrac(void) 561 { 562 int i; 563 564 for (i = -4; i <= 4; ++i) { 565 struct timespec a = timespec_init(i, 100); 566 struct timespec b; 567 568 b = abs_tspec(a); 569 TEST_ASSERT_EQUAL(1, test_tspec(b)); 570 } 571 572 return; 573 } 574 575 // --------------------------------------------------------------------- 576 // test support stuff -- part 2 577 // --------------------------------------------------------------------- 578 579 void 580 test_Helpers2(void) 581 { 582 struct timespec limit = timespec_init(0, 2); 583 struct timespec x, y; 584 long i; 585 586 for (x.tv_sec = -2; x.tv_sec < 3; x.tv_sec++) 587 for (x.tv_nsec = 1; 588 x.tv_nsec < 1000000000; 589 x.tv_nsec += 499999999) { 590 for (i = -4; i < 5; ++i) { 591 y = x; 592 y.tv_nsec += i; 593 if (i >= -2 && i <= 2) { 594 TEST_ASSERT_TRUE(AssertTimespecClose(x, y, limit)); 595 } 596 else 597 { 598 TEST_ASSERT_FALSE(AssertTimespecClose(x, y, limit)); 599 } 600 } 601 } 602 603 return; 604 } 605 606 //---------------------------------------------------------------------- 607 // conversion to l_fp 608 //---------------------------------------------------------------------- 609 610 void 611 test_ToLFPbittest(void) 612 { 613 l_fp lfpClose = l_fp_init(0, 1); 614 u_int32 i; 615 616 for (i = 0; i < 1000000000; i+=1000) { 617 struct timespec a = timespec_init(1, i); 618 l_fp E= l_fp_init(1, my_tick_to_tsf(i)); 619 l_fp r; 620 621 r = tspec_intv_to_lfp(a); 622 TEST_ASSERT_TRUE(AssertFpClose(E, r, lfpClose)); 623 } 624 625 return; 626 } 627 628 629 void 630 test_ToLFPrelPos(void) 631 { 632 int i; 633 634 for (i = 0; i < COUNTOF(fdata); ++i) { 635 struct timespec a = timespec_init(1, fdata[i].nsec); 636 l_fp E = l_fp_init(1, fdata[i].frac); 637 l_fp r; 638 639 r = tspec_intv_to_lfp(a); 640 TEST_ASSERT_EQUAL_l_fp(E, r); 641 } 642 643 return; 644 } 645 646 647 void 648 test_ToLFPrelNeg(void) 649 { 650 int i; 651 652 for (i = 0; i < COUNTOF(fdata); ++i) { 653 struct timespec a = timespec_init(-1, fdata[i].nsec); 654 l_fp E = l_fp_init(~0, fdata[i].frac); 655 l_fp r; 656 657 r = tspec_intv_to_lfp(a); 658 TEST_ASSERT_EQUAL_l_fp(E, r); 659 } 660 661 return; 662 } 663 664 665 void 666 test_ToLFPabs(void) 667 { 668 int i; 669 670 for (i = 0; i < COUNTOF(fdata); ++i) { 671 struct timespec a = timespec_init(1, fdata[i].nsec); 672 l_fp E = l_fp_init(1 + JAN_1970, fdata[i].frac); 673 l_fp r; 674 675 r = tspec_stamp_to_lfp(a); 676 TEST_ASSERT_EQUAL_l_fp(E, r); 677 } 678 679 return; 680 } 681 682 //---------------------------------------------------------------------- 683 // conversion from l_fp 684 //---------------------------------------------------------------------- 685 686 void 687 test_FromLFPbittest(void) 688 { 689 struct timespec limit = timespec_init(0, 2); 690 691 // Not *exactly* a bittest, because 2**32 tests would take a 692 // really long time even on very fast machines! So we do test 693 // every 1000 fractional units. 694 u_int32 tsf; 695 for (tsf = 0; tsf < ~((u_int32)(1000)); tsf += 1000) { 696 struct timespec E = timespec_init(1, my_tsf_to_tick(tsf)); 697 l_fp a = l_fp_init(1, tsf); 698 struct timespec r; 699 700 r = lfp_intv_to_tspec(a); 701 // The conversion might be off by one nanosecond when 702 // comparing to calculated value. 703 TEST_ASSERT_TRUE(AssertTimespecClose(E, r, limit)); 704 } 705 706 return; 707 } 708 709 710 void 711 test_FromLFPrelPos(void) 712 { 713 struct timespec limit = timespec_init(0, 2); 714 int i; 715 716 for (i = 0; i < COUNTOF(fdata); ++i) { 717 l_fp a = l_fp_init(1, fdata[i].frac); 718 struct timespec E = timespec_init(1, fdata[i].nsec); 719 struct timespec r; 720 721 r = lfp_intv_to_tspec(a); 722 TEST_ASSERT_TRUE(AssertTimespecClose(E, r, limit)); 723 } 724 725 return; 726 } 727 728 729 void 730 test_FromLFPrelNeg(void) 731 { 732 struct timespec limit = timespec_init(0, 2); 733 int i; 734 735 for (i = 0; i < COUNTOF(fdata); ++i) { 736 l_fp a = l_fp_init(~0, fdata[i].frac); 737 struct timespec E = timespec_init(-1, fdata[i].nsec); 738 struct timespec r; 739 740 r = lfp_intv_to_tspec(a); 741 TEST_ASSERT_TRUE(AssertTimespecClose(E, r, limit)); 742 } 743 744 return; 745 } 746 747 748 // nsec -> frac -> nsec roundtrip, using a prime start and increment 749 void 750 test_LFProundtrip(void) 751 { 752 int32_t t; 753 u_int32 i; 754 755 for (t = -1; t < 2; ++t) 756 for (i = 4999; i < 1000000000; i += 10007) { 757 struct timespec E = timespec_init(t, i); 758 l_fp a; 759 struct timespec r; 760 761 a = tspec_intv_to_lfp(E); 762 r = lfp_intv_to_tspec(a); 763 TEST_ASSERT_EQUAL_timespec(E, r); 764 } 765 766 return; 767 } 768 769 //---------------------------------------------------------------------- 770 // string formatting 771 //---------------------------------------------------------------------- 772 773 void 774 test_ToString(void) 775 { 776 static const struct { 777 time_t sec; 778 long nsec; 779 const char * repr; 780 } data [] = { 781 { 0, 0, "0.000000000" }, 782 { 2, 0, "2.000000000" }, 783 {-2, 0, "-2.000000000" }, 784 { 0, 1, "0.000000001" }, 785 { 0,-1, "-0.000000001" }, 786 { 1,-1, "0.999999999" }, 787 {-1, 1, "-0.999999999" }, 788 {-1,-1, "-1.000000001" }, 789 }; 790 int i; 791 792 for (i = 0; i < COUNTOF(data); ++i) { 793 struct timespec a = timespec_init(data[i].sec, data[i].nsec); 794 const char * E = data[i].repr; 795 const char * r = tspectoa(a); 796 TEST_ASSERT_EQUAL_STRING(E, r); 797 } 798 799 return; 800 } 801 802 // -*- EOF -*- 803