2 * time-test.c -- test the time functions
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
21 #include <apr_general.h>
24 #include "../svn_test.h"
26 /* All these variables should refer to the same point in time. */
27 apr_time_t test_timestamp
= APR_TIME_C(1021316450966679);
28 const char *test_timestring
=
29 "2002-05-13T19:00:50.966679Z";
30 const char *test_old_timestring
=
31 "Mon 13 May 2002 22:00:50.966679 (day 133, dst 1, gmt_off 010800)";
35 test_time_to_cstring(const char **msg
,
36 svn_boolean_t msg_only
,
37 svn_test_opts_t
*opts
,
40 const char *timestring
;
42 *msg
= "test svn_time_to_cstring";
47 timestring
= svn_time_to_cstring(test_timestamp
,pool
);
49 if (strcmp(timestring
,test_timestring
) != 0)
51 return svn_error_createf
52 (SVN_ERR_TEST_FAILED
, NULL
,
53 "svn_time_to_cstring (%" APR_TIME_T_FMT
54 ") returned date string '%s' instead of '%s'",
55 test_timestamp
,timestring
,test_timestring
);
63 test_time_from_cstring(const char **msg
,
64 svn_boolean_t msg_only
,
65 svn_test_opts_t
*opts
,
70 *msg
= "test svn_time_from_cstring";
75 SVN_ERR(svn_time_from_cstring(×tamp
, test_timestring
, pool
));
77 if (timestamp
!= test_timestamp
)
79 return svn_error_createf
80 (SVN_ERR_TEST_FAILED
, NULL
,
81 "svn_time_from_cstring (%s) returned time '%" APR_TIME_T_FMT
82 "' instead of '%" APR_TIME_T_FMT
"'",
83 test_timestring
,timestamp
,test_timestamp
);
89 /* Before editing these tests cases please see the comment in
90 * test_time_from_cstring_old regarding the requirements to exercise the bug
91 * that they exist to test. */
92 static const char *failure_old_tests
[] = {
94 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
95 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
96 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
97 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
98 " 3 Oct 2000 HH:MM:SS.UUU (day 277, dst 1, gmt_off -18000)",
101 "Tue 3 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
102 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
103 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
104 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
105 " 2000 HH:MM:SS.UUU (day 277, dst 1, gmt_off -18000)",
111 test_time_from_cstring_old(const char **msg
,
112 svn_boolean_t msg_only
,
113 svn_test_opts_t
*opts
,
116 apr_time_t timestamp
;
119 *msg
= "test svn_time_from_cstring (old format)";
124 SVN_ERR(svn_time_from_cstring(×tamp
, test_old_timestring
, pool
));
126 if (timestamp
!= test_timestamp
)
128 return svn_error_createf
129 (SVN_ERR_TEST_FAILED
, NULL
,
130 "svn_time_from_cstring (%s) returned time '%" APR_TIME_T_FMT
131 "' instead of '%" APR_TIME_T_FMT
"'",
132 test_old_timestring
,timestamp
,test_timestamp
);
135 /* These tests should fail. They've been added to cover a string overflow
136 * found in our code. However, even if they fail that may not indicate
137 * that there is no problem. The strings being tested need to be
138 * sufficently long to cause a segmentation fault in order to exercise
139 * this bug. Unfortunately due to differences in compilers, architectures,
140 * etc. there is no way to be sure that the bug is being exerercised on
142 for (ft
= failure_old_tests
; *ft
; ft
++)
144 svn_error_t
*err
= svn_time_from_cstring(×tamp
, *ft
, pool
);
146 return svn_error_createf
147 (SVN_ERR_TEST_FAILED
, NULL
,
148 "svn_time_from_cstring (%s) succeeded when it should have failed",
150 svn_error_clear(err
);
158 test_time_invariant(const char **msg
,
159 svn_boolean_t msg_only
,
160 svn_test_opts_t
*opts
,
163 apr_time_t current_timestamp
= apr_time_now();
164 const char *timestring
;
165 apr_time_t timestamp
;
167 *msg
= "test svn_time_[to/from]_cstring() invariant";
172 timestring
= svn_time_to_cstring(current_timestamp
, pool
);
173 SVN_ERR(svn_time_from_cstring(×tamp
, timestring
, pool
));
175 if (timestamp
!= current_timestamp
)
177 return svn_error_createf
178 (SVN_ERR_TEST_FAILED
, NULL
,
179 "svn_time_from_cstring ( svn_time_to_cstring (n) ) returned time '%"
181 "' instead of '%" APR_TIME_T_FMT
"'",
182 timestamp
,current_timestamp
);
200 /* These date strings do not specify a time zone, so we expand the
201 svn_parse_date result it in the local time zone and verify that
202 against the desired values. */
203 static struct date_test localtz_tests
[] = {
205 { "2013-01-25", 2013, 1, 25, 0, 0, 0, 0 },
206 { "2013-1-25", 2013, 1, 25, 0, 0, 0, 0 },
207 { "2013-01-2", 2013, 1, 2, 0, 0, 0, 0 },
208 /* YYYY-M[M]-D[D][Th[h]:mm[:ss[.u[u[u[u[u[u] */
209 { "2015-04-26T00:01:59.652655", 2015, 4, 26, 0, 1, 59, 652655 },
210 { "2034-07-20T17:03:36.11379", 2034, 7, 20, 17, 3, 36, 113790 },
211 { "1975-10-29T17:23:56.3859", 1975, 10, 29, 17, 23, 56, 385900 },
212 { "2024-06-08T13:06:14.897", 2024, 6, 8, 13, 6, 14, 897000 },
213 { "2000-01-10T05:11:11.65", 2000, 1, 10, 5, 11, 11, 650000 },
214 { "2017-01-28T07:21:13.2", 2017, 1, 28, 7, 21, 13, 200000 },
215 { "2013-05-18T13:52:49", 2013, 5, 18, 13, 52, 49, 0 },
216 { "2020-05-14T15:28", 2020, 5, 14, 15, 28, 0, 0 },
217 { "2032-05-14T7:28", 2032, 5, 14, 7, 28, 0, 0 },
218 { "2020-5-14T15:28", 2020, 5, 14, 15, 28, 0, 0 },
219 { "2020-05-1T15:28", 2020, 5, 1, 15, 28, 0, 0 },
221 { "20100226", 2010, 2, 26, 0, 0, 0, 0 },
222 /* YYYYMMDD[Thhmm[ss[.u[u[u[u[u[u] */
223 { "19860214T050745.6", 1986, 2, 14, 5, 7, 45, 600000 },
224 { "20230219T0045", 2023, 2, 19, 0, 45, 0, 0 },
225 /* YYYY-M[M]-D[D] [h]h:mm[:ss[.u[u[u[u[u[u] */
226 { "1975-07-11 06:31:49.749504", 1975, 7, 11, 6, 31, 49, 749504 },
227 { "2037-05-06 00:08", 2037, 5, 6, 0, 8, 0, 0 },
228 { "2037-5-6 7:01", 2037, 5, 6, 7, 1, 0, 0 },
229 /* Make sure we can do leap days. */
230 { "1976-02-29", 1976, 02, 29, 0, 0, 0, 0 },
231 { "2000-02-29", 2000, 02, 29, 0, 0, 0, 0 },
235 /* These date strings specify an explicit time zone, so we expand the
236 svn_parse_date result in UTC and verify that against the desired
237 values (which have been adjusted for the specified time zone). */
238 static struct date_test gmt_tests
[] = {
239 /* YYYY-MM-DDThh:mm[:ss[.u[u[u[u[u[u]Z */
240 { "1979-05-05T15:16:04.39Z", 1979, 5, 5, 15, 16, 4, 390000 },
241 { "2012-03-25T12:14Z", 2012, 3, 25, 12, 14, 0, 0 },
242 /* YYYY-MM-DDThh:mm[:ss[.u[u[u[u[u[u]+OO[:oo] */
243 { "1991-09-13T20:13:01.12779+02:26", 1991, 9, 13, 17, 47, 1, 127790 },
244 { "1971-07-20T06:11-10", 1971, 7, 20, 16, 11, 0, 0 },
245 /* YYYYMMDDThhmm[ss[.u[u[u[u[u[u]Z */
246 { "20010808T105155.527Z", 2001, 8, 8, 10, 51, 55, 527000 },
247 { "19781204T1322Z", 1978, 12, 4, 13, 22, 0, 0 },
248 /* YYYYMMDDThhmm[ss[.u[u[u[u[u[u]+OO[oo] */
249 { "20030930T001647.8008-0230", 2003, 9, 30, 2, 46, 47, 800800 },
250 { "19810526T1705+22", 1981, 5, 25, 19, 5, 0, 0 },
251 /* YYYY-MM-DD hh:mm[:ss[.u[u[u[u[u[u][ +OO[oo] */
252 { "2024-06-02 11:30:36 +08", 2024, 6, 2, 3, 30, 36, 0 },
253 { "1994-10-07 08:08 -1457", 1994, 10, 07, 23, 5, 0, 0 },
257 /* These date strings only specify a time of day, so we fill the
258 current date into the desired values before comparing. (Currently
259 we do not allow a time zone with just a time of day.) */
260 static struct date_test daytime_tests
[] = {
261 /* hh:mm[:ss[.u[u[u[u[u[u] */
262 { "00:54:15.46", 0, 0, 0, 0, 54, 15, 460000 },
263 { "18:21", 0, 0, 0, 18, 21, 0, 0 },
267 /* These date strings should not parse correctly. */
268 static const char *failure_tests
[] = {
269 "2000-00-02", /* Invalid month */
270 "2000-13-02", /* Invalid month */
271 "2000-01-32", /* Invalid day */
272 "2000-01-00", /* Invalid day */
273 "1999-02-29", /* Invalid leap day */
274 "2000-01-01 24:00:00", /* Invalid hour */
275 "2000-01-01 00:60:00", /* Invalid minute */
276 "2000-01-01 00:00:61", /* Invalid second (60 is okay for leap seconds) */
277 "2000-01-01+24:00", /* Invalid timezone hours */
278 "2000-01-01-02:60", /* Invalid timezone minutes */
279 "2000-01-01Z", /* Date with timezone, but no time */
283 "2000-01-01T10", /* Time with hours but no minutes */
290 compare_results(struct date_test
*dt
,
291 apr_time_exp_t
*expt
)
293 if (expt
->tm_year
+ 1900 != dt
->year
|| expt
->tm_mon
+ 1 != dt
->mon
294 || expt
->tm_mday
!= dt
->mday
|| expt
->tm_hour
!= dt
->hour
295 || expt
->tm_min
!= dt
->min
|| expt
->tm_sec
!= dt
->sec
296 || expt
->tm_usec
!= dt
->usec
)
297 return svn_error_createf
298 (SVN_ERR_TEST_FAILED
, NULL
, "Comparison failed for '%s'", dt
->str
);
303 test_parse_date(const char **msg
,
304 svn_boolean_t msg_only
,
305 svn_test_opts_t
*opts
,
308 apr_time_t now
, result
;
309 apr_time_exp_t nowexp
, expt
;
310 svn_boolean_t matched
;
311 struct date_test
*dt
;
314 *msg
= "test svn_parse_date";
319 now
= apr_time_now();
320 if (apr_time_exp_lt(&nowexp
, now
) != APR_SUCCESS
)
321 return svn_error_create(SVN_ERR_TEST_FAILED
, NULL
, "Can't expand time");
323 for (dt
= localtz_tests
; dt
->str
; dt
++)
325 SVN_ERR(svn_parse_date(&matched
, &result
, dt
->str
, now
, pool
));
327 return svn_error_createf
328 (SVN_ERR_TEST_FAILED
, NULL
, "Match failed for '%s'", dt
->str
);
329 if (apr_time_exp_lt(&expt
, result
) != APR_SUCCESS
)
330 return svn_error_createf
331 (SVN_ERR_TEST_FAILED
, NULL
, "Expand failed for '%s'", dt
->str
);
332 SVN_ERR(compare_results(dt
, &expt
));
335 for (dt
= gmt_tests
; dt
->str
; dt
++)
337 SVN_ERR(svn_parse_date(&matched
, &result
, dt
->str
, now
, pool
));
339 return svn_error_createf
340 (SVN_ERR_TEST_FAILED
, NULL
, "Match failed for '%s'", dt
->str
);
341 if (apr_time_exp_gmt(&expt
, result
) != APR_SUCCESS
)
342 return svn_error_createf
343 (SVN_ERR_TEST_FAILED
, NULL
, "Expand failed for '%s'", dt
->str
);
344 SVN_ERR(compare_results(dt
, &expt
));
347 for (dt
= daytime_tests
; dt
->str
; dt
++)
349 SVN_ERR(svn_parse_date(&matched
, &result
, dt
->str
, now
, pool
));
351 return svn_error_createf
352 (SVN_ERR_TEST_FAILED
, NULL
, "Match failed for '%s'", dt
->str
);
353 if (apr_time_exp_lt(&expt
, result
) != APR_SUCCESS
)
354 return svn_error_createf
355 (SVN_ERR_TEST_FAILED
, NULL
, "Expand failed for '%s'", dt
->str
);
356 dt
->year
= nowexp
.tm_year
+ 1900;
357 dt
->mon
= nowexp
.tm_mon
+ 1;
358 dt
->mday
= nowexp
.tm_mday
;
359 SVN_ERR(compare_results(dt
, &expt
));
362 for (ft
= failure_tests
; *ft
; ft
++)
364 SVN_ERR(svn_parse_date(&matched
, &result
, *ft
, now
, pool
));
366 return svn_error_createf
367 (SVN_ERR_TEST_FAILED
, NULL
, "Match succeeded for '%s'", *ft
);
375 /* The test table. */
377 struct svn_test_descriptor_t test_funcs
[] =
380 SVN_TEST_PASS(test_time_to_cstring
),
381 SVN_TEST_PASS(test_time_from_cstring
),
382 SVN_TEST_PASS(test_time_from_cstring_old
),
383 SVN_TEST_PASS(test_time_invariant
),
384 SVN_TEST_PASS(test_parse_date
),