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