Branch data Line data Source code
1 : : // Copyright 2011-2025 David Robillard <d@drobilla.net>
2 : : // SPDX-License-Identifier: ISC
3 : :
4 : : #undef NDEBUG
5 : :
6 : : #include "time_test_utils.h"
7 : :
8 : : #include <exess/exess.h>
9 : :
10 : : #include <assert.h>
11 : : #include <stdbool.h>
12 : : #include <stdint.h>
13 : : #include <string.h>
14 : :
15 : : static const ExessDate nozone = {2001, 1, 2, EXESS_TIMEZONE_LOCAL};
16 : : static const ExessDate utc = {2002, 2, 3, EXESS_TIMEZONE_UTC};
17 : : static const ExessDate zoned = {2003, 3, 4, INIT_ZONE(11, 30)};
18 : : static const ExessDate early = {99, 3, 4, INIT_ZONE(11, 30)};
19 : : static const ExessDate future = {12345, 3, 4, INIT_ZONE(11, 30)};
20 : : static const ExessDate lowest = {INT16_MIN, 1, 1, INIT_ZONE(-14, 0)};
21 : : static const ExessDate highest = {INT16_MAX, 1, 1, INIT_ZONE(14, 0)};
22 : : static const ExessDate garbage1 = {2004, 0, 1, INIT_ZONE(11, 30)};
23 : : static const ExessDate garbage2 = {2005, 13, 1, INIT_ZONE(11, 30)};
24 : : static const ExessDate garbage3 = {2006, 1, 0, INIT_ZONE(11, 30)};
25 : : static const ExessDate garbage4 = {2006, 1, 32, INIT_ZONE(11, 30)};
26 : :
27 : : static void
28 : 47 : check_read(const char* const string,
29 : : const ExessStatus expected_status,
30 : : const int64_t expected_year,
31 : : const uint8_t expected_month,
32 : : const uint8_t expected_day,
33 : : const int8_t expected_tz_hour,
34 : : const int8_t expected_tz_minute,
35 : : const bool expected_tz_is_present,
36 : : const size_t expected_count)
37 : : {
38 : 47 : ExessDate value = {0, 0, 0, EXESS_TIMEZONE_LOCAL};
39 : :
40 : 47 : const ExessResult r = exess_read_date(string, &value);
41 [ - + ]: 47 : assert(r.status == expected_status);
42 [ - + ]: 47 : assert(r.count == expected_count);
43 [ - + ]: 47 : assert(value.year == expected_year);
44 [ - + ]: 47 : assert(value.month == expected_month);
45 [ - + ]: 47 : assert(value.day == expected_day);
46 [ + + + + : 47 : assert((!expected_tz_is_present && value.zone == EXESS_TIMEZONE_LOCAL) ||
- + ]
47 : : value.zone == (4 * expected_tz_hour) + (expected_tz_minute / 15));
48 : 47 : }
49 : :
50 : : static void
51 : 1 : test_read_date(void)
52 : : {
53 : : // No input
54 : 1 : check_read("", EXESS_EXPECTED_DIGIT, 0, 0, 0, 0, 0, false, 0);
55 : 1 : check_read("\t\n\r ", EXESS_EXPECTED_DIGIT, 0, 0, 0, 0, 0, false, 4);
56 : :
57 : : // Good values
58 : 1 : check_read("2004-04-12", EXESS_SUCCESS, 2004, 4, 12, 0, 0, false, 10);
59 : 1 : check_read("-0045-01-01", EXESS_SUCCESS, -45, 1, 1, 0, 0, false, 11);
60 : 1 : check_read("12004-04-12", EXESS_SUCCESS, 12004, 4, 12, 0, 0, false, 11);
61 : 1 : check_read("2004-04-12-05:00", EXESS_SUCCESS, 2004, 4, 12, -5, 0, true, 16);
62 : 1 : check_read("2004-04-12Z", EXESS_SUCCESS, 2004, 4, 12, 0, 0, true, 11);
63 : 1 : check_read("2001-10-26", EXESS_SUCCESS, 2001, 10, 26, 0, 0, false, 10);
64 : 1 : check_read("2001-10-26+02:00", EXESS_SUCCESS, 2001, 10, 26, 2, 0, true, 16);
65 : 1 : check_read("2001-10-26Z", EXESS_SUCCESS, 2001, 10, 26, 0, 0, true, 11);
66 : 1 : check_read("2001-10-26+00:00", EXESS_SUCCESS, 2001, 10, 26, 0, 0, true, 16);
67 : 1 : check_read("-2001-10-26", EXESS_SUCCESS, -2001, 10, 26, 0, 0, false, 11);
68 : 1 : check_read("-20000-04-01", EXESS_SUCCESS, -20000, 04, 01, 0, 0, false, 12);
69 : :
70 : : // Non-canonical
71 : 1 : check_read("02004-04-12", EXESS_SUCCESS, 2004, 4, 12, 0, 0, false, 11);
72 : 1 : check_read(" 02004-04-12 ", EXESS_SUCCESS, 2004, 4, 12, 0, 0, false, 12);
73 : :
74 : : // Good common year values
75 : 1 : check_read("1900-02-28", EXESS_SUCCESS, 1900, 2, 28, 0, 0, false, 10);
76 : :
77 : : // Good leap year values
78 : 1 : check_read("2000-02-29", EXESS_SUCCESS, 2000, 2, 29, 0, 0, false, 10);
79 : 1 : check_read("2004-02-29", EXESS_SUCCESS, 2004, 2, 29, 0, 0, false, 10);
80 : :
81 : : // Longest possible string
82 : 1 : check_read("-32768-01-01-14:00",
83 : : EXESS_SUCCESS,
84 : : -32768,
85 : : 1,
86 : : 1,
87 : : -14,
88 : : 0,
89 : : true,
90 : : EXESS_MAX_DATE_LENGTH);
91 : :
92 : : // Limits
93 : 1 : check_read("-32768-01-01", EXESS_SUCCESS, -32768, 1, 1, 0, 0, false, 12);
94 : 1 : check_read("32767-01-01", EXESS_SUCCESS, 32767, 1, 1, 0, 0, false, 11);
95 : :
96 : : // Out of range years
97 : 1 : check_read("-32769-01-01", EXESS_OUT_OF_RANGE, 0, 0, 0, 0, 0, false, 6);
98 : 1 : check_read("32768-01-01", EXESS_OUT_OF_RANGE, 0, 0, 0, 0, 0, false, 5);
99 : :
100 : : // Out of range months
101 : 1 : check_read("2001-00-26", EXESS_OUT_OF_RANGE, 2001, 0, 0, 0, 0, false, 7);
102 : 1 : check_read("2001-13-26", EXESS_OUT_OF_RANGE, 2001, 13, 0, 0, 0, false, 7);
103 : 1 : check_read("2001-10-00", EXESS_OUT_OF_RANGE, 2001, 10, 0, 0, 0, false, 10);
104 : 1 : check_read("2001-10-32", EXESS_OUT_OF_RANGE, 2001, 10, 32, 0, 0, false, 10);
105 : :
106 : : // Out of range days
107 : 1 : check_read("2001-01-32", EXESS_OUT_OF_RANGE, 2001, 1, 32, 0, 0, false, 10);
108 : 1 : check_read("2001-02-29", EXESS_OUT_OF_RANGE, 2001, 2, 29, 0, 0, false, 10);
109 : 1 : check_read("2001-03-32", EXESS_OUT_OF_RANGE, 2001, 3, 32, 0, 0, false, 10);
110 : 1 : check_read("2001-04-31", EXESS_OUT_OF_RANGE, 2001, 4, 31, 0, 0, false, 10);
111 : 1 : check_read("2001-05-32", EXESS_OUT_OF_RANGE, 2001, 5, 32, 0, 0, false, 10);
112 : 1 : check_read("2001-06-31", EXESS_OUT_OF_RANGE, 2001, 6, 31, 0, 0, false, 10);
113 : 1 : check_read("2001-07-32", EXESS_OUT_OF_RANGE, 2001, 7, 32, 0, 0, false, 10);
114 : 1 : check_read("2001-08-32", EXESS_OUT_OF_RANGE, 2001, 8, 32, 0, 0, false, 10);
115 : 1 : check_read("2001-09-31", EXESS_OUT_OF_RANGE, 2001, 9, 31, 0, 0, false, 10);
116 : 1 : check_read("2001-10-32", EXESS_OUT_OF_RANGE, 2001, 10, 32, 0, 0, false, 10);
117 : 1 : check_read("2001-11-31", EXESS_OUT_OF_RANGE, 2001, 11, 31, 0, 0, false, 10);
118 : 1 : check_read("2001-12-32", EXESS_OUT_OF_RANGE, 2001, 12, 32, 0, 0, false, 10);
119 : :
120 : : // Trailing garbage
121 : 1 : check_read("2004-04-12A", EXESS_SUCCESS, 2004, 4, 12, 0, 0, false, 10);
122 : :
123 : : // Garbage
124 : 1 : check_read("f", EXESS_EXPECTED_DIGIT, 0, 0, 0, 0, 0, false, 0);
125 : 1 : check_read("99-04-12", EXESS_EXPECTED_DIGIT, 99, 0, 0, 0, 0, false, 2);
126 : 1 : check_read("2004-4-2", EXESS_EXPECTED_DIGIT, 2004, 4, 0, 0, 0, false, 6);
127 : 1 : check_read("2004/04/02", EXESS_EXPECTED_DASH, 2004, 0, 0, 0, 0, false, 4);
128 : 1 : check_read("04-12-2004", EXESS_EXPECTED_DIGIT, 4, 0, 0, 0, 0, false, 2);
129 : 1 : check_read("2001-10", EXESS_EXPECTED_DASH, 2001, 10, 0, 0, 0, false, 7);
130 : 1 : check_read("01-10-26", EXESS_EXPECTED_DIGIT, 1, 0, 0, 0, 0, false, 2);
131 : 1 : }
132 : :
133 : : static void
134 : 17 : check_write(const ExessDate value,
135 : : const ExessStatus expected_status,
136 : : const size_t buf_size,
137 : : const char* const expected_string)
138 : : {
139 : 17 : char buf[EXESS_MAX_DATE_LENGTH + 1] = {
140 : : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
141 : :
142 [ - + ]: 17 : assert(buf_size <= sizeof(buf));
143 : :
144 : 17 : const ExessResult r = exess_write_date(value, buf_size, buf);
145 [ - + ]: 17 : assert(r.status == expected_status);
146 [ + + ]: 17 : if (!r.status) {
147 [ - + ]: 7 : assert(!strcmp(buf, expected_string));
148 [ - + ]: 7 : assert(r.count == strlen(buf));
149 [ - + ]: 7 : assert(exess_write_date(value, 0, NULL).count == r.count);
150 : : }
151 : 17 : }
152 : :
153 : : static void
154 : 1 : test_write_date(void)
155 : : {
156 : 1 : check_write(nozone, EXESS_SUCCESS, 11, "2001-01-02");
157 : 1 : check_write(utc, EXESS_SUCCESS, 12, "2002-02-03Z");
158 : 1 : check_write(zoned, EXESS_SUCCESS, 17, "2003-03-04+11:30");
159 : 1 : check_write(early, EXESS_SUCCESS, 17, "0099-03-04+11:30");
160 : 1 : check_write(future, EXESS_SUCCESS, 18, "12345-03-04+11:30");
161 : 1 : check_write(lowest, EXESS_SUCCESS, 19, "-32768-01-01-14:00");
162 : 1 : check_write(highest, EXESS_SUCCESS, 18, "32767-01-01+14:00");
163 : :
164 : 1 : check_write(garbage1, EXESS_BAD_VALUE, 14, "");
165 : 1 : check_write(garbage2, EXESS_BAD_VALUE, 14, "");
166 : 1 : check_write(garbage3, EXESS_BAD_VALUE, 14, "");
167 : 1 : check_write(garbage4, EXESS_BAD_VALUE, 14, "");
168 : :
169 : 1 : check_write(nozone, EXESS_NO_SPACE, 10, "");
170 : 1 : check_write(future, EXESS_NO_SPACE, 5, "");
171 : 1 : check_write(lowest, EXESS_NO_SPACE, 5, "");
172 : 1 : check_write(highest, EXESS_NO_SPACE, 5, "");
173 : 1 : check_write(utc, EXESS_NO_SPACE, 11, "");
174 : 1 : check_write(zoned, EXESS_NO_SPACE, 16, "");
175 : :
176 : : // Check that nothing is written when there isn't enough space
177 : 1 : char c = 42;
178 : 1 : const ExessResult r = exess_write_date(nozone, 0, &c);
179 [ - + ]: 1 : assert(c == 42);
180 [ - + ]: 1 : assert(r.status == EXESS_NO_SPACE);
181 [ - + ]: 1 : assert(r.count == 10);
182 : 1 : }
183 : :
184 : : int
185 : 1 : main(void)
186 : : {
187 : 1 : test_read_date();
188 : 1 : test_write_date();
189 : :
190 : 1 : return 0;
191 : : }
|