1 1.2 christos /* $NetBSD: calendar.c,v 1.3 2024/08/18 20:47:26 christos Exp $ */ 2 1.2 christos 3 1.1 christos #include "config.h" 4 1.1 christos 5 1.2 christos #include "ntp_stdlib.h" /* test fail without this include, for some reason */ 6 1.1 christos #include "ntp_calendar.h" 7 1.2 christos #include "ntp_calgps.h" 8 1.2 christos #include "ntp_unixtime.h" 9 1.2 christos #include "ntp_fp.h" 10 1.1 christos #include "unity.h" 11 1.1 christos 12 1.2 christos #include <string.h> 13 1.1 christos 14 1.2 christos static char mbuf[128]; 15 1.1 christos 16 1.1 christos static int leapdays(int year); 17 1.1 christos 18 1.2 christos void setUp(void); 19 1.2 christos int isGT(int first, int second); 20 1.2 christos int leapdays(int year); 21 1.2 christos char * CalendarFromCalToString(const struct calendar *cal); 22 1.2 christos char * CalendarFromIsoToString(const struct isodate *iso); 23 1.2 christos int IsEqualCal(const struct calendar *expected, const struct calendar *actual); 24 1.2 christos int IsEqualIso(const struct isodate *expected, const struct isodate *actual); 25 1.2 christos char * DateFromCalToString(const struct calendar *cal); 26 1.2 christos char * DateFromIsoToString(const struct isodate *iso); 27 1.2 christos int IsEqualDateCal(const struct calendar *expected, const struct calendar *actual); 28 1.2 christos int IsEqualDateIso(const struct isodate *expected, const struct isodate *actual); 29 1.2 christos 30 1.2 christos void test_Constants(void); 31 1.2 christos void test_DaySplitMerge(void); 32 1.2 christos void test_WeekSplitMerge(void); 33 1.2 christos void test_SplitYearDays1(void); 34 1.2 christos void test_SplitYearDays2(void); 35 1.2 christos void test_SplitEraDays(void); 36 1.2 christos void test_SplitEraWeeks(void); 37 1.2 christos void test_RataDie1(void); 38 1.2 christos void test_LeapYears1(void); 39 1.2 christos void test_LeapYears2(void); 40 1.2 christos void test_LeapYears3(void); 41 1.2 christos void test_RoundTripDate(void); 42 1.2 christos void test_RoundTripYearStart(void); 43 1.2 christos void test_RoundTripMonthStart(void); 44 1.2 christos void test_RoundTripWeekStart(void); 45 1.2 christos void test_RoundTripDayStart(void); 46 1.2 christos void test_IsoCalYearsToWeeks(void); 47 1.2 christos void test_IsoCalWeeksToYearStart(void); 48 1.2 christos void test_IsoCalWeeksToYearEnd(void); 49 1.2 christos void test_DaySecToDate(void); 50 1.2 christos void test_GpsRollOver(void); 51 1.2 christos void test_GpsRemapFunny(void); 52 1.2 christos 53 1.2 christos void test_GpsNtpFixpoints(void); 54 1.2 christos void test_NtpToNtp(void); 55 1.2 christos void test_NtpToTime(void); 56 1.2 christos 57 1.2 christos void test_CalUMod7(void); 58 1.2 christos void test_CalIMod7(void); 59 1.2 christos void test_RellezCentury1_1(void); 60 1.2 christos void test_RellezCentury3_1(void); 61 1.2 christos void test_RellezYearZero(void); 62 1.1 christos 63 1.1 christos 64 1.2 christos void 65 1.2 christos setUp(void) 66 1.2 christos { 67 1.2 christos init_lib(); 68 1.1 christos 69 1.2 christos return; 70 1.2 christos } 71 1.1 christos 72 1.1 christos 73 1.2 christos /* 74 1.2 christos * --------------------------------------------------------------------- 75 1.2 christos * test support stuff 76 1.2 christos * --------------------------------------------------------------------- 77 1.2 christos */ 78 1.2 christos int 79 1.2 christos isGT(int first, int second) 80 1.2 christos { 81 1.2 christos if(first > second) { 82 1.2 christos return TRUE; 83 1.2 christos } else { 84 1.2 christos return FALSE; 85 1.1 christos } 86 1.1 christos } 87 1.1 christos 88 1.2 christos int 89 1.2 christos leapdays(int year) 90 1.1 christos { 91 1.1 christos if (year % 400 == 0) 92 1.1 christos return 1; 93 1.1 christos if (year % 100 == 0) 94 1.1 christos return 0; 95 1.1 christos if (year % 4 == 0) 96 1.1 christos return 1; 97 1.1 christos return 0; 98 1.1 christos } 99 1.1 christos 100 1.2 christos char * 101 1.2 christos CalendarFromCalToString( 102 1.2 christos const struct calendar *cal) 103 1.2 christos { 104 1.2 christos char * str = malloc(sizeof (char) * 100); 105 1.2 christos snprintf(str, 100, "%u-%02u-%02u (%u) %02u:%02u:%02u", 106 1.2 christos cal->year, (u_int)cal->month, (u_int)cal->monthday, 107 1.2 christos cal->yearday, 108 1.2 christos (u_int)cal->hour, (u_int)cal->minute, (u_int)cal->second); 109 1.2 christos str[99] = '\0'; /* paranoia rulez! */ 110 1.2 christos return str; 111 1.1 christos } 112 1.1 christos 113 1.2 christos char * 114 1.2 christos CalendarFromIsoToString( 115 1.2 christos const struct isodate *iso) 116 1.2 christos { 117 1.2 christos char * str = emalloc (sizeof (char) * 100); 118 1.2 christos snprintf(str, 100, "%u-W%02u-%02u %02u:%02u:%02u", 119 1.2 christos iso->year, (u_int)iso->week, (u_int)iso->weekday, 120 1.2 christos (u_int)iso->hour, (u_int)iso->minute, (u_int)iso->second); 121 1.2 christos str[99] = '\0'; /* paranoia rulez! */ 122 1.2 christos return str; 123 1.1 christos } 124 1.1 christos 125 1.2 christos int 126 1.2 christos IsEqualCal( 127 1.2 christos const struct calendar *expected, 128 1.2 christos const struct calendar *actual) 129 1.2 christos { 130 1.2 christos if (expected->year == actual->year && 131 1.2 christos (!expected->yearday || expected->yearday == actual->yearday) && 132 1.2 christos expected->month == actual->month && 133 1.2 christos expected->monthday == actual->monthday && 134 1.2 christos expected->hour == actual->hour && 135 1.2 christos expected->minute == actual->minute && 136 1.2 christos expected->second == actual->second) { 137 1.1 christos return TRUE; 138 1.1 christos } else { 139 1.2 christos char *p_exp = CalendarFromCalToString(expected); 140 1.2 christos char *p_act = CalendarFromCalToString(actual); 141 1.2 christos 142 1.2 christos printf("expected: %s but was %s", p_exp, p_act); 143 1.2 christos 144 1.2 christos free(p_exp); 145 1.2 christos free(p_act); 146 1.2 christos 147 1.1 christos return FALSE; 148 1.1 christos } 149 1.1 christos } 150 1.1 christos 151 1.2 christos int 152 1.2 christos IsEqualIso( 153 1.2 christos const struct isodate *expected, 154 1.2 christos const struct isodate *actual) 155 1.2 christos { 156 1.2 christos if (expected->year == actual->year && 157 1.2 christos expected->week == actual->week && 158 1.2 christos expected->weekday == actual->weekday && 159 1.2 christos expected->hour == actual->hour && 160 1.2 christos expected->minute == actual->minute && 161 1.2 christos expected->second == actual->second) { 162 1.1 christos return TRUE; 163 1.1 christos } else { 164 1.2 christos printf("expected: %s but was %s", 165 1.2 christos CalendarFromIsoToString(expected), 166 1.2 christos CalendarFromIsoToString(actual)); 167 1.1 christos return FALSE; 168 1.1 christos } 169 1.1 christos } 170 1.1 christos 171 1.2 christos char * 172 1.2 christos DateFromCalToString( 173 1.2 christos const struct calendar *cal) 174 1.2 christos { 175 1.2 christos 176 1.2 christos char * str = emalloc (sizeof (char) * 100); 177 1.2 christos snprintf(str, 100, "%u-%02u-%02u (%u)", 178 1.2 christos cal->year, (u_int)cal->month, (u_int)cal->monthday, 179 1.2 christos cal->yearday); 180 1.2 christos str[99] = '\0'; /* paranoia rulez! */ 181 1.2 christos return str; 182 1.2 christos } 183 1.1 christos 184 1.2 christos char * 185 1.2 christos DateFromIsoToString( 186 1.2 christos const struct isodate *iso) 187 1.2 christos { 188 1.2 christos 189 1.2 christos char * str = emalloc (sizeof (char) * 100); 190 1.2 christos snprintf(str, 100, "%u-W%02u-%02u", 191 1.2 christos iso->year, (u_int)iso->week, (u_int)iso->weekday); 192 1.2 christos str[99] = '\0'; /* paranoia rulez! */ 193 1.2 christos return str; 194 1.1 christos } 195 1.1 christos 196 1.2 christos int/*BOOL*/ 197 1.2 christos IsEqualDateCal( 198 1.2 christos const struct calendar *expected, 199 1.2 christos const struct calendar *actual) 200 1.2 christos { 201 1.2 christos if (expected->year == actual->year && 202 1.2 christos (!expected->yearday || expected->yearday == actual->yearday) && 203 1.2 christos expected->month == actual->month && 204 1.2 christos expected->monthday == actual->monthday) { 205 1.1 christos return TRUE; 206 1.1 christos } else { 207 1.2 christos printf("expected: %s but was %s", 208 1.2 christos DateFromCalToString(expected), 209 1.2 christos DateFromCalToString(actual)); 210 1.1 christos return FALSE; 211 1.1 christos } 212 1.1 christos } 213 1.1 christos 214 1.2 christos int/*BOOL*/ 215 1.2 christos IsEqualDateIso( 216 1.2 christos const struct isodate *expected, 217 1.2 christos const struct isodate *actual) 218 1.2 christos { 219 1.2 christos if (expected->year == actual->year && 220 1.2 christos expected->week == actual->week && 221 1.2 christos expected->weekday == actual->weekday) { 222 1.1 christos return TRUE; 223 1.1 christos } else { 224 1.2 christos printf("expected: %s but was %s", 225 1.2 christos DateFromIsoToString(expected), 226 1.2 christos DateFromIsoToString(actual)); 227 1.1 christos return FALSE; 228 1.1 christos } 229 1.1 christos } 230 1.1 christos 231 1.2 christos static int/*BOOL*/ 232 1.2 christos strToCal( 233 1.2 christos struct calendar * jd, 234 1.2 christos const char * str 235 1.2 christos ) 236 1.2 christos { 237 1.2 christos unsigned short y,m,d, H,M,S; 238 1.2 christos 239 1.2 christos if (6 == sscanf(str, "%hu-%2hu-%2huT%2hu:%2hu:%2hu", 240 1.2 christos &y, &m, &d, &H, &M, &S)) { 241 1.2 christos memset(jd, 0, sizeof(*jd)); 242 1.2 christos jd->year = y; 243 1.2 christos jd->month = (uint8_t)m; 244 1.2 christos jd->monthday = (uint8_t)d; 245 1.2 christos jd->hour = (uint8_t)H; 246 1.2 christos jd->minute = (uint8_t)M; 247 1.2 christos jd->second = (uint8_t)S; 248 1.2 christos 249 1.2 christos return TRUE; 250 1.2 christos } 251 1.2 christos return FALSE; 252 1.2 christos } 253 1.2 christos 254 1.2 christos /* 255 1.2 christos * --------------------------------------------------------------------- 256 1.2 christos * test cases 257 1.2 christos * --------------------------------------------------------------------- 258 1.2 christos */ 259 1.1 christos 260 1.2 christos /* days before month, with a full-year pad at the upper end */ 261 1.1 christos static const u_short real_month_table[2][13] = { 262 1.1 christos /* -*- table for regular years -*- */ 263 1.1 christos { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 264 1.1 christos /* -*- table for leap years -*- */ 265 1.1 christos { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 266 1.1 christos }; 267 1.1 christos 268 1.2 christos /* days in month, with one month wrap-around at both ends */ 269 1.1 christos static const u_short real_month_days[2][14] = { 270 1.1 christos /* -*- table for regular years -*- */ 271 1.1 christos { 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31 }, 272 1.1 christos /* -*- table for leap years -*- */ 273 1.1 christos { 31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31 } 274 1.1 christos }; 275 1.1 christos 276 1.2 christos void 277 1.2 christos test_Constants(void) 278 1.2 christos { 279 1.2 christos int32_t rdn; 280 1.2 christos struct calendar jdn; 281 1.2 christos 282 1.2 christos jdn.year = 1900; 283 1.2 christos jdn.month = 1; 284 1.2 christos jdn.monthday = 1; 285 1.2 christos rdn = ntpcal_date_to_rd(&jdn); 286 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(DAY_NTP_STARTS, rdn, "(NTP EPOCH)"); 287 1.2 christos 288 1.2 christos jdn.year = 1980; 289 1.2 christos jdn.month = 1; 290 1.2 christos jdn.monthday = 6; 291 1.2 christos rdn = ntpcal_date_to_rd(&jdn); 292 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(DAY_GPS_STARTS, rdn, "(GPS EPOCH)"); 293 1.2 christos } 294 1.2 christos 295 1.2 christos /* test the day/sec join & split ops, making sure that 32bit 296 1.2 christos * intermediate results would definitely overflow and the hi DWORD of 297 1.2 christos * the 'vint64' is definitely needed. 298 1.2 christos */ 299 1.2 christos void 300 1.2 christos test_DaySplitMerge(void) 301 1.2 christos { 302 1.1 christos int32 day,sec; 303 1.2 christos 304 1.1 christos for (day = -1000000; day <= 1000000; day += 100) { 305 1.1 christos for (sec = -100000; sec <= 186400; sec += 10000) { 306 1.2 christos vint64 merge; 307 1.2 christos ntpcal_split split; 308 1.2 christos int32 eday; 309 1.2 christos int32 esec; 310 1.2 christos 311 1.2 christos merge = ntpcal_dayjoin(day, sec); 312 1.2 christos split = ntpcal_daysplit(&merge); 313 1.2 christos eday = day; 314 1.2 christos esec = sec; 315 1.1 christos 316 1.1 christos while (esec >= 86400) { 317 1.1 christos eday += 1; 318 1.1 christos esec -= 86400; 319 1.1 christos } 320 1.1 christos while (esec < 0) { 321 1.1 christos eday -= 1; 322 1.1 christos esec += 86400; 323 1.1 christos } 324 1.1 christos 325 1.1 christos TEST_ASSERT_EQUAL(eday, split.hi); 326 1.1 christos TEST_ASSERT_EQUAL(esec, split.lo); 327 1.1 christos } 328 1.1 christos } 329 1.2 christos 330 1.2 christos return; 331 1.1 christos } 332 1.1 christos 333 1.2 christos void 334 1.2 christos test_WeekSplitMerge(void) 335 1.2 christos { 336 1.2 christos int32 wno,sec; 337 1.2 christos 338 1.2 christos for (wno = -1000000; wno <= 1000000; wno += 100) { 339 1.2 christos for (sec = -100000; sec <= 2*SECSPERWEEK; sec += 10000) { 340 1.2 christos vint64 merge; 341 1.2 christos ntpcal_split split; 342 1.2 christos int32 ewno; 343 1.2 christos int32 esec; 344 1.2 christos 345 1.2 christos merge = ntpcal_weekjoin(wno, sec); 346 1.2 christos split = ntpcal_weeksplit(&merge); 347 1.2 christos ewno = wno; 348 1.2 christos esec = sec; 349 1.2 christos 350 1.2 christos while (esec >= SECSPERWEEK) { 351 1.2 christos ewno += 1; 352 1.2 christos esec -= SECSPERWEEK; 353 1.2 christos } 354 1.2 christos while (esec < 0) { 355 1.2 christos ewno -= 1; 356 1.2 christos esec += SECSPERWEEK; 357 1.2 christos } 358 1.2 christos 359 1.2 christos TEST_ASSERT_EQUAL(ewno, split.hi); 360 1.2 christos TEST_ASSERT_EQUAL(esec, split.lo); 361 1.2 christos } 362 1.2 christos } 363 1.2 christos 364 1.2 christos return; 365 1.2 christos } 366 1.2 christos 367 1.2 christos void 368 1.2 christos test_SplitYearDays1(void) 369 1.2 christos { 370 1.1 christos int32 eyd; 371 1.2 christos 372 1.1 christos for (eyd = -1; eyd <= 365; eyd++) { 373 1.1 christos ntpcal_split split = ntpcal_split_yeardays(eyd, 0); 374 1.1 christos if (split.lo >= 0 && split.hi >= 0) { 375 1.2 christos TEST_ASSERT_TRUE(isGT(12,split.hi)); 376 1.2 christos TEST_ASSERT_TRUE(isGT(real_month_days[0][split.hi+1], split.lo)); 377 1.1 christos int32 tyd = real_month_table[0][split.hi] + split.lo; 378 1.1 christos TEST_ASSERT_EQUAL(eyd, tyd); 379 1.1 christos } else 380 1.1 christos TEST_ASSERT_TRUE(eyd < 0 || eyd > 364); 381 1.1 christos } 382 1.2 christos 383 1.2 christos return; 384 1.1 christos } 385 1.2 christos 386 1.2 christos void 387 1.2 christos test_SplitYearDays2(void) 388 1.2 christos { 389 1.1 christos int32 eyd; 390 1.2 christos 391 1.1 christos for (eyd = -1; eyd <= 366; eyd++) { 392 1.1 christos ntpcal_split split = ntpcal_split_yeardays(eyd, 1); 393 1.1 christos if (split.lo >= 0 && split.hi >= 0) { 394 1.2 christos /* basic checks do not work on compunds :( */ 395 1.2 christos /* would like: TEST_ASSERT_TRUE(12 > split.hi); */ 396 1.2 christos TEST_ASSERT_TRUE(isGT(12,split.hi)); 397 1.2 christos TEST_ASSERT_TRUE(isGT(real_month_days[1][split.hi+1], split.lo)); 398 1.1 christos int32 tyd = real_month_table[1][split.hi] + split.lo; 399 1.1 christos TEST_ASSERT_EQUAL(eyd, tyd); 400 1.1 christos } else 401 1.1 christos TEST_ASSERT_TRUE(eyd < 0 || eyd > 365); 402 1.1 christos } 403 1.2 christos 404 1.2 christos return; 405 1.1 christos } 406 1.2 christos 407 1.2 christos void 408 1.2 christos test_SplitEraDays(void) 409 1.2 christos { 410 1.2 christos int32_t ed, rd; 411 1.2 christos ntpcal_split sd; 412 1.2 christos for (ed = -10000; ed < 1000000; ++ed) { 413 1.2 christos sd = ntpcal_split_eradays(ed, NULL); 414 1.2 christos rd = ntpcal_days_in_years(sd.hi) + sd.lo; 415 1.2 christos TEST_ASSERT_EQUAL(ed, rd); 416 1.2 christos TEST_ASSERT_TRUE(0 <= sd.lo && sd.lo <= 365); 417 1.2 christos } 418 1.2 christos } 419 1.2 christos 420 1.2 christos void 421 1.2 christos test_SplitEraWeeks(void) 422 1.2 christos { 423 1.2 christos int32_t ew, rw; 424 1.2 christos ntpcal_split sw; 425 1.2 christos for (ew = -10000; ew < 1000000; ++ew) { 426 1.2 christos sw = isocal_split_eraweeks(ew); 427 1.2 christos rw = isocal_weeks_in_years(sw.hi) + sw.lo; 428 1.2 christos TEST_ASSERT_EQUAL(ew, rw); 429 1.2 christos TEST_ASSERT_TRUE(0 <= sw.lo && sw.lo <= 52); 430 1.2 christos } 431 1.2 christos } 432 1.2 christos 433 1.2 christos void 434 1.2 christos test_RataDie1(void) 435 1.2 christos { 436 1.2 christos int32 testDate = 1; /* 0001-01-01 (proleptic date) */ 437 1.1 christos struct calendar expected = { 1, 1, 1, 1 }; 438 1.1 christos struct calendar actual; 439 1.1 christos 440 1.1 christos ntpcal_rd_to_date(&actual, testDate); 441 1.2 christos TEST_ASSERT_TRUE(IsEqualDateCal(&expected, &actual)); 442 1.2 christos 443 1.2 christos return; 444 1.1 christos } 445 1.1 christos 446 1.2 christos /* check last day of february for first 10000 years */ 447 1.2 christos void 448 1.2 christos test_LeapYears1(void) 449 1.2 christos { 450 1.1 christos struct calendar dateIn, dateOut; 451 1.1 christos 452 1.1 christos for (dateIn.year = 1; dateIn.year < 10000; ++dateIn.year) { 453 1.1 christos dateIn.month = 2; 454 1.1 christos dateIn.monthday = 28 + leapdays(dateIn.year); 455 1.1 christos dateIn.yearday = 31 + dateIn.monthday; 456 1.1 christos 457 1.1 christos ntpcal_rd_to_date(&dateOut, ntpcal_date_to_rd(&dateIn)); 458 1.1 christos 459 1.2 christos TEST_ASSERT_TRUE(IsEqualDateCal(&dateIn, &dateOut)); 460 1.1 christos } 461 1.2 christos 462 1.2 christos return; 463 1.1 christos } 464 1.1 christos 465 1.2 christos /* check first day of march for first 10000 years */ 466 1.2 christos void 467 1.2 christos test_LeapYears2(void) 468 1.2 christos { 469 1.1 christos struct calendar dateIn, dateOut; 470 1.1 christos 471 1.1 christos for (dateIn.year = 1; dateIn.year < 10000; ++dateIn.year) { 472 1.1 christos dateIn.month = 3; 473 1.1 christos dateIn.monthday = 1; 474 1.1 christos dateIn.yearday = 60 + leapdays(dateIn.year); 475 1.1 christos 476 1.1 christos ntpcal_rd_to_date(&dateOut, ntpcal_date_to_rd(&dateIn)); 477 1.2 christos TEST_ASSERT_TRUE(IsEqualDateCal(&dateIn, &dateOut)); 478 1.2 christos } 479 1.2 christos 480 1.2 christos return; 481 1.2 christos } 482 1.2 christos 483 1.2 christos /* check the 'is_leapyear()' implementation for 4400 years */ 484 1.2 christos void 485 1.2 christos test_LeapYears3(void) 486 1.2 christos { 487 1.2 christos int32_t year; 488 1.2 christos int l1, l2; 489 1.2 christos 490 1.2 christos for (year = -399; year < 4000; ++year) { 491 1.2 christos l1 = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); 492 1.2 christos l2 = is_leapyear(year); 493 1.2 christos snprintf(mbuf, sizeof(mbuf), "y=%d", year); 494 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(l1, l2, mbuf); 495 1.1 christos } 496 1.1 christos } 497 1.1 christos 498 1.2 christos /* Full roundtrip from 1601-01-01 to 2400-12-31 499 1.2 christos * checks sequence of rata die numbers and validates date output 500 1.2 christos * (since the input is all nominal days of the calendar in that range 501 1.2 christos * and the result of the inverse calculation must match the input no 502 1.2 christos * invalid output can occur.) 503 1.2 christos */ 504 1.2 christos void 505 1.2 christos test_RoundTripDate(void) 506 1.2 christos { 507 1.1 christos struct calendar truDate, expDate = { 1600, 0, 12, 31 };; 508 1.2 christos int leaps; 509 1.1 christos int32 truRdn, expRdn = ntpcal_date_to_rd(&expDate); 510 1.1 christos 511 1.1 christos while (expDate.year < 2400) { 512 1.1 christos expDate.year++; 513 1.1 christos expDate.month = 0; 514 1.1 christos expDate.yearday = 0; 515 1.1 christos leaps = leapdays(expDate.year); 516 1.1 christos while (expDate.month < 12) { 517 1.2 christos expDate.month++; 518 1.1 christos expDate.monthday = 0; 519 1.1 christos while (expDate.monthday < real_month_days[leaps][expDate.month]) { 520 1.1 christos expDate.monthday++; 521 1.1 christos expDate.yearday++; 522 1.1 christos expRdn++; 523 1.1 christos 524 1.1 christos truRdn = ntpcal_date_to_rd(&expDate); 525 1.1 christos TEST_ASSERT_EQUAL(expRdn, truRdn); 526 1.1 christos 527 1.1 christos ntpcal_rd_to_date(&truDate, truRdn); 528 1.2 christos TEST_ASSERT_TRUE(IsEqualDateCal(&expDate, &truDate)); 529 1.1 christos } 530 1.1 christos } 531 1.1 christos } 532 1.2 christos 533 1.2 christos return; 534 1.1 christos } 535 1.1 christos 536 1.2 christos /* Roundtrip testing on calyearstart */ 537 1.2 christos void 538 1.2 christos test_RoundTripYearStart(void) 539 1.2 christos { 540 1.1 christos static const time_t pivot = 0; 541 1.1 christos u_int32 ntp, expys, truys; 542 1.1 christos struct calendar date; 543 1.1 christos 544 1.1 christos for (ntp = 0; ntp < 0xFFFFFFFFu - 30000000u; ntp += 30000000u) { 545 1.1 christos truys = calyearstart(ntp, &pivot); 546 1.1 christos ntpcal_ntp_to_date(&date, ntp, &pivot); 547 1.1 christos date.month = date.monthday = 1; 548 1.1 christos date.hour = date.minute = date.second = 0; 549 1.1 christos expys = ntpcal_date_to_ntp(&date); 550 1.1 christos TEST_ASSERT_EQUAL(expys, truys); 551 1.1 christos } 552 1.1 christos 553 1.2 christos return; 554 1.2 christos } 555 1.2 christos 556 1.2 christos /* Roundtrip testing on calmonthstart */ 557 1.2 christos void 558 1.2 christos test_RoundTripMonthStart(void) 559 1.2 christos { 560 1.1 christos static const time_t pivot = 0; 561 1.1 christos u_int32 ntp, expms, trums; 562 1.1 christos struct calendar date; 563 1.1 christos 564 1.1 christos for (ntp = 0; ntp < 0xFFFFFFFFu - 2000000u; ntp += 2000000u) { 565 1.1 christos trums = calmonthstart(ntp, &pivot); 566 1.1 christos ntpcal_ntp_to_date(&date, ntp, &pivot); 567 1.1 christos date.monthday = 1; 568 1.1 christos date.hour = date.minute = date.second = 0; 569 1.1 christos expms = ntpcal_date_to_ntp(&date); 570 1.1 christos TEST_ASSERT_EQUAL(expms, trums); 571 1.1 christos } 572 1.1 christos 573 1.2 christos return; 574 1.2 christos } 575 1.2 christos 576 1.2 christos /* Roundtrip testing on calweekstart */ 577 1.2 christos void 578 1.2 christos test_RoundTripWeekStart(void) 579 1.2 christos { 580 1.1 christos static const time_t pivot = 0; 581 1.1 christos u_int32 ntp, expws, truws; 582 1.1 christos struct isodate date; 583 1.1 christos 584 1.1 christos for (ntp = 0; ntp < 0xFFFFFFFFu - 600000u; ntp += 600000u) { 585 1.1 christos truws = calweekstart(ntp, &pivot); 586 1.1 christos isocal_ntp_to_date(&date, ntp, &pivot); 587 1.1 christos date.hour = date.minute = date.second = 0; 588 1.1 christos date.weekday = 1; 589 1.1 christos expws = isocal_date_to_ntp(&date); 590 1.1 christos TEST_ASSERT_EQUAL(expws, truws); 591 1.1 christos } 592 1.1 christos 593 1.2 christos return; 594 1.2 christos } 595 1.2 christos 596 1.2 christos /* Roundtrip testing on caldaystart */ 597 1.2 christos void 598 1.2 christos test_RoundTripDayStart(void) 599 1.2 christos { 600 1.1 christos static const time_t pivot = 0; 601 1.1 christos u_int32 ntp, expds, truds; 602 1.1 christos struct calendar date; 603 1.1 christos 604 1.1 christos for (ntp = 0; ntp < 0xFFFFFFFFu - 80000u; ntp += 80000u) { 605 1.1 christos truds = caldaystart(ntp, &pivot); 606 1.1 christos ntpcal_ntp_to_date(&date, ntp, &pivot); 607 1.1 christos date.hour = date.minute = date.second = 0; 608 1.1 christos expds = ntpcal_date_to_ntp(&date); 609 1.1 christos TEST_ASSERT_EQUAL(expds, truds); 610 1.1 christos } 611 1.2 christos 612 1.2 christos return; 613 1.2 christos } 614 1.2 christos 615 1.2 christos /* --------------------------------------------------------------------- 616 1.2 christos * ISO8601 week calendar internals 617 1.2 christos * 618 1.2 christos * The ISO8601 week calendar implementation is simple in the terms of 619 1.2 christos * the math involved, but the implementation of the calculations must 620 1.2 christos * take care of a few things like overflow, floor division, and sign 621 1.2 christos * corrections. 622 1.2 christos * 623 1.2 christos * Most of the functions are straight forward, but converting from years 624 1.2 christos * to weeks and from weeks to years warrants some extra tests. These use 625 1.2 christos * an independent reference implementation of the conversion from years 626 1.2 christos * to weeks. 627 1.2 christos * --------------------------------------------------------------------- 628 1.2 christos */ 629 1.2 christos 630 1.2 christos /* helper / reference implementation for the first week of year in the 631 1.2 christos * ISO8601 week calendar. This is based on the reference definition of 632 1.2 christos * the ISO week calendar start: The Monday closest to January,1st of the 633 1.2 christos * corresponding year in the Gregorian calendar. 634 1.2 christos */ 635 1.2 christos static int32_t 636 1.2 christos refimpl_WeeksInIsoYears( 637 1.2 christos int32_t years) 638 1.2 christos { 639 1.2 christos int32_t days, weeks; 640 1.2 christos 641 1.2 christos days = ntpcal_weekday_close( 642 1.2 christos ntpcal_days_in_years(years) + 1, 643 1.2 christos CAL_MONDAY) - 1; 644 1.2 christos /* the weekday functions operate on RDN, while we want elapsed 645 1.2 christos * units here -- we have to add / sub 1 in the midlle / at the 646 1.2 christos * end of the operation that gets us the first day of the ISO 647 1.2 christos * week calendar day. 648 1.2 christos */ 649 1.2 christos weeks = days / 7; 650 1.2 christos days = days % 7; 651 1.2 christos TEST_ASSERT_EQUAL(0, days); /* paranoia check... */ 652 1.2 christos 653 1.2 christos return weeks; 654 1.2 christos } 655 1.2 christos 656 1.2 christos /* The next tests loop over 5000yrs, but should still be very fast. If 657 1.2 christos * they are not, the calendar needs a better implementation... 658 1.2 christos */ 659 1.2 christos void 660 1.2 christos test_IsoCalYearsToWeeks(void) 661 1.2 christos { 662 1.2 christos int32_t years; 663 1.2 christos int32_t wref, wcal; 664 1.2 christos 665 1.2 christos for (years = -1000; years < 4000; ++years) { 666 1.2 christos /* get number of weeks before years (reference) */ 667 1.2 christos wref = refimpl_WeeksInIsoYears(years); 668 1.2 christos /* get number of weeks before years (object-under-test) */ 669 1.2 christos wcal = isocal_weeks_in_years(years); 670 1.2 christos TEST_ASSERT_EQUAL(wref, wcal); 671 1.2 christos } 672 1.2 christos 673 1.2 christos return; 674 1.2 christos } 675 1.2 christos 676 1.2 christos void 677 1.2 christos test_IsoCalWeeksToYearStart(void) 678 1.2 christos { 679 1.2 christos int32_t years; 680 1.2 christos int32_t wref; 681 1.2 christos ntpcal_split ysplit; 682 1.2 christos 683 1.2 christos for (years = -1000; years < 4000; ++years) { 684 1.2 christos /* get number of weeks before years (reference) */ 685 1.2 christos wref = refimpl_WeeksInIsoYears(years); 686 1.2 christos /* reverse split */ 687 1.2 christos ysplit = isocal_split_eraweeks(wref); 688 1.2 christos /* check invariants: same year, week 0 */ 689 1.2 christos TEST_ASSERT_EQUAL(years, ysplit.hi); 690 1.2 christos TEST_ASSERT_EQUAL(0, ysplit.lo); 691 1.2 christos } 692 1.2 christos 693 1.2 christos return; 694 1.2 christos } 695 1.2 christos 696 1.2 christos void 697 1.2 christos test_IsoCalWeeksToYearEnd(void) 698 1.2 christos { 699 1.2 christos int32_t years; 700 1.2 christos int32_t wref; 701 1.2 christos ntpcal_split ysplit; 702 1.2 christos 703 1.2 christos for (years = -1000; years < 4000; ++years) { 704 1.2 christos /* get last week of previous year */ 705 1.2 christos wref = refimpl_WeeksInIsoYears(years) - 1; 706 1.2 christos /* reverse split */ 707 1.2 christos ysplit = isocal_split_eraweeks(wref); 708 1.2 christos /* check invariants: previous year, week 51 or 52 */ 709 1.2 christos TEST_ASSERT_EQUAL(years-1, ysplit.hi); 710 1.2 christos TEST_ASSERT(ysplit.lo == 51 || ysplit.lo == 52); 711 1.2 christos } 712 1.2 christos 713 1.2 christos return; 714 1.2 christos } 715 1.2 christos 716 1.2 christos void 717 1.2 christos test_DaySecToDate(void) 718 1.2 christos { 719 1.2 christos struct calendar cal; 720 1.2 christos int32_t days; 721 1.2 christos 722 1.2 christos days = ntpcal_daysec_to_date(&cal, -86400); 723 1.2 christos TEST_ASSERT_MESSAGE((days==-1 && cal.hour==0 && cal.minute==0 && cal.second==0), 724 1.2 christos "failed for -86400"); 725 1.2 christos 726 1.2 christos days = ntpcal_daysec_to_date(&cal, -86399); 727 1.2 christos TEST_ASSERT_MESSAGE((days==-1 && cal.hour==0 && cal.minute==0 && cal.second==1), 728 1.2 christos "failed for -86399"); 729 1.2 christos 730 1.2 christos days = ntpcal_daysec_to_date(&cal, -1); 731 1.2 christos TEST_ASSERT_MESSAGE((days==-1 && cal.hour==23 && cal.minute==59 && cal.second==59), 732 1.2 christos "failed for -1"); 733 1.2 christos 734 1.2 christos days = ntpcal_daysec_to_date(&cal, 0); 735 1.2 christos TEST_ASSERT_MESSAGE((days==0 && cal.hour==0 && cal.minute==0 && cal.second==0), 736 1.2 christos "failed for 0"); 737 1.2 christos 738 1.2 christos days = ntpcal_daysec_to_date(&cal, 1); 739 1.2 christos TEST_ASSERT_MESSAGE((days==0 && cal.hour==0 && cal.minute==0 && cal.second==1), 740 1.2 christos "failed for 1"); 741 1.2 christos 742 1.2 christos days = ntpcal_daysec_to_date(&cal, 86399); 743 1.2 christos TEST_ASSERT_MESSAGE((days==0 && cal.hour==23 && cal.minute==59 && cal.second==59), 744 1.2 christos "failed for 86399"); 745 1.2 christos 746 1.2 christos days = ntpcal_daysec_to_date(&cal, 86400); 747 1.2 christos TEST_ASSERT_MESSAGE((days==1 && cal.hour==0 && cal.minute==0 && cal.second==0), 748 1.2 christos "failed for 86400"); 749 1.2 christos 750 1.2 christos return; 751 1.2 christos } 752 1.2 christos 753 1.2 christos /* -------------------------------------------------------------------- 754 1.2 christos * unfolding of (truncated) NTP time stamps to full 64bit values. 755 1.2 christos * 756 1.2 christos * Note: These tests need a 64bit time_t to be useful. 757 1.2 christos */ 758 1.2 christos 759 1.2 christos void 760 1.2 christos test_NtpToNtp(void) 761 1.2 christos { 762 1.2 christos # if SIZEOF_TIME_T <= 4 763 1.2 christos 764 1.2 christos TEST_IGNORE_MESSAGE("test only useful for sizeof(time_t) > 4, skipped"); 765 1.2 christos 766 1.2 christos # else 767 1.2 christos 768 1.2 christos static const uint32_t ntp_vals[6] = { 769 1.2 christos UINT32_C(0x00000000), 770 1.2 christos UINT32_C(0x00000001), 771 1.2 christos UINT32_C(0x7FFFFFFF), 772 1.2 christos UINT32_C(0x80000000), 773 1.2 christos UINT32_C(0x80000001), 774 1.2 christos UINT32_C(0xFFFFFFFF) 775 1.2 christos }; 776 1.2 christos 777 1.2 christos static char lbuf[128]; 778 1.2 christos vint64 hold; 779 1.2 christos time_t pivot, texp, diff; 780 1.2 christos int loops, iloop; 781 1.2 christos 782 1.2 christos pivot = 0; 783 1.2 christos for (loops = 0; loops < 16; ++loops) { 784 1.2 christos for (iloop = 0; iloop < 6; ++iloop) { 785 1.2 christos hold = ntpcal_ntp_to_ntp( 786 1.2 christos ntp_vals[iloop], &pivot); 787 1.2 christos texp = vint64_to_time(&hold); 788 1.2 christos 789 1.2 christos /* constraint 1: texp must be in the 790 1.2 christos * (right-open) intervall [p-(2^31), p+(2^31)[, 791 1.2 christos * but the pivot 'p' must be taken in full NTP 792 1.2 christos * time scale! 793 1.2 christos */ 794 1.2 christos diff = texp - (pivot + JAN_1970); 795 1.2 christos snprintf(lbuf, sizeof(lbuf), 796 1.2 christos "bounds check: piv=%lld exp=%lld dif=%lld", 797 1.2 christos (long long)pivot, 798 1.2 christos (long long)texp, 799 1.2 christos (long long)diff); 800 1.2 christos TEST_ASSERT_MESSAGE((diff >= INT32_MIN) && (diff <= INT32_MAX), 801 1.2 christos lbuf); 802 1.2 christos 803 1.2 christos /* constraint 2: low word must be equal to 804 1.2 christos * input 805 1.2 christos */ 806 1.2 christos snprintf(lbuf, sizeof(lbuf), 807 1.2 christos "low check: ntp(in)=$%08lu ntp(out[0:31])=$%08lu", 808 1.2 christos (unsigned long)ntp_vals[iloop], 809 1.2 christos (unsigned long)hold.D_s.lo); 810 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(ntp_vals[iloop], hold.D_s.lo, lbuf); 811 1.2 christos } 812 1.2 christos pivot += 0x20000000; 813 1.2 christos } 814 1.2 christos # endif 815 1.2 christos } 816 1.2 christos 817 1.2 christos void 818 1.2 christos test_NtpToTime(void) 819 1.2 christos { 820 1.2 christos # if SIZEOF_TIME_T <= 4 821 1.2 christos 822 1.2 christos TEST_IGNORE_MESSAGE("test only useful for sizeof(time_t) > 4, skipped"); 823 1.2 christos 824 1.2 christos # else 825 1.2 christos 826 1.2 christos static const uint32_t ntp_vals[6] = { 827 1.2 christos UINT32_C(0x00000000), 828 1.2 christos UINT32_C(0x00000001), 829 1.2 christos UINT32_C(0x7FFFFFFF), 830 1.2 christos UINT32_C(0x80000000), 831 1.2 christos UINT32_C(0x80000001), 832 1.2 christos UINT32_C(0xFFFFFFFF) 833 1.2 christos }; 834 1.2 christos 835 1.2 christos static char lbuf[128]; 836 1.2 christos vint64 hold; 837 1.2 christos time_t pivot, texp, diff; 838 1.2 christos uint32_t back; 839 1.2 christos int loops, iloop; 840 1.2 christos 841 1.2 christos pivot = 0; 842 1.2 christos for (loops = 0; loops < 16; ++loops) { 843 1.2 christos for (iloop = 0; iloop < 6; ++iloop) { 844 1.2 christos hold = ntpcal_ntp_to_time( 845 1.2 christos ntp_vals[iloop], &pivot); 846 1.2 christos texp = vint64_to_time(&hold); 847 1.2 christos 848 1.2 christos /* constraint 1: texp must be in the 849 1.2 christos * (right-open) intervall [p-(2^31), p+(2^31)[ 850 1.2 christos */ 851 1.2 christos diff = texp - pivot; 852 1.2 christos snprintf(lbuf, sizeof(lbuf), 853 1.2 christos "bounds check: piv=%lld exp=%lld dif=%lld", 854 1.2 christos (long long)pivot, 855 1.2 christos (long long)texp, 856 1.2 christos (long long)diff); 857 1.2 christos TEST_ASSERT_MESSAGE((diff >= INT32_MIN) && (diff <= INT32_MAX), 858 1.2 christos lbuf); 859 1.2 christos 860 1.2 christos /* constraint 2: conversion from full time back 861 1.2 christos * to truncated NTP time must yield same result 862 1.2 christos * as input. 863 1.2 christos */ 864 1.2 christos back = (uint32_t)texp + JAN_1970; 865 1.2 christos snprintf(lbuf, sizeof(lbuf), 866 1.2 christos "modulo check: ntp(in)=$%08lu ntp(out)=$%08lu", 867 1.2 christos (unsigned long)ntp_vals[iloop], 868 1.2 christos (unsigned long)back); 869 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(ntp_vals[iloop], back, lbuf); 870 1.2 christos } 871 1.2 christos pivot += 0x20000000; 872 1.2 christos } 873 1.2 christos # endif 874 1.2 christos } 875 1.2 christos 876 1.2 christos /* -------------------------------------------------------------------- 877 1.2 christos * GPS rollover 878 1.2 christos * -------------------------------------------------------------------- 879 1.2 christos */ 880 1.2 christos void 881 1.2 christos test_GpsRollOver(void) 882 1.2 christos { 883 1.2 christos /* we test on wednesday, noon, and on the border */ 884 1.2 christos static const int32_t wsec1 = 3*SECSPERDAY + SECSPERDAY/2; 885 1.2 christos static const int32_t wsec2 = 7 * SECSPERDAY - 1; 886 1.2 christos static const int32_t week0 = GPSNTP_WSHIFT + 2047; 887 1.2 christos static const int32_t week1 = GPSNTP_WSHIFT + 2048; 888 1.2 christos TCivilDate jd; 889 1.2 christos TGpsDatum gps; 890 1.2 christos l_fp fpz; 891 1.2 christos 892 1.2 christos ZERO(fpz); 893 1.2 christos 894 1.2 christos /* test on 2nd rollover, April 2019 895 1.2 christos * we set the base date properly one week *before the rollover, to 896 1.2 christos * check if the expansion merrily hops over the warp. 897 1.2 christos */ 898 1.2 christos basedate_set_day(2047 * 7 + NTP_TO_GPS_DAYS); 899 1.2 christos 900 1.2 christos strToCal(&jd, "19-04-03T12:00:00"); 901 1.2 christos gps = gpscal_from_calendar(&jd, fpz); 902 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(week0, gps.weeks, "(week test 1))"); 903 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(wsec1, gps.wsecs, "(secs test 1)"); 904 1.2 christos 905 1.2 christos strToCal(&jd, "19-04-06T23:59:59"); 906 1.2 christos gps = gpscal_from_calendar(&jd, fpz); 907 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(week0, gps.weeks, "(week test 2)"); 908 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(wsec2, gps.wsecs, "(secs test 2)"); 909 1.2 christos 910 1.2 christos strToCal(&jd, "19-04-07T00:00:00"); 911 1.2 christos gps = gpscal_from_calendar(&jd, fpz); 912 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(week1, gps.weeks, "(week test 3)"); 913 1.2 christos TEST_ASSERT_EQUAL_MESSAGE( 0 , gps.wsecs, "(secs test 3)"); 914 1.2 christos 915 1.2 christos strToCal(&jd, "19-04-10T12:00:00"); 916 1.2 christos gps = gpscal_from_calendar(&jd, fpz); 917 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(week1, gps.weeks, "(week test 4)"); 918 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(wsec1, gps.wsecs, "(secs test 4)"); 919 1.2 christos } 920 1.2 christos 921 1.2 christos void 922 1.2 christos test_GpsRemapFunny(void) 923 1.2 christos { 924 1.2 christos TCivilDate di, dc, de; 925 1.2 christos TGpsDatum gd; 926 1.2 christos 927 1.2 christos l_fp fpz; 928 1.2 christos 929 1.2 christos ZERO(fpz); 930 1.2 christos basedate_set_day(2048 * 7 + NTP_TO_GPS_DAYS); 931 1.2 christos 932 1.2 christos /* expand 2digit year to 2080, then fold back into 3rd GPS era: */ 933 1.2 christos strToCal(&di, "80-01-01T00:00:00"); 934 1.2 christos strToCal(&de, "2021-02-15T00:00:00"); 935 1.2 christos gd = gpscal_from_calendar(&di, fpz); 936 1.2 christos gpscal_to_calendar(&dc, &gd); 937 1.2 christos TEST_ASSERT_TRUE(IsEqualCal(&de, &dc)); 938 1.2 christos 939 1.2 christos /* expand 2digit year to 2080, then fold back into 3rd GPS era: */ 940 1.2 christos strToCal(&di, "80-01-05T00:00:00"); 941 1.2 christos strToCal(&de, "2021-02-19T00:00:00"); 942 1.2 christos gd = gpscal_from_calendar(&di, fpz); 943 1.2 christos gpscal_to_calendar(&dc, &gd); 944 1.2 christos TEST_ASSERT_TRUE(IsEqualCal(&de, &dc)); 945 1.2 christos 946 1.2 christos /* remap days before epoch into 3rd era: */ 947 1.2 christos strToCal(&di, "1980-01-05T00:00:00"); 948 1.2 christos strToCal(&de, "2038-11-20T00:00:00"); 949 1.2 christos gd = gpscal_from_calendar(&di, fpz); 950 1.2 christos gpscal_to_calendar(&dc, &gd); 951 1.2 christos TEST_ASSERT_TRUE(IsEqualCal(&de, &dc)); 952 1.2 christos 953 1.2 christos /* remap GPS epoch: */ 954 1.2 christos strToCal(&di, "1980-01-06T00:00:00"); 955 1.2 christos strToCal(&de, "2019-04-07T00:00:00"); 956 1.2 christos gd = gpscal_from_calendar(&di, fpz); 957 1.2 christos gpscal_to_calendar(&dc, &gd); 958 1.2 christos TEST_ASSERT_TRUE(IsEqualCal(&de, &dc)); 959 1.2 christos } 960 1.2 christos 961 1.2 christos void 962 1.2 christos test_GpsNtpFixpoints(void) 963 1.2 christos { 964 1.2 christos basedate_set_day(NTP_TO_GPS_DAYS); 965 1.2 christos TGpsDatum e1gps; 966 1.2 christos TNtpDatum e1ntp, r1ntp; 967 1.2 christos l_fp lfpe , lfpr; 968 1.2 christos 969 1.2 christos lfpe.l_ui = 0; 970 1.2 christos lfpe.l_uf = UINT32_C(0x80000000); 971 1.2 christos 972 1.2 christos ZERO(e1gps); 973 1.2 christos e1gps.weeks = 0; 974 1.2 christos e1gps.wsecs = SECSPERDAY; 975 1.2 christos e1gps.frac = UINT32_C(0x80000000); 976 1.2 christos 977 1.2 christos ZERO(e1ntp); 978 1.2 christos e1ntp.frac = UINT32_C(0x80000000); 979 1.2 christos 980 1.2 christos r1ntp = gpsntp_from_gpscal(&e1gps); 981 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(e1ntp.days, r1ntp.days, "gps -> ntp / days"); 982 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(e1ntp.secs, r1ntp.secs, "gps -> ntp / secs"); 983 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(e1ntp.frac, r1ntp.frac, "gps -> ntp / frac"); 984 1.2 christos 985 1.2 christos lfpr = ntpfp_from_gpsdatum(&e1gps); 986 1.2 christos snprintf(mbuf, sizeof(mbuf), "gps -> l_fp: %s <=> %s", 987 1.2 christos lfptoa(&lfpe, 9), lfptoa(&lfpr, 9)); 988 1.2 christos TEST_ASSERT_TRUE_MESSAGE(L_ISEQU(&lfpe, &lfpr), mbuf); 989 1.2 christos 990 1.2 christos lfpr = ntpfp_from_ntpdatum(&e1ntp); 991 1.2 christos snprintf(mbuf, sizeof(mbuf), "ntp -> l_fp: %s <=> %s", 992 1.2 christos lfptoa(&lfpe, 9), lfptoa(&lfpr, 9)); 993 1.2 christos TEST_ASSERT_TRUE_MESSAGE(L_ISEQU(&lfpe, &lfpr), mbuf); 994 1.2 christos } 995 1.2 christos 996 1.2 christos void 997 1.2 christos test_CalUMod7(void) 998 1.2 christos { 999 1.2 christos TEST_ASSERT_EQUAL(0, u32mod7(0)); 1000 1.2 christos TEST_ASSERT_EQUAL(1, u32mod7(INT32_MAX)); 1001 1.2 christos TEST_ASSERT_EQUAL(2, u32mod7(UINT32_C(1)+INT32_MAX)); 1002 1.2 christos TEST_ASSERT_EQUAL(3, u32mod7(UINT32_MAX)); 1003 1.2 christos } 1004 1.2 christos 1005 1.2 christos void 1006 1.2 christos test_CalIMod7(void) 1007 1.2 christos { 1008 1.2 christos TEST_ASSERT_EQUAL(5, i32mod7(INT32_MIN)); 1009 1.2 christos TEST_ASSERT_EQUAL(6, i32mod7(-1)); 1010 1.2 christos TEST_ASSERT_EQUAL(0, i32mod7(0)); 1011 1.2 christos TEST_ASSERT_EQUAL(1, i32mod7(INT32_MAX)); 1012 1.2 christos } 1013 1.2 christos 1014 1.2 christos /* Century expansion tests. Reverse application of Zeller's congruence, 1015 1.2 christos * sort of... hence the name "Rellez", Zeller backwards. Just in case 1016 1.2 christos * you didn't notice ;) 1017 1.2 christos */ 1018 1.2 christos 1019 1.2 christos void 1020 1.3 christos test_RellezCentury1_1(void) 1021 1.2 christos { 1022 1.2 christos /* 1st day of a century */ 1023 1.2 christos TEST_ASSERT_EQUAL(1901, ntpcal_expand_century( 1, 1, 1, CAL_TUESDAY )); 1024 1.2 christos TEST_ASSERT_EQUAL(2001, ntpcal_expand_century( 1, 1, 1, CAL_MONDAY )); 1025 1.2 christos TEST_ASSERT_EQUAL(2101, ntpcal_expand_century( 1, 1, 1, CAL_SATURDAY )); 1026 1.2 christos TEST_ASSERT_EQUAL(2201, ntpcal_expand_century( 1, 1, 1, CAL_THURSDAY )); 1027 1.2 christos /* bad/impossible cases: */ 1028 1.2 christos TEST_ASSERT_EQUAL( 0, ntpcal_expand_century( 1, 1, 1, CAL_WEDNESDAY)); 1029 1.2 christos TEST_ASSERT_EQUAL( 0, ntpcal_expand_century( 1, 1, 1, CAL_FRIDAY )); 1030 1.2 christos TEST_ASSERT_EQUAL( 0, ntpcal_expand_century( 1, 1, 1, CAL_SUNDAY )); 1031 1.2 christos } 1032 1.2 christos 1033 1.2 christos void 1034 1.3 christos test_RellezCentury3_1(void) 1035 1.2 christos { 1036 1.2 christos /* 1st day in March of a century (the tricky point) */ 1037 1.2 christos TEST_ASSERT_EQUAL(1901, ntpcal_expand_century( 1, 3, 1, CAL_FRIDAY )); 1038 1.2 christos TEST_ASSERT_EQUAL(2001, ntpcal_expand_century( 1, 3, 1, CAL_THURSDAY )); 1039 1.2 christos TEST_ASSERT_EQUAL(2101, ntpcal_expand_century( 1, 3, 1, CAL_TUESDAY )); 1040 1.2 christos TEST_ASSERT_EQUAL(2201, ntpcal_expand_century( 1, 3, 1, CAL_SUNDAY )); 1041 1.2 christos /* bad/impossible cases: */ 1042 1.2 christos TEST_ASSERT_EQUAL( 0, ntpcal_expand_century( 1, 3, 1, CAL_MONDAY )); 1043 1.2 christos TEST_ASSERT_EQUAL( 0, ntpcal_expand_century( 1, 3, 1, CAL_WEDNESDAY)); 1044 1.2 christos TEST_ASSERT_EQUAL( 0, ntpcal_expand_century( 1, 3, 1, CAL_SATURDAY )); 1045 1.2 christos } 1046 1.2 christos 1047 1.2 christos void 1048 1.3 christos test_RellezYearZero(void) 1049 1.2 christos { 1050 1.2 christos /* the infamous year zero */ 1051 1.2 christos TEST_ASSERT_EQUAL(1900, ntpcal_expand_century( 0, 1, 1, CAL_MONDAY )); 1052 1.2 christos TEST_ASSERT_EQUAL(2000, ntpcal_expand_century( 0, 1, 1, CAL_SATURDAY )); 1053 1.2 christos TEST_ASSERT_EQUAL(2100, ntpcal_expand_century( 0, 1, 1, CAL_FRIDAY )); 1054 1.2 christos TEST_ASSERT_EQUAL(2200, ntpcal_expand_century( 0, 1, 1, CAL_WEDNESDAY)); 1055 1.2 christos /* bad/impossible cases: */ 1056 1.2 christos TEST_ASSERT_EQUAL( 0, ntpcal_expand_century( 0, 1, 1, CAL_TUESDAY )); 1057 1.2 christos TEST_ASSERT_EQUAL( 0, ntpcal_expand_century( 0, 1, 1, CAL_THURSDAY )); 1058 1.2 christos TEST_ASSERT_EQUAL( 0, ntpcal_expand_century( 0, 1, 1, CAL_SUNDAY )); 1059 1.2 christos } 1060 1.2 christos 1061 1.2 christos void test_RellezEra(void); 1062 1.2 christos void test_RellezEra(void) 1063 1.2 christos { 1064 1.2 christos static const unsigned int mt[13] = { 0, 31,28,31,30,31,30,31,31,30,31,30,31 }; 1065 1.2 christos unsigned int yi, yo, m, d, wd; 1066 1.2 christos 1067 1.2 christos /* last day before our era -- fold forward */ 1068 1.2 christos yi = 1899; 1069 1.2 christos m = 12; 1070 1.2 christos d = 31; 1071 1.2 christos wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1; 1072 1.2 christos yo = ntpcal_expand_century((yi%100), m, d, wd); 1073 1.2 christos snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u", 1074 1.2 christos yi, m, d, wd); 1075 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(2299, yo, mbuf); 1076 1.2 christos 1077 1.2 christos /* 1st day after our era -- fold back */ 1078 1.2 christos yi = 2300; 1079 1.2 christos m = 1; 1080 1.2 christos d = 1; 1081 1.2 christos wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1; 1082 1.2 christos yo = ntpcal_expand_century((yi%100), m, d, wd); 1083 1.2 christos snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u", 1084 1.2 christos yi, m, d, wd); 1085 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(1900, yo, mbuf); 1086 1.2 christos 1087 1.2 christos /* test every month in our 400y era */ 1088 1.2 christos for (yi = 1900; yi < 2300; ++yi) { 1089 1.2 christos for (m = 1; m < 12; ++m) { 1090 1.2 christos /* test first day of month */ 1091 1.2 christos d = 1; 1092 1.2 christos wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1; 1093 1.2 christos yo = ntpcal_expand_century((yi%100), m, d, wd); 1094 1.2 christos snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u", 1095 1.2 christos yi, m, d, wd); 1096 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(yi, yo, mbuf); 1097 1.2 christos 1098 1.2 christos /* test last day of month */ 1099 1.2 christos d = mt[m] + (m == 2 && is_leapyear(yi)); 1100 1.2 christos wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1; 1101 1.2 christos yo = ntpcal_expand_century((yi%100), m, d, wd); 1102 1.2 christos snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u", 1103 1.2 christos yi, m, d, wd); 1104 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(yi, yo, mbuf); 1105 1.2 christos } 1106 1.2 christos } 1107 1.2 christos } 1108 1.2 christos 1109 1.2 christos /* This is nearly a verbatim copy of the in-situ implementation of 1110 1.2 christos * Zeller's congruence in libparse/clk_rawdcf.c, so the algorithm 1111 1.2 christos * can be tested. 1112 1.2 christos */ 1113 1.2 christos static int 1114 1.2 christos zeller_expand( 1115 1.2 christos unsigned int y, 1116 1.2 christos unsigned int m, 1117 1.2 christos unsigned int d, 1118 1.2 christos unsigned int wd 1119 1.2 christos ) 1120 1.2 christos { 1121 1.2 christos unsigned int c; 1122 1.2 christos 1123 1.2 christos if ((y >= 100u) || (--m >= 12u) || (--d >= 31u) || (--wd >= 7u)) 1124 1.2 christos return 0; 1125 1.2 christos 1126 1.2 christos if ((m += 10u) >= 12u) 1127 1.2 christos m -= 12u; 1128 1.2 christos else if (--y >= 100u) 1129 1.2 christos y += 100u; 1130 1.2 christos d += y + (y >> 2) + 2u; 1131 1.2 christos d += (m * 83u + 16u) >> 5; 1132 1.2 christos 1133 1.2 christos c = (((252u + wd - d) * 0x6db6db6eU) >> 29) & 7u; 1134 1.2 christos if (c > 3u) 1135 1.2 christos return 0; 1136 1.2 christos 1137 1.2 christos if ((m > 9u) && (++y >= 100u)) { 1138 1.2 christos y -= 100u; 1139 1.2 christos c = (c + 1) & 3u; 1140 1.2 christos } 1141 1.2 christos y += (c * 100u); 1142 1.2 christos y += (y < 370u) ? 2000 : 1600; 1143 1.2 christos return (int)y; 1144 1.2 christos } 1145 1.2 christos 1146 1.2 christos void test_zellerDirect(void); 1147 1.2 christos void test_zellerDirect(void) 1148 1.2 christos { 1149 1.2 christos static const unsigned int mt[13] = { 0, 31,28,31,30,31,30,31,31,30,31,30,31 }; 1150 1.2 christos unsigned int yi, yo, m, d, wd; 1151 1.2 christos 1152 1.2 christos /* last day before our era -- fold forward */ 1153 1.2 christos yi = 1969; 1154 1.2 christos m = 12; 1155 1.2 christos d = 31; 1156 1.2 christos wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1; 1157 1.2 christos yo = zeller_expand((yi%100), m, d, wd); 1158 1.2 christos snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u", 1159 1.2 christos yi, m, d, wd); 1160 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(2369, yo, mbuf); 1161 1.2 christos 1162 1.2 christos /* 1st day after our era -- fold back */ 1163 1.2 christos yi = 2370; 1164 1.2 christos m = 1; 1165 1.2 christos d = 1; 1166 1.2 christos wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1; 1167 1.2 christos yo = zeller_expand((yi%100), m, d, wd); 1168 1.2 christos snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u", 1169 1.2 christos yi, m, d, wd); 1170 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(1970, yo, mbuf); 1171 1.2 christos 1172 1.2 christos /* test every month in our 400y era */ 1173 1.2 christos for (yi = 1970; yi < 2370; ++yi) { 1174 1.2 christos for (m = 1; m < 12; ++m) { 1175 1.2 christos /* test first day of month */ 1176 1.2 christos d = 1; 1177 1.2 christos wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1; 1178 1.2 christos yo = zeller_expand((yi%100), m, d, wd); 1179 1.2 christos snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u", 1180 1.2 christos yi, m, d, wd); 1181 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(yi, yo, mbuf); 1182 1.2 christos 1183 1.2 christos /* test last day of month */ 1184 1.2 christos d = mt[m] + (m == 2 && is_leapyear(yi)); 1185 1.2 christos wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1; 1186 1.2 christos yo = zeller_expand((yi%100), m, d, wd); 1187 1.2 christos snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u", 1188 1.2 christos yi, m, d, wd); 1189 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(yi, yo, mbuf); 1190 1.2 christos } 1191 1.2 christos } 1192 1.2 christos } 1193 1.2 christos 1194 1.2 christos void test_ZellerDirectBad(void); 1195 1.2 christos void test_ZellerDirectBad(void) 1196 1.2 christos { 1197 1.2 christos unsigned int y, n, wd; 1198 1.2 christos for (y = 2001; y < 2101; ++y) { 1199 1.2 christos wd = ntpcal_edate_to_eradays(y-1, 0, 0) % 7 + 1; 1200 1.2 christos /* move 4 centuries ahead */ 1201 1.2 christos wd = (wd + 5) % 7 + 1; 1202 1.2 christos for (n = 0; n < 3; ++n) { 1203 1.2 christos TEST_ASSERT_EQUAL(0, zeller_expand((y%100), 1, 1, wd)); 1204 1.2 christos wd = (wd + 4) % 7 + 1; 1205 1.2 christos } 1206 1.2 christos } 1207 1.2 christos } 1208 1.2 christos 1209 1.2 christos void test_zellerModInv(void); 1210 1.2 christos void test_zellerModInv(void) 1211 1.2 christos { 1212 1.2 christos unsigned int i, r1, r2; 1213 1.2 christos 1214 1.2 christos for (i = 0; i < 2048; ++i) { 1215 1.2 christos r1 = (3 * i) % 7; 1216 1.2 christos r2 = ((i * 0x6db6db6eU) >> 29) & 7u; 1217 1.2 christos snprintf(mbuf, sizeof(mbuf), "i=%u", i); 1218 1.2 christos TEST_ASSERT_EQUAL_MESSAGE(r1, r2, mbuf); 1219 1.2 christos } 1220 1.2 christos } 1221 1.2 christos 1222 1.1 christos 1223