1 /* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "test-lib.h"
4 #include "time-util.h"
5 
6 #include <time.h>
7 
test_timeval_cmp(void)8 static void test_timeval_cmp(void)
9 {
10 	static const struct {
11 		struct timeval tv1, tv2;
12 		int output;
13 	} tests[] = {
14 		{
15 			.tv1 = { 0, 0 },
16 			.tv2 = { 0, 0 },
17 			.output = 0,
18 		}, {
19 			.tv1 = { INT_MAX, 999999 },
20 			.tv2 = { INT_MAX, 999999 },
21 			.output = 0,
22 		}, {
23 			.tv1 = { 0, 0 },
24 			.tv2 = { 0, 1 },
25 			.output = -1,
26 		}, {
27 			.tv1 = { 0, 0 },
28 			.tv2 = { 1, 0 },
29 			.output = -1,
30 		}, {
31 			.tv1 = { 0, 999999 },
32 			.tv2 = { 1, 0 },
33 			.output = -1,
34 		}, {
35 			.tv1 = { 1, 0 },
36 			.tv2 = { 1, 1 },
37 			.output = -1,
38 		}, {
39 			.tv1 = { -INT_MAX, 0 },
40 			.tv2 = { INT_MAX, 0 },
41 			.output = -1,
42 		}
43 	};
44 	unsigned int i;
45 
46 	test_begin("timeval_cmp()");
47 	for (i = 0; i < N_ELEMENTS(tests); i++) {
48 		const struct timeval *tv1 = &tests[i].tv1, *tv2 = &tests[i].tv2;
49 		int output = tests[i].output;
50 
51 		test_assert(timeval_cmp(tv1, tv2) == output);
52 		test_assert(timeval_cmp(tv2, tv1) == -output);
53 	}
54 	test_end();
55 }
56 
test_timeval_cmp_margin(void)57 static void test_timeval_cmp_margin(void)
58 {
59 	static const struct {
60 		struct timeval tv1, tv2;
61 		unsigned int margin;
62 		int output;
63 	} tests[] = {
64 		{
65 			.tv1 = { 0, 0 },
66 			.tv2 = { 0, 0 },
67 			.output = 0,
68 		},{
69 			.tv1 = { INT_MAX, 999999 },
70 			.tv2 = { INT_MAX, 999999 },
71 			.output = 0,
72 
73 		},{
74 			.tv1 = { 0, 0 },
75 			.tv2 = { 0, 1 },
76 			.output = -1,
77 		},{
78 			.tv1 = { 0, 0 },
79 			.tv2 = { 1, 0 },
80 			.output = -1,
81 		},{
82 			.tv1 = { 0, 999999 },
83 			.tv2 = { 1, 0 },
84 			.output = -1,
85 		},{
86 			.tv1 = { 1, 0 },
87 			.tv2 = { 1, 1 },
88 			.output = -1,
89 		},{
90 			.tv1 = { -INT_MAX, 0 },
91 			.tv2 = { INT_MAX, 0 },
92 			.output = -1,
93 		},{
94 			.tv1 = { 0, 999999 },
95 			.tv2 = { 1, 0 },
96 			.margin = 1,
97 			.output = 0,
98 		},{
99 			.tv1 = { 1, 0 },
100 			.tv2 = { 1, 1 },
101 			.margin = 1,
102 			.output = 0,
103 		},{
104 			.tv1 = { 0, 999998 },
105 			.tv2 = { 1, 0 },
106 			.margin = 1,
107 			.output = -1,
108 		},{
109 			.tv1 = { 1, 0 },
110 			.tv2 = { 1, 2 },
111 			.margin = 1,
112 			.output = -1,
113 		},{
114 			.tv1 = { 0, 998000 },
115 			.tv2 = { 1, 0 },
116 			.margin = 2000,
117 			.output = 0,
118 		},{
119 			.tv1 = { 1, 0 },
120 			.tv2 = { 1, 2000 },
121 			.margin = 2000,
122 			.output = 0,
123 		},{
124 			.tv1 = { 0, 997999 },
125 			.tv2 = { 1, 0 },
126 			.margin = 2000,
127 			.output = -1,
128 		},{
129 			.tv1 = { 1, 0 },
130 			.tv2 = { 1, 2001 },
131 			.margin = 2000,
132 			.output = -1,
133 		},{
134 			.tv1 = { 0, 1 },
135 			.tv2 = { 1, 0 },
136 			.margin = 999999,
137 			.output = 0,
138 		},{
139 			.tv1 = { 1, 0 },
140 			.tv2 = { 1, 999999 },
141 			.margin = 999999,
142 			.output = 0,
143 		},{
144 			.tv1 = { 0, 0 },
145 			.tv2 = { 1, 0 },
146 			.margin = 999999,
147 			.output = -1,
148 		},{
149 			.tv1 = { 1, 0 },
150 			.tv2 = { 2, 0 },
151 			.margin = 999999,
152 			.output = -1,
153 		},{
154 			.tv1 = { 10, 0 },
155 			.tv2 = { 11, 500000 },
156 			.margin = 1500000,
157 			.output = 0,
158 		},{
159 			.tv1 = { 8, 500000 },
160 			.tv2 = { 10, 0 },
161 			.margin = 1500000,
162 			.output = 0,
163 		},{
164 			.tv1 = { 10, 0 },
165 			.tv2 = { 11, 500001 },
166 			.margin = 1500000,
167 			.output = -1,
168 		},{
169 			.tv1 = { 8, 499999 },
170 			.tv2 = { 10, 0 },
171 			.margin = 1500000,
172 			.output = -1,
173 		},{
174 			.tv1 = { 1517925358, 999989 },
175 			.tv2 = { 1517925359, 753 },
176 			.margin = 2000,
177 			.output = 0,
178 		}
179 	};
180 	unsigned int i;
181 
182 	test_begin("timeval_cmp_margin()");
183 	for (i = 0; i < N_ELEMENTS(tests); i++) {
184 		const struct timeval *tv1 = &tests[i].tv1, *tv2 = &tests[i].tv2;
185 		unsigned int margin = tests[i].margin;
186 		int output = tests[i].output;
187 
188 		test_assert(timeval_cmp_margin(tv1, tv2, margin) == output);
189 		test_assert(timeval_cmp_margin(tv2, tv1, margin) == -output);
190 	}
191 	test_end();
192 }
193 
test_timeval_diff(void)194 static void test_timeval_diff(void)
195 {
196 	static const struct timeval input[] = {
197 		{ 1, 0 }, { 0, 999999 },
198 		{ 1, 0 }, { 0, 999001 },
199 		{ 1, 1 }, { 0, 999001 },
200 		{ 2, 1 }, { 1, 0 },
201 		{ INT_MAX, 0 }, { INT_MAX-1, 1 }
202 	};
203 	static int output[] = {
204 		1,
205 		999,
206 		1000,
207 		1000001,
208 		999999
209 	};
210 	unsigned int i;
211 	long long udiff;
212 	int mdiff;
213 
214 	test_begin("timeval_diff_*()");
215 	for (i = 0; i < N_ELEMENTS(input); i += 2) {
216 		udiff = timeval_diff_usecs(&input[i], &input[i+1]);
217 		mdiff = timeval_diff_msecs(&input[i], &input[i+1]);
218 		test_assert(udiff == output[i/2]);
219 		test_assert(mdiff == udiff/1000);
220 
221 		udiff = timeval_diff_usecs(&input[i+1], &input[i]);
222 		mdiff = timeval_diff_msecs(&input[i+1], &input[i]);
223 		test_assert(udiff == -output[i/2]);
224 		test_assert(mdiff == udiff/1000);
225 	}
226 	test_end();
227 }
228 
test_time_to_local_day_start(void)229 static void test_time_to_local_day_start(void)
230 {
231 	/* Try this around days when DST changes in some of the more popular
232 	   timezones. If that works, everything else probably works too. */
233 	const struct tm tests[] = {
234 		/* Europe winter -> summer */
235 		{ .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 26 },
236 		{ .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 26,
237 		  .tm_hour = 23, .tm_min = 59, .tm_sec = 59 },
238 		/* Europe summer -> winter */
239 		{ .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 29 },
240 		{ .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 29,
241 		  .tm_hour = 23, .tm_min = 59, .tm_sec = 59 },
242 		/* USA winter -> summer */
243 		{ .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 12 },
244 		{ .tm_year = 2017-1900, .tm_mon = 2, .tm_mday = 12,
245 		  .tm_hour = 23, .tm_min = 59, .tm_sec = 59 },
246 		/* USA summer -> winter */
247 		{ .tm_year = 2017-1900, .tm_mon = 10, .tm_mday = 5 },
248 		{ .tm_year = 2017-1900, .tm_mon = 10, .tm_mday = 5,
249 		  .tm_hour = 23, .tm_min = 59, .tm_sec = 59 },
250 
251 		/* (some of) Australia summer -> winter */
252 		{ .tm_year = 2017-1900, .tm_mon = 3, .tm_mday = 2 },
253 		{ .tm_year = 2017-1900, .tm_mon = 3, .tm_mday = 2,
254 		  .tm_hour = 23, .tm_min = 59, .tm_sec = 59 },
255 		/* (some of) Australia winter -> summer */
256 		{ .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 1 },
257 		{ .tm_year = 2017-1900, .tm_mon = 9, .tm_mday = 1,
258 		  .tm_hour = 23, .tm_min = 59, .tm_sec = 59 },
259 	};
260 	const struct tm *tm;
261 	struct tm tm_copy;
262 	time_t t;
263 
264 	test_begin("time_to_local_day_start()");
265 	for (unsigned i = 0; i < N_ELEMENTS(tests); i++) {
266 		tm_copy = tests[i];
267 		tm_copy.tm_isdst = -1;
268 		t = mktime(&tm_copy);
269 		test_assert_idx(t != (time_t)-1, i);
270 
271 		t = time_to_local_day_start(t);
272 		tm = localtime(&t);
273 		test_assert_idx(tm->tm_year == tests[i].tm_year &&
274 				tm->tm_mon == tests[i].tm_mon &&
275 				tm->tm_mday == tests[i].tm_mday, i);
276 		test_assert_idx(tm->tm_hour == 0 && tm->tm_min == 0 &&
277 				tm->tm_sec == 0, i);
278 	}
279 	test_end();
280 }
281 
test_timestamp(const char * ts,int idx)282 static void test_timestamp(const char *ts, int idx)
283 {
284 	/* %G:%H:%M:%S */
285 	const char **t = t_strsplit(ts, ":");
286 	unsigned len = str_array_length(t);
287 	test_assert_idx(len == 4, idx);
288 
289 	/* %G - ISO 8601 year */
290 	test_assert_idx(strlen(t[0]) == 4, idx);
291 	unsigned v = 0;
292 	test_assert_idx(str_to_uint(t[0], &v) == 0, idx);
293 	test_assert_idx(1000 <= v, idx);
294 	test_assert_idx(v <= 3000, idx);
295 
296 	/* %H - hour from 00 to 23 */
297 	test_assert_idx(strlen(t[1]) == 2, idx);
298 	test_assert_idx(str_to_uint(t[1], &v) == 0, idx);
299 	test_assert_idx(v <= 23, idx);
300 
301 	/* %M - minute from 00 to 59 */
302 	test_assert_idx(strlen(t[2]) == 2, idx);
303 	test_assert_idx(str_to_uint(t[2], &v) == 0, idx);
304 	test_assert_idx(v <= 59, idx);
305 
306 	/* %S - second from 00 to 60 */
307 	test_assert_idx(strlen(t[3]) == 2, idx);
308 	test_assert_idx(str_to_uint(t[3], &v) == 0, idx);
309 	test_assert_idx(v <= 60, idx);
310 }
311 
312 #define TS_FMT "%G:%H:%M:%S"
test_strftime_now(void)313 static void test_strftime_now(void)
314 {
315 	test_begin("t_strftime and variants now");
316 
317 	time_t now = time(NULL);
318 	test_timestamp(t_strftime(TS_FMT, gmtime(&now)), 0);
319 	test_timestamp(t_strfgmtime(TS_FMT, now), 1);
320 	test_timestamp(t_strflocaltime(TS_FMT, now), 2);
321 
322 	test_end();
323 }
324 
325 #define RFC2822_FMT "%a, %d %b %Y %T"
test_strftime_fixed(void)326 static void test_strftime_fixed(void)
327 {
328 	test_begin("t_strftime and variants fixed timestamp");
329 
330 	time_t ts = 1481222536;
331 	const char *exp = "Thu, 08 Dec 2016 18:42:16";
332 	test_assert(strcmp(t_strftime(RFC2822_FMT, gmtime(&ts)), exp) == 0);
333 	test_assert(strcmp(t_strfgmtime(RFC2822_FMT, ts), exp) == 0);
334 
335 	test_end();
336 }
337 
test_micro_nanoseconds(void)338 static void test_micro_nanoseconds(void)
339 {
340 	uint64_t secs, usecs, nsecs;
341 
342 	test_begin("i_microseconds() and i_nanoseconds()");
343 
344 	secs = time(NULL);
345 	usecs = i_microseconds();
346 	nsecs = i_nanoseconds();
347 
348 	/* Assume max 1 seconds time difference between the calls. That should
349 	   be more than enough, while still not failing if there are temporary
350 	   hangs when running in heavily loaded systems. */
351 	test_assert(usecs/1000000 - secs <= 1);
352 	test_assert(nsecs/1000 - usecs <= 1000000);
353 
354 	test_end();
355 }
356 
test_str_to_timeval(void)357 static void test_str_to_timeval(void)
358 {
359 	struct {
360 		const char *str;
361 		time_t tv_sec;
362 		suseconds_t tv_usec;
363 	} tests[] = {
364 		{ "0", 0, 0 },
365 		{ "0.0", 0, 0 },
366 		{ "0.000000", 0, 0 },
367 		{ "0.1", 0, 100000 },
368 		{ "0.100000", 0, 100000 },
369 		{ "0.000001", 0, 1 },
370 		{ "0.100000", 0, 100000 },
371 		{ "2147483647", 2147483647, 0 },
372 		{ "2147483647.999999", 2147483647, 999999 },
373 	};
374 	const char *test_failures[] = {
375 		"",
376 		"0.",
377 		"0.0000000",
378 		"1234.-1",
379 		"1234.x",
380 		"x",
381 		"x.100",
382 	};
383 	struct timeval tv;
384 
385 	test_begin("str_to_timeval");
386 	for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
387 		test_assert_idx(str_to_timeval(tests[i].str, &tv) == 0, i);
388 		test_assert_idx(tv.tv_sec == tests[i].tv_sec, i);
389 		test_assert_idx(tv.tv_usec == tests[i].tv_usec, i);
390 	}
391 	for (unsigned int i = 0; i < N_ELEMENTS(test_failures); i++)
392 		test_assert_idx(str_to_timeval(test_failures[i], &tv) == -1, i);
393 	test_end();
394 }
395 
test_time_util(void)396 void test_time_util(void)
397 {
398 	test_timeval_cmp();
399 	test_timeval_cmp_margin();
400 	test_timeval_diff();
401 	test_time_to_local_day_start();
402 	test_strftime_now();
403 	test_strftime_fixed();
404 	test_micro_nanoseconds();
405 	test_str_to_timeval();
406 }
407