1 #include "config.h"
2 #include "ntp.h"
3 #include "ntp_stdlib.h"
4 #include "parse.h"
5 
6 #include "unity.h"
7 #include "unity_fixture.h"
8 
9 #include "caltime.h"
10 
11 TEST_GROUP(calendar);
12 
TEST_SETUP(calendar)13 TEST_SETUP(calendar) {}
14 
TEST_TEAR_DOWN(calendar)15 TEST_TEAR_DOWN(calendar) {}
16 
17 
18 #include "ntp_calendar.h"
19 
20 static const char *DateToString(char *, const struct calendar *);
21 
DateToString(char * str,const struct calendar * cal)22 static const char *DateToString(char *str, const struct calendar *cal) {
23 	snprintf(str, 255, "%hu-%u-%u(%u)\n", cal->year, (unsigned int)cal->month, (unsigned int)cal->monthday, cal->yearday);
24 	return str;
25 }
26 
27 
IsEqualDate(const struct calendar * expected,const struct calendar * actual)28 static bool IsEqualDate(const struct calendar *expected,
29                         const struct calendar *actual) {
30 	char str[255];
31 	char str1[255];
32 	if (expected->year == actual->year &&
33 	    (!expected->yearday || expected->yearday == actual->yearday) &&
34 	    expected->month == actual->month &&
35 	    expected->monthday == actual->monthday) {
36 			return true;
37 	} else {
38 		printf("Expected: %s but was %s\n",
39                        DateToString(str, expected),
40                        DateToString(str1, actual));
41 		return false;
42 	}
43 }
44 
45 
46 // ---------------------------------------------------------------------
47 // test cases
48 // ---------------------------------------------------------------------
49 static const unsigned short real_month_table[2][13] = {
50 	/* -*- table for regular years -*- */
51 	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
52 	/* -*- table for leap years -*- */
53 	{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
54 };
55 
56 // days in month, with one month wrap-around at both ends
57 static const unsigned short real_month_days[2][14] = {
58 	/* -*- table for regular years -*- */
59 	{ 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31 },
60 	/* -*- table for leap years -*- */
61 	{ 31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31 }
62 };
63 
TEST(calendar,is_leapyear)64 TEST(calendar, is_leapyear) {
65         /* check is_leapyear() */
66 	TEST_ASSERT_EQUAL(false, is_leapyear(1900));
67 	TEST_ASSERT_EQUAL(false, is_leapyear(1970));
68 	TEST_ASSERT_EQUAL(false, is_leapyear(1999));
69 	TEST_ASSERT_EQUAL(true, is_leapyear(2000));
70 	TEST_ASSERT_EQUAL(false, is_leapyear(2001));
71 	TEST_ASSERT_EQUAL(true, is_leapyear(2004));
72 	TEST_ASSERT_EQUAL(true, is_leapyear(2040));
73 }
74 
TEST(calendar,julian0)75 TEST(calendar, julian0) {
76         /* check julian0() */
77 	TEST_ASSERT_EQUAL(693961, julian0(1900));
78 	TEST_ASSERT_EQUAL(719528, julian0(1970));
79 	TEST_ASSERT_EQUAL(730120, julian0(1999));
80 	TEST_ASSERT_EQUAL(730485, julian0(2000));
81 	TEST_ASSERT_EQUAL(730851, julian0(2001));
82 	TEST_ASSERT_EQUAL(745095, julian0(2040));
83 }
84 
TEST(calendar,days_per_year)85 TEST(calendar, days_per_year) {
86         /* check is_leapyear() */
87 	TEST_ASSERT_EQUAL(365, days_per_year(1900));
88 	TEST_ASSERT_EQUAL(365, days_per_year(1970));
89 	TEST_ASSERT_EQUAL(365, days_per_year(1999));
90 	TEST_ASSERT_EQUAL(366, days_per_year(2000));
91 	TEST_ASSERT_EQUAL(365, days_per_year(2001));
92 	TEST_ASSERT_EQUAL(366, days_per_year(2004));
93 	TEST_ASSERT_EQUAL(366, days_per_year(2040));
94 }
95 
96 #ifdef CLOCK_GENERIC
TEST(calendar,parse_to_unixtime)97 TEST(calendar, parse_to_unixtime) {
98         /* check is_leapyear() */
99         clocktime_t  	ct;
100         time_t       	result;
101         unsigned long       Flag;
102 
103         ct.day = 1;
104         ct.month = 1;
105         ct.year = 1970;
106         ct.hour = ct.minute = ct.second = ct.usecond = 0;
107         ct.utcoffset = 0;
108         ct.utctime = 0;
109         ct.flags = 0;
110 
111         Flag = 0;
112         result = parse_to_unixtime( &ct, &Flag );
113 
114 	TEST_ASSERT_EQUAL(0, result);
115 
116         ct.year = 2000;
117         ct.hour = 2;
118         ct.utctime = 0;
119         result = parse_to_unixtime( &ct, &Flag );
120 	TEST_ASSERT_EQUAL(946692000L, result);
121 
122         ct.year = 2037;
123         ct.minute = 2;
124         ct.second = 3;
125         ct.utctime = 0;
126         result = parse_to_unixtime( &ct, &Flag );
127 	TEST_ASSERT_EQUAL(2114388123L, result);
128 }
129 #endif
130 
TEST(calendar,PeriodicExtend1)131 TEST(calendar, PeriodicExtend1) {
132 	// Test positive cycle, pivot > value
133 	TEST_ASSERT_EQUAL(1001, ntpcal_periodic_extend(1000, 5, 2));
134 	// Test positive cycle, pivot < value
135 	TEST_ASSERT_EQUAL(6, ntpcal_periodic_extend(5, 1000, 2));
136 	// Test negative cycle, pivot > value
137 	TEST_ASSERT_EQUAL(999, ntpcal_periodic_extend(1000, 5, -2));
138 	// Test negative cycle, pivot < value
139 	TEST_ASSERT_EQUAL(4, ntpcal_periodic_extend(5, 1000, -2));
140 }
141 
142 // test the NTP to 64-bit Unix scale time conversion
TEST(calendar,NtpToTime1)143 TEST(calendar, NtpToTime1) {
144 	TEST_ASSERT_EQUAL(2085978538, ntpcal_ntp_to_time(42, 23));
145 }
146 
147 // test the NTP to 64-bit NTP scale time conversion
TEST(calendar,NtpToNtp1)148 TEST(calendar, NtpToNtp1) {
149 	TEST_ASSERT_EQUAL(4294967338, ntpcal_ntp_to_ntp(42, 23));
150 }
151 
152 // test the day/sec join & split ops, making sure that 32bit
153 // intermediate results would definitely overflow and the hi DWORD of
154 // the 'time64_t' is definitely needed.
TEST(calendar,DaySplitMerge)155 TEST(calendar, DaySplitMerge) {
156 	int32_t day;
157 	int32_t sec;
158 
159 	for (day = -1000000; day <= 1000000; day += 100) {
160 		for (sec = -100000; sec <= 186400; sec += 10000) {
161 			time64_t	     merge = ntpcal_dayjoin(day, sec);
162 			ntpcal_split split = ntpcal_daysplit(merge);
163 			int32_t	     eday  = day;
164 			int32_t	     esec  = sec;
165 
166 			while (esec >= 86400) {
167 				eday += 1;
168 				esec -= 86400;
169 			}
170 			while (esec < 0) {
171 				eday -= 1;
172 				esec += 86400;
173 			}
174 
175 			TEST_ASSERT_EQUAL(eday, split.hi);
176 			TEST_ASSERT_EQUAL(esec, split.lo);
177 		}
178 	}
179 }
180 
TEST(calendar,SplitEraDays1)181 TEST(calendar, SplitEraDays1) {
182 	ntpcal_split res;
183 	int32_t isleapyear = 42;
184 
185 	// Test no flag, no-leap, positive
186 	res = ntpcal_split_eradays(4, NULL);
187 	TEST_ASSERT_EQUAL(0, res.hi);
188 	TEST_ASSERT_EQUAL(4, res.lo);
189 	TEST_ASSERT_EQUAL(42, isleapyear);
190 
191 	// Test flag, no-leap, positive
192 	res = ntpcal_split_eradays(4, &isleapyear);
193 	TEST_ASSERT_EQUAL(0, res.hi);
194 	TEST_ASSERT_EQUAL(4, res.lo);
195 	TEST_ASSERT_EQUAL(0, isleapyear);
196 
197 	// Test flag, leap, positive
198 	res = ntpcal_split_eradays(1400, &isleapyear);
199 	TEST_ASSERT_EQUAL(3, res.hi);
200 	TEST_ASSERT_EQUAL(305, res.lo);
201 	TEST_ASSERT_EQUAL(1, isleapyear);
202 
203 	isleapyear = 0;
204 
205 	// Test flag, leap, negative
206 	res = ntpcal_split_eradays(-100, &isleapyear);
207 	TEST_ASSERT_EQUAL(-1, res.hi);
208 	TEST_ASSERT_EQUAL(266, res.lo);
209 	TEST_ASSERT_EQUAL(1, isleapyear);
210 }
211 
TEST(calendar,SplitYearDays1)212 TEST(calendar, SplitYearDays1) {
213 	int32_t eyd;
214 
215 	for (eyd = -1; eyd <= 365; eyd++) {
216 		ntpcal_split split = ntpcal_split_yeardays(eyd, 0);
217 		if (split.lo >= 0 && split.hi >= 0) {
218 			TEST_ASSERT_LESS_THAN_INT32(12, split.hi);
219 			TEST_ASSERT_LESS_THAN_INT32(real_month_days[0][split.hi+1], split.lo);
220 			int32_t tyd = real_month_table[0][split.hi] + split.lo;
221 			TEST_ASSERT_EQUAL(eyd, tyd);
222 		} else
223 			TEST_ASSERT_TRUE(eyd < 0 || eyd > 364);
224 	}
225 }
226 
TEST(calendar,SplitYearDays2)227 TEST(calendar, SplitYearDays2) {
228 	int32_t eyd;
229 
230 	for (eyd = -1; eyd <= 366; eyd++) {
231 		ntpcal_split split = ntpcal_split_yeardays(eyd, 1);
232 		if (split.lo >= 0 && split.hi >= 0) {
233 			TEST_ASSERT_LESS_THAN_INT32(12, split.hi);
234 			TEST_ASSERT_LESS_THAN_INT32(real_month_days[1][split.hi+1], split.lo);
235 			int32_t tyd = real_month_table[1][split.hi] + split.lo;
236 			TEST_ASSERT_EQUAL(eyd, tyd);
237 		} else
238 			TEST_ASSERT_TRUE(eyd < 0 || eyd > 365);
239 		}
240 }
241 
TEST(calendar,RataDie1)242 TEST(calendar, RataDie1) {
243 	int32_t	 testDate = 1; // 0001-01-01 (proleptic date)
244 	struct calendar expected = { 1, 1, 1, 1, 0, 0, 0, 0};
245 	struct calendar actual;
246 
247 	ntpcal_rd_to_date(&actual, testDate);
248 	TEST_ASSERT_TRUE(IsEqualDate(&expected, &actual));
249 }
250 
TEST(calendar,DaysecToDate1)251 TEST(calendar, DaysecToDate1) {
252 	struct calendar cal;
253 	int32_t days;
254 
255 	// Test normal date
256 	days = ntpcal_daysec_to_date(&cal, 100000);
257 	TEST_ASSERT_EQUAL(days, 1);
258 	TEST_ASSERT_EQUAL(cal.hour, 3);
259 	TEST_ASSERT_EQUAL(cal.minute, 46);
260 	TEST_ASSERT_EQUAL(cal.second, 40);
261 
262 	// Test negative date
263 	days = ntpcal_daysec_to_date(&cal, -100000);
264 	TEST_ASSERT_EQUAL(-2, days);
265 	TEST_ASSERT_EQUAL(20, cal.hour);
266 	TEST_ASSERT_EQUAL(13, cal.minute);
267 	TEST_ASSERT_EQUAL(20, cal.second);
268 }
269 
TEST(calendar,TimeToDate1)270 TEST(calendar, TimeToDate1) {
271 	struct calendar jd = {0, 0, 0, 0, 0, 0, 0, 0};
272 	int res;
273 
274 	res = ntpcal_time_to_date(&jd, 1000000);
275 	TEST_ASSERT_EQUAL(0, res);
276 	TEST_ASSERT_EQUAL(1970, jd.year);
277 	TEST_ASSERT_EQUAL(12, jd.yearday);
278 	TEST_ASSERT_EQUAL(1, jd.month);
279 	TEST_ASSERT_EQUAL(12, jd.monthday);
280 	TEST_ASSERT_EQUAL(13, jd.hour);
281 	TEST_ASSERT_EQUAL(46, jd.minute);
282 	TEST_ASSERT_EQUAL(40, jd.second);
283 	TEST_ASSERT_EQUAL(1, jd.weekday);
284 }
285 
TEST(calendar,DayJoin1)286 TEST(calendar, DayJoin1) {
287 	TEST_ASSERT_EQUAL(4323600, ntpcal_dayjoin(50, 3600));
288 }
289 
TEST(calendar,DaysInYears1)290 TEST(calendar, DaysInYears1) {
291 	// Test positive less than one gregorian cycle of years
292 	TEST_ASSERT_EQUAL(109572, ntpcal_days_in_years(300));
293 	// Test positive one gregorian cycle of years
294 	TEST_ASSERT_EQUAL(146097, ntpcal_days_in_years(400));
295 	// Test positive greater than one gregorian cycle of years
296 	TEST_ASSERT_EQUAL(182621, ntpcal_days_in_years(500));
297 	// Test negative less than one gregorian cycle of years
298 	TEST_ASSERT_EQUAL(-109573, ntpcal_days_in_years(-300));
299 	// Test negative one gregorian cycle of years
300 	TEST_ASSERT_EQUAL(-146097, ntpcal_days_in_years(-400));
301 	// Test negative greater than one gregorian cycle of years
302 	TEST_ASSERT_EQUAL(-182622, ntpcal_days_in_years(-500));
303 }
304 
TEST(calendar,EdateToEradays1)305 TEST(calendar, EdateToEradays1) {
306 	// Test positive, no months
307 	TEST_ASSERT_EQUAL(1827, ntpcal_edate_to_eradays(5, 0, 1));
308 	// Test positive, with months
309 	TEST_ASSERT_EQUAL(1917, ntpcal_edate_to_eradays(5, 3, 1));
310 	// Test negative, no months
311 	TEST_ASSERT_EQUAL(-1828, ntpcal_edate_to_eradays(-5, 0, -1));
312 	// Test negative, with months
313 	TEST_ASSERT_EQUAL(-1920, ntpcal_edate_to_eradays(-5, -3, -1));
314 }
315 
TEST(calendar,EtimeToSeconds1)316 TEST(calendar, EtimeToSeconds1) {
317 	TEST_ASSERT_EQUAL(18181, ntpcal_etime_to_seconds(5, 3, 1));
318 }
319 
TEST(calendar,TmToRd1)320 TEST(calendar, TmToRd1) {
321 	struct tm utm;
322 
323 	utm.tm_year = 10;
324 	utm.tm_mon = 5;
325 	utm.tm_mday = 1;
326 	TEST_ASSERT_EQUAL(697399, ntpcal_tm_to_rd(&utm));
327 }
328 
329 // check last day of february for first 10000 years
TEST(calendar,LeapYears1)330 TEST(calendar, LeapYears1) {
331 	struct calendar dateIn, dateOut;
332 
333 	for (dateIn.year = 1; dateIn.year < 10000; ++dateIn.year) {
334 		dateIn.month	= 2;
335 		dateIn.monthday = is_leapyear(dateIn.year) ? 29 : 28;
336 		dateIn.yearday	= 31 + dateIn.monthday;
337 
338 		ntpcal_rd_to_date(&dateOut, ntpcal_date_to_rd(&dateIn));
339 
340 		TEST_ASSERT_TRUE(IsEqualDate(&dateIn, &dateOut));
341 	}
342 }
343 
344 // check first day of march for first 10000 years
TEST(calendar,LeapYears2)345 TEST(calendar, LeapYears2) {
346 	struct calendar dateIn, dateOut;
347 
348 	for (dateIn.year = 1; dateIn.year < 10000; ++dateIn.year) {
349 		dateIn.month	= 3;
350 		dateIn.monthday = 1;
351 		dateIn.yearday	= is_leapyear(dateIn.year) ? 61 : 60;
352 
353 		ntpcal_rd_to_date(&dateOut, ntpcal_date_to_rd(&dateIn));
354 		TEST_ASSERT_TRUE(IsEqualDate(&dateIn, &dateOut));
355 	}
356 }
357 
358 // Full roundtrip for 1601-01-01 to 2400-12-31
359 // checks sequence of rata die numbers and validates date output
360 // (since the input is all nominal days of the calendar in that range
361 // and the result of the inverse calculation must match the input no
362 // invalid output can occur.)
TEST(calendar,RoundTripDate)363 TEST(calendar, RoundTripDate) {
364 	struct calendar truDate, expDate = { 1600, 0, 12, 31, 0, 0, 0, 0};
365 	int32_t	 truRdn, expRdn	= ntpcal_date_to_rd(&expDate);
366 	int	 leaps;
367 
368 	while (expDate.year < 2400) {
369 		expDate.year++;
370 		expDate.month	= 0;
371 		expDate.yearday = 0;
372 		leaps = is_leapyear(expDate.year) ? 1 : 0;
373 		while (expDate.month < 12) {
374 			expDate.month++;
375 			expDate.monthday = 0;
376 			while (expDate.monthday < real_month_days[leaps][expDate.month]) {
377 				expDate.monthday++;
378 				expDate.yearday++;
379 				expRdn++;
380 
381 				truRdn = ntpcal_date_to_rd(&expDate);
382 				TEST_ASSERT_EQUAL(expRdn, truRdn);
383 
384 				ntpcal_rd_to_date(&truDate, truRdn);
385 				TEST_ASSERT_TRUE(IsEqualDate(&expDate, &truDate));
386 			}
387 		}
388 	}
389 }
390 
TEST(calendar,DateToDaysec1)391 TEST(calendar, DateToDaysec1) {
392 	struct calendar jd;
393 
394 	jd.hour = 18;
395 	jd.minute = 45;
396 	jd.second = 15;
397 	TEST_ASSERT_EQUAL(67515, ntpcal_date_to_daysec(&jd));
398 }
399 
TEST(calendar,TmToDaysec1)400 TEST(calendar, TmToDaysec1) {
401 	struct tm utm;
402 
403 	utm.tm_hour = 18;
404 	utm.tm_min = 45;
405 	utm.tm_sec = 15;
406 	TEST_ASSERT_EQUAL(67515, ntpcal_tm_to_daysec(&utm));
407 }
408 
TEST(calendar,DateToTime1)409 TEST(calendar, DateToTime1) {
410 	struct calendar jd;
411 
412 	jd.year = 2000;
413 	jd.month = 2;
414 	jd.monthday = 4;
415 	jd.hour = 8;
416 	jd.minute = 16;
417 	jd.second = 32;
418 	TEST_ASSERT_EQUAL(949652192, ntpcal_date_to_time(&jd));
419 }
420 
TEST(calendar,Ntp64ToDate1)421 TEST(calendar, Ntp64ToDate1) {
422 	struct calendar jd;
423 
424 	TEST_ASSERT_EQUAL(0, ntpcal_ntp64_to_date(&jd, 10000000));
425 	TEST_ASSERT_EQUAL(1900, jd.year);
426 	TEST_ASSERT_EQUAL(4, jd.month);
427 	TEST_ASSERT_EQUAL(26, jd.monthday);
428 	TEST_ASSERT_EQUAL(17, jd.hour);
429 	TEST_ASSERT_EQUAL(46, jd.minute);
430 	TEST_ASSERT_EQUAL(40, jd.second);
431 }
432 
TEST(calendar,NtpToDate1)433 TEST(calendar, NtpToDate1) {
434 	struct calendar jd;
435 
436 	TEST_ASSERT_EQUAL(1, ntpcal_ntp_to_date(&jd, 86400, 1000000));
437 	TEST_ASSERT_EQUAL(2036, jd.year);
438 	TEST_ASSERT_EQUAL(2, jd.month);
439 	TEST_ASSERT_EQUAL(8, jd.monthday);
440 	TEST_ASSERT_EQUAL(6, jd.hour);
441 	TEST_ASSERT_EQUAL(28, jd.minute);
442 	TEST_ASSERT_EQUAL(16, jd.second);
443 }
444 
445 
TEST_GROUP_RUNNER(calendar)446 TEST_GROUP_RUNNER(calendar) {
447 	RUN_TEST_CASE(calendar, is_leapyear);
448 	RUN_TEST_CASE(calendar, julian0);
449 	RUN_TEST_CASE(calendar, days_per_year);
450 #ifdef CLOCK_GENERIC
451 	RUN_TEST_CASE(calendar, parse_to_unixtime);
452 #endif
453 	RUN_TEST_CASE(calendar, PeriodicExtend1);
454 	RUN_TEST_CASE(calendar, NtpToTime1);
455 	RUN_TEST_CASE(calendar, NtpToNtp1);
456 	RUN_TEST_CASE(calendar, DaySplitMerge);
457 	RUN_TEST_CASE(calendar, DaysecToDate1);
458 	RUN_TEST_CASE(calendar, SplitEraDays1);
459 	RUN_TEST_CASE(calendar, SplitYearDays1);
460 	RUN_TEST_CASE(calendar, SplitYearDays2);
461 	RUN_TEST_CASE(calendar, RataDie1);
462 	RUN_TEST_CASE(calendar, TimeToDate1);
463 	RUN_TEST_CASE(calendar, DayJoin1);
464 	RUN_TEST_CASE(calendar, DaysInYears1);
465 	RUN_TEST_CASE(calendar, EdateToEradays1);
466 	RUN_TEST_CASE(calendar, EtimeToSeconds1);
467 	RUN_TEST_CASE(calendar, TmToRd1);
468 	RUN_TEST_CASE(calendar, LeapYears1);
469 	RUN_TEST_CASE(calendar, LeapYears2);
470 	RUN_TEST_CASE(calendar, RoundTripDate);
471 	RUN_TEST_CASE(calendar, DateToDaysec1);
472 	RUN_TEST_CASE(calendar, TmToDaysec1);
473 	RUN_TEST_CASE(calendar, DateToTime1);
474 	RUN_TEST_CASE(calendar, Ntp64ToDate1);
475 	RUN_TEST_CASE(calendar, NtpToDate1);
476 }
477