Add a little more to the svn_rangelist_intersect test to test the
[svn.git] / subversion / tests / libsvn_subr / time-test.c
blob73f5bced0018be83161f0542514f0ffe3add040c
1 /*
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 * ====================================================================
19 #include <stdio.h>
20 #include <string.h>
21 #include <apr_general.h>
22 #include "svn_time.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)";
34 static svn_error_t *
35 test_time_to_cstring(const char **msg,
36 svn_boolean_t msg_only,
37 svn_test_opts_t *opts,
38 apr_pool_t *pool)
40 const char *timestring;
42 *msg = "test svn_time_to_cstring";
44 if (msg_only)
45 return SVN_NO_ERROR;
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);
58 return SVN_NO_ERROR;
62 static svn_error_t *
63 test_time_from_cstring(const char **msg,
64 svn_boolean_t msg_only,
65 svn_test_opts_t *opts,
66 apr_pool_t *pool)
68 apr_time_t timestamp;
70 *msg = "test svn_time_from_cstring";
72 if (msg_only)
73 return SVN_NO_ERROR;
75 SVN_ERR(svn_time_from_cstring(&timestamp, 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);
86 return SVN_NO_ERROR;
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[] = {
93 /* Overflow Day */
94 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
95 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
96 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
97 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
98 " 3 Oct 2000 HH:MM:SS.UUU (day 277, dst 1, gmt_off -18000)",
100 /* Overflow Month */
101 "Tue 3 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
102 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
103 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
104 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
105 " 2000 HH:MM:SS.UUU (day 277, dst 1, gmt_off -18000)",
107 NULL
110 static svn_error_t *
111 test_time_from_cstring_old(const char **msg,
112 svn_boolean_t msg_only,
113 svn_test_opts_t *opts,
114 apr_pool_t *pool)
116 apr_time_t timestamp;
117 const char **ft;
119 *msg = "test svn_time_from_cstring (old format)";
121 if (msg_only)
122 return SVN_NO_ERROR;
124 SVN_ERR(svn_time_from_cstring(&timestamp, 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
141 * all platforms. */
142 for (ft = failure_old_tests; *ft; ft++)
144 svn_error_t *err = svn_time_from_cstring(&timestamp, *ft, pool);
145 if (! err)
146 return svn_error_createf
147 (SVN_ERR_TEST_FAILED, NULL,
148 "svn_time_from_cstring (%s) succeeded when it should have failed",
149 *ft);
150 svn_error_clear(err);
153 return SVN_NO_ERROR;
157 static svn_error_t *
158 test_time_invariant(const char **msg,
159 svn_boolean_t msg_only,
160 svn_test_opts_t *opts,
161 apr_pool_t *pool)
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";
169 if (msg_only)
170 return SVN_NO_ERROR;
172 timestring = svn_time_to_cstring(current_timestamp, pool);
173 SVN_ERR(svn_time_from_cstring(&timestamp, 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 '%"
180 APR_TIME_T_FMT
181 "' instead of '%" APR_TIME_T_FMT "'",
182 timestamp,current_timestamp);
185 return SVN_NO_ERROR;
189 struct date_test {
190 const char *str;
191 apr_int32_t year;
192 apr_int32_t mon;
193 apr_int32_t mday;
194 apr_int32_t hour;
195 apr_int32_t min;
196 apr_int32_t sec;
197 apr_int32_t usec;
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[] = {
204 /* YYYY-M[M]-D[D] */
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 },
220 /* YYYYMMDD */
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 },
232 { NULL }
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 },
254 { NULL }
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 },
264 { NULL }
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 */
280 "2000-01-01+01:00",
281 "20000101Z",
282 "20000101-0100",
283 "2000-01-01T10", /* Time with hours but no minutes */
284 "20000101T10",
285 "2000-01-01 10",
286 NULL
289 static svn_error_t *
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);
299 return SVN_NO_ERROR;
302 static svn_error_t *
303 test_parse_date(const char **msg,
304 svn_boolean_t msg_only,
305 svn_test_opts_t *opts,
306 apr_pool_t *pool)
308 apr_time_t now, result;
309 apr_time_exp_t nowexp, expt;
310 svn_boolean_t matched;
311 struct date_test *dt;
312 const char **ft;
314 *msg = "test svn_parse_date";
316 if (msg_only)
317 return SVN_NO_ERROR;
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));
326 if (!matched)
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));
338 if (!matched)
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));
350 if (!matched)
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));
365 if (matched)
366 return svn_error_createf
367 (SVN_ERR_TEST_FAILED, NULL, "Match succeeded for '%s'", *ft);
370 return SVN_NO_ERROR;
375 /* The test table. */
377 struct svn_test_descriptor_t test_funcs[] =
379 SVN_TEST_NULL,
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),
385 SVN_TEST_NULL