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 27static u_int32 my_tick_to_tsf(u_int32 ticks); 28static u_int32 my_tsf_to_tick(u_int32 tsf); 29 30 31// that's it... 32struct lfpfracdata { 33 long nsec; 34 u_int32 frac; 35}; 36 37 38void setUp(void); 39void test_Helpers1(void); 40void test_Normalise(void); 41void test_SignNoFrac(void); 42void test_SignWithFrac(void); 43void test_CmpFracEQ(void); 44void test_CmpFracGT(void); 45void test_CmpFracLT(void); 46void test_AddFullNorm(void); 47void test_AddFullOflow1(void); 48void test_AddNsecNorm(void); 49void test_AddNsecOflow1(void); 50void test_SubFullNorm(void); 51void test_SubFullOflow(void); 52void test_SubNsecNorm(void); 53void test_SubNsecOflow(void); 54void test_Neg(void); 55void test_AbsNoFrac(void); 56void test_AbsWithFrac(void); 57void test_Helpers2(void); 58void test_ToLFPbittest(void); 59void test_ToLFPrelPos(void); 60void test_ToLFPrelNeg(void); 61void test_ToLFPabs(void); 62void test_FromLFPbittest(void); 63void test_FromLFPrelPos(void); 64void test_FromLFPrelNeg(void); 65void test_LFProundtrip(void); 66void test_ToString(void); 67 68const bool timespec_isValid(struct timespec V); 69struct timespec timespec_init(time_t hi, long lo); 70l_fp l_fp_init(int32 i, u_int32 f); 71bool AssertFpClose(const l_fp m, const l_fp n, const l_fp limit); 72bool AssertTimespecClose(const struct timespec m, 73 const struct timespec n, 74 const struct timespec limit); 75 76 77//***************************MY CUSTOM FUNCTIONS*************************** 78 79 80void 81setUp(void) 82{ 83 init_lib(); 84 85 return; 86} 87 88 89const bool 90timespec_isValid(struct timespec V) 91{ 92 93 return V.tv_nsec >= 0 && V.tv_nsec < 1000000000; 94} 95 96 97struct timespec 98timespec_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 109l_fp 110l_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 121bool 122AssertFpClose(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 144bool 145AssertTimespecClose(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 164static 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 182u_int32 183my_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 200u_int32 201my_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 219void 220test_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 243void 244test_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 262void 263test_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 280void 281test_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//---------------------------------------------------------------------- 300void 301test_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 319void 320test_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 339void 340test_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 362void 363test_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 382void 383test_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 402void 403test_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 419void 420test_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 440void 441test_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 460void 461test_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 480void 481test_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 498void 499test_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 520void 521test_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 542void 543test_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 559void 560test_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 579void 580test_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 610void 611test_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 629void 630test_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 647void 648test_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 665void 666test_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 686void 687test_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 710void 711test_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 729void 730test_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 749void 750test_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 773void 774test_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