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