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 ExessTimezone missing = {EXESS_TIMEZONE_LOCAL};
16 : : static const ExessTimezone utc = INIT_ZONE(0, 0);
17 : : static const ExessTimezone plus = INIT_ZONE(11, 30);
18 : : static const ExessTimezone minus = INIT_ZONE(-11, -30);
19 : : static const ExessTimezone slight = INIT_ZONE(0, 30);
20 : : static const ExessTimezone lowest = INIT_ZONE(-14, 0);
21 : : static const ExessTimezone highest = INIT_ZONE(14, 0);
22 : : static const ExessTimezone garbage1 = INIT_ZONE(-14, -15);
23 : : static const ExessTimezone garbage2 = INIT_ZONE(14, 15);
24 : : static const ExessTimezone garbage3 = INIT_ZONE(-15, 0);
25 : : static const ExessTimezone garbage4 = INIT_ZONE(15, 0);
26 : :
27 : : static void
28 : 1 : test_construct(void)
29 : : {
30 : : // Out of bounds
31 [ - + ]: 1 : assert(exess_timezone(-15, 00) == EXESS_TIMEZONE_LOCAL);
32 [ - + ]: 1 : assert(exess_timezone(15, 00) == EXESS_TIMEZONE_LOCAL);
33 : :
34 : : // Extremes
35 [ - + ]: 1 : assert(exess_timezone(-14, -45) == -59);
36 [ - + ]: 1 : assert(exess_timezone(14, 45) == 59);
37 : :
38 : : // Bad minutes
39 [ - + ]: 1 : assert(exess_timezone(12, 20) == EXESS_TIMEZONE_LOCAL);
40 [ - + ]: 1 : assert(exess_timezone(-12, -20) == EXESS_TIMEZONE_LOCAL);
41 : :
42 : : // Bad signs
43 [ - + ]: 1 : assert(exess_timezone(12, -30) == EXESS_TIMEZONE_LOCAL);
44 [ - + ]: 1 : assert(exess_timezone(-12, 30) == EXESS_TIMEZONE_LOCAL);
45 : :
46 : : // All valid negative cases
47 : 1 : ExessTimezone z = -59;
48 [ + + ]: 16 : for (int8_t h = -14; h < 1; ++h) {
49 [ + + ]: 75 : for (int8_t m = -45; m < 15; m = (int8_t)(m + 15)) {
50 [ - + ]: 60 : assert(exess_timezone(h, m) == z);
51 : 60 : ++z;
52 : : }
53 : : }
54 : :
55 [ - + ]: 1 : assert(z == 1);
56 : :
57 : : // All valid non-negative cases
58 : 1 : z = 0;
59 [ + + ]: 16 : for (int8_t h = 0; h < 15; ++h) {
60 [ + + ]: 75 : for (int8_t m = 0; m < 60; m = (int8_t)(m + 15)) {
61 [ - + ]: 60 : assert(exess_timezone(h, m) == z);
62 : 60 : ++z;
63 : : }
64 : : }
65 : 1 : }
66 : :
67 : : static void
68 : 26 : check_read(const char* const string,
69 : : const ExessStatus expected_status,
70 : : const int8_t expected_hour,
71 : : const int8_t expected_minute,
72 : : const bool expected_is_present,
73 : : const size_t expected_count)
74 : : {
75 : : // The timezone interface is not public, so we test it via time
76 : 26 : char time_string[] = "12:00:00XXXXXX";
77 : 26 : strncpy(time_string + 8, string, sizeof(time_string) - 9);
78 : :
79 : 26 : ExessTime value = {EXESS_TIMEZONE_LOCAL, 0, 0, 0, 0};
80 : 26 : const ExessResult r = exess_read_time(time_string, &value);
81 : :
82 [ - + ]: 26 : assert(r.status == expected_status);
83 [ - + ]: 26 : assert(r.count == 8 + expected_count);
84 [ + + - + : 26 : assert((!expected_is_present && value.zone == EXESS_TIMEZONE_LOCAL) ||
- + ]
85 : : value.zone == (4 * expected_hour) + (expected_minute / 15));
86 : 26 : }
87 : :
88 : : static void
89 : 1 : test_read_timezone(void)
90 : : {
91 : : // Basic values
92 : 1 : check_read("Z", EXESS_SUCCESS, 0, 0, true, 1);
93 : 1 : check_read("-05:00", EXESS_SUCCESS, -5, 0, true, 6);
94 : 1 : check_read("+02:00", EXESS_SUCCESS, 2, 0, true, 6);
95 : 1 : check_read("+00:00", EXESS_SUCCESS, 0, 0, true, 6);
96 : 1 : check_read("-00:00", EXESS_SUCCESS, 0, 0, true, 6);
97 : :
98 : : // Limits
99 : 1 : check_read("-14:00", EXESS_SUCCESS, -14, 0, true, 6);
100 : 1 : check_read("+14:00", EXESS_SUCCESS, 14, 0, true, 6);
101 : 1 : check_read("-13:45", EXESS_SUCCESS, -13, -45, true, 6);
102 : 1 : check_read("+13:45", EXESS_SUCCESS, 13, 45, true, 6);
103 : :
104 : : // Out of range
105 : 1 : check_read("-14:15", EXESS_OUT_OF_RANGE, 0, 0, false, 6);
106 : 1 : check_read("+14:15", EXESS_OUT_OF_RANGE, 0, 0, false, 6);
107 : 1 : check_read("-15:00", EXESS_OUT_OF_RANGE, 0, 0, false, 3);
108 : 1 : check_read("+15:00", EXESS_OUT_OF_RANGE, 0, 0, false, 3);
109 : 1 : check_read("-13:60", EXESS_OUT_OF_RANGE, 0, 0, false, 6);
110 : 1 : check_read("+13:60", EXESS_OUT_OF_RANGE, 0, 0, false, 6);
111 : :
112 : : // Trailing garbage
113 : 1 : check_read("05:00", EXESS_SUCCESS, 0, 0, false, 0);
114 : :
115 : : // Garbage
116 : 1 : check_read("+05:01", EXESS_UNSUPPORTED, 0, 0, false, 6);
117 : 1 : check_read("+5:00", EXESS_EXPECTED_DIGIT, 0, 0, false, 2);
118 : 1 : check_read("+5:0", EXESS_EXPECTED_DIGIT, 0, 0, false, 2);
119 : 1 : check_read("+5:", EXESS_EXPECTED_DIGIT, 0, 0, false, 2);
120 : 1 : check_read("+:0", EXESS_EXPECTED_DIGIT, 0, 0, false, 1);
121 : 1 : check_read("+A5:00", EXESS_EXPECTED_DIGIT, 0, 0, false, 1);
122 : 1 : check_read("+0A:00", EXESS_EXPECTED_DIGIT, 0, 0, false, 2);
123 : 1 : check_read("+05A00", EXESS_EXPECTED_COLON, 0, 0, false, 3);
124 : 1 : check_read("+05:A0", EXESS_EXPECTED_DIGIT, 0, 0, false, 4);
125 : 1 : check_read("+05:0A", EXESS_EXPECTED_DIGIT, 0, 0, false, 5);
126 : 1 : }
127 : :
128 : : static void
129 : 13 : check_write(const ExessTimezone value,
130 : : const ExessStatus expected_status,
131 : : const size_t buf_size,
132 : : const char* const expected_string)
133 : : {
134 : : // The timezone interface is not public, so we test it via time
135 : 13 : char buf[EXESS_MAX_TIME_LENGTH + 1] = {1, 2, 3, 4, 5, 6, 7, 8, 9,
136 : : 10, 11, 12, 13, 14, 15, 16, 17, 18,
137 : : 19, 20, 21, 22, 23, 24, 25};
138 : :
139 [ - + ]: 13 : assert(buf_size <= sizeof(buf) - 8);
140 : :
141 : 13 : const ExessTime time = {value, 12, 0, 0, 0};
142 : 13 : const ExessResult r = exess_write_time(time, 8 + buf_size, buf);
143 [ - + ]: 13 : assert(r.status == expected_status);
144 [ + + ]: 13 : if (!r.status) {
145 [ - + ]: 7 : assert((r.count == strlen(buf)));
146 [ - + - - : 7 : assert((!buf[0] && !expected_string[0]) ||
- + ]
147 : : !strcmp(buf + 8, expected_string));
148 [ - + ]: 7 : assert(exess_write_time(time, 0, NULL).count == r.count);
149 : : }
150 : 13 : }
151 : :
152 : : static void
153 : 1 : test_write_timezone(void)
154 : : {
155 : 1 : check_write(missing, EXESS_SUCCESS, 1, "");
156 : 1 : check_write(utc, EXESS_SUCCESS, 2, "Z");
157 : 1 : check_write(plus, EXESS_SUCCESS, 7, "+11:30");
158 : 1 : check_write(minus, EXESS_SUCCESS, 7, "-11:30");
159 : 1 : check_write(slight, EXESS_SUCCESS, 7, "+00:30");
160 : 1 : check_write(lowest, EXESS_SUCCESS, 7, "-14:00");
161 : 1 : check_write(highest, EXESS_SUCCESS, 7, "+14:00");
162 : :
163 : 1 : check_write(garbage1, EXESS_BAD_VALUE, 7, "");
164 : 1 : check_write(garbage2, EXESS_BAD_VALUE, 7, "");
165 : 1 : check_write(garbage3, EXESS_BAD_VALUE, 7, "");
166 : 1 : check_write(garbage4, EXESS_BAD_VALUE, 7, "");
167 : :
168 : 1 : check_write(utc, EXESS_NO_SPACE, 1, "");
169 : 1 : check_write(plus, EXESS_NO_SPACE, 6, "");
170 : 1 : }
171 : :
172 : : static void
173 : 106 : check_round_trip(const ExessTimezone value)
174 : : {
175 : 106 : char buf[EXESS_MAX_TIME_LENGTH + 1] = {1, 2, 3, 4, 5, 6, 7, 8, 9,
176 : : 10, 11, 12, 13, 14, 15, 16, 17, 18,
177 : : 19, 20, 21, 22, 23, 24, 25};
178 : :
179 : 106 : const ExessTime time = {value, 12, 0, 0, 0};
180 : 106 : ExessTime parsed_time = {EXESS_TIMEZONE_LOCAL, 0, 0, 0, 0};
181 : :
182 [ - + ]: 106 : assert(!exess_write_time(time, sizeof(buf), buf).status);
183 [ - + ]: 106 : assert(!exess_read_time(buf, &parsed_time).status);
184 [ - + ]: 106 : assert(!memcmp(&parsed_time.zone, &value, sizeof(ExessTimezone)));
185 : 106 : }
186 : :
187 : : static void
188 : 1 : test_round_trip(void)
189 : : {
190 : 1 : check_round_trip(lowest);
191 : 1 : check_round_trip(highest);
192 : :
193 [ + + ]: 27 : for (int8_t h = -13; h < 13; ++h) {
194 [ + + ]: 130 : for (int8_t q = 0; q < 4; ++q) {
195 : 104 : const ExessTimezone value = {(int8_t)((4 * h) + q)};
196 : :
197 : 104 : check_round_trip(value);
198 : : }
199 : : }
200 : 1 : }
201 : :
202 : : int
203 : 1 : main(void)
204 : : {
205 : 1 : test_construct();
206 : 1 : test_read_timezone();
207 : 1 : test_write_timezone();
208 : 1 : test_round_trip();
209 : :
210 : 1 : return 0;
211 : : }
|