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 "decimal_test_utils.h"
7 : : #include "double_test_utils.h"
8 : : #include "int_test_utils.h"
9 : : #include "num_test_utils.h"
10 : : #include "write_test_utils.h"
11 : :
12 : : #include <exess/exess.h>
13 : :
14 : : #include <assert.h>
15 : : #include <float.h>
16 : : #include <math.h>
17 : : #include <stdint.h>
18 : : #include <stdio.h>
19 : : #include <string.h>
20 : :
21 : : #define VALUE_SIZE 22U
22 : : #define STRING_SIZE 327U
23 : :
24 : : static void
25 : 31 : check_read(const char* const string,
26 : : const ExessStatus expected_status,
27 : : const double expected_number,
28 : : const size_t expected_count)
29 : : {
30 : 31 : EXESS_ALIGN uint8_t value[VALUE_SIZE] = {0};
31 : 31 : EXESS_ALIGN uint8_t expected_value[VALUE_SIZE] = {0};
32 : :
33 : : const size_t expected_value_size =
34 : 31 : set_decimal_value(sizeof(expected_value), expected_value, expected_number);
35 : :
36 : : const ExessVariableResult r =
37 : 31 : exess_read_decimal(string, sizeof(value), value);
38 : :
39 [ - + ]: 31 : assert(r.status == expected_status);
40 [ - + ]: 31 : assert(r.read_count == expected_count);
41 [ - + ]: 31 : assert(r.write_count == expected_value_size);
42 [ - + ]: 31 : assert(!memcmp(expected_value, value, r.write_count));
43 : 31 : }
44 : :
45 : : static void
46 : 1 : test_read_decimal(void)
47 : : {
48 : : // No value
49 : 1 : check_read("", EXESS_EXPECTED_DIGIT, (double)NAN, 0);
50 : 1 : check_read("\t\n\r ", EXESS_EXPECTED_DIGIT, (double)NAN, 4);
51 : :
52 : : // Basic values
53 : 1 : check_read("-100.25", EXESS_SUCCESS, -100.25, 7);
54 : 1 : check_read("1.2", EXESS_SUCCESS, 1.2, 3);
55 : 1 : check_read("0.01", EXESS_SUCCESS, 0.01, 4);
56 : 1 : check_read("10.0", EXESS_SUCCESS, 10.0, 4);
57 : :
58 : : // Non-canonical form
59 : 1 : check_read("\t\n\r 42.24 ", EXESS_SUCCESS, 42.24, 9);
60 : 1 : check_read("12.", EXESS_SUCCESS, 12., 3);
61 : 1 : check_read(".34", EXESS_SUCCESS, 0.34, 3);
62 : 1 : check_read("+.56", EXESS_SUCCESS, 0.56, 4);
63 : 1 : check_read("-.78", EXESS_SUCCESS, -0.78, 4);
64 : 1 : check_read("+00.10", EXESS_SUCCESS, 0.1, 6);
65 : :
66 : : // Double limits
67 : :
68 : 1 : check_read(
69 : : "0."
70 : : "00000000000000000000000000000000000000000000000000000000000000000000000000"
71 : : "00000000000000000000000000000000000000000000000000000000000000000000000000"
72 : : "00000000000000000000000000000000000000000000000000000000000000000000000000"
73 : : "00000000000000000000000000000000000000000000000000000000000000000000000000"
74 : : "0000000000022250738585072014",
75 : : EXESS_SUCCESS,
76 : : DBL_MIN,
77 : : 326);
78 : :
79 : 1 : check_read("1797693134862315700000000000000000000000000000000000000000000000"
80 : : "0000000000000000000000000000000000000000000000000000000000000000"
81 : : "0000000000000000000000000000000000000000000000000000000000000000"
82 : : "0000000000000000000000000000000000000000000000000000000000000000"
83 : : "00000000000000000000000000000000000000000000000000000.0",
84 : : EXESS_SUCCESS,
85 : : DBL_MAX,
86 : : 311);
87 : :
88 : : // Superfluous digits
89 : 1 : check_read("12345678901234567890", EXESS_LOSS, 12345678901234568000.0, 20);
90 : 1 : check_read("1.2345678901234567890", EXESS_LOSS, 1.2345678901234568, 21);
91 : :
92 : : // Special values
93 : 1 : check_read("-0.0E0", EXESS_SUCCESS, -0.0, 4);
94 : 1 : check_read("0.0E0", EXESS_SUCCESS, 0.0, 3);
95 : 1 : check_read("+0.0E0", EXESS_SUCCESS, 0.0, 4);
96 : :
97 : : // No exponent
98 : 1 : check_read("1", EXESS_SUCCESS, 1.0, 1);
99 : 1 : check_read("2.3", EXESS_SUCCESS, 2.3, 3);
100 : 1 : check_read("-4.5", EXESS_SUCCESS, -4.5, 4);
101 : :
102 : : // Trailing dot
103 : 1 : check_read("0.0.1", EXESS_SUCCESS, 0.0, 3);
104 : 1 : check_read("0.1.2", EXESS_SUCCESS, 0.1, 3);
105 : 1 : check_read("1.2.3", EXESS_SUCCESS, 1.2, 3);
106 : :
107 : : // Garbage
108 : 1 : check_read("NaN", EXESS_EXPECTED_DIGIT, (double)NAN, 0);
109 : 1 : check_read("INF", EXESS_EXPECTED_DIGIT, (double)NAN, 0);
110 : 1 : check_read("-INF", EXESS_EXPECTED_DIGIT, (double)NAN, 1);
111 : 1 : check_read("true", EXESS_EXPECTED_DIGIT, (double)NAN, 0);
112 : 1 : check_read("+true", EXESS_EXPECTED_DIGIT, (double)NAN, 1);
113 : 1 : check_read("-false", EXESS_EXPECTED_DIGIT, (double)NAN, 1);
114 : :
115 : : // Insufficient output space
116 : 1 : EXESS_ALIGN char value[VALUE_SIZE] = {0};
117 [ + + ]: 6 : for (size_t i = 0U; i < 5; ++i) {
118 [ - + ]: 5 : assert(exess_read_decimal("1", i, value).status == EXESS_NO_SPACE);
119 : : }
120 : 1 : }
121 : :
122 : : /// Check that `str` is a canonical xsd:decimal string
123 : : static void
124 : 6 : check_canonical(const char* const str)
125 : : {
126 [ - + ]: 6 : assert(strlen(str) > 2); // Shortest possible is something like 1.2
127 [ + + - + ]: 6 : assert(str[0] == '-' || is_digit(str[0]));
128 : :
129 : 6 : const int first_digit = str[0] == '-' ? 1 : 0;
130 : :
131 [ + + ]: 660 : for (const char* s = str + first_digit; *s; ++s) {
132 [ + + - + ]: 654 : assert(*s == '.' || is_digit(*s));
133 : : }
134 : 6 : }
135 : :
136 : : static void
137 : 9 : check_write(const double number,
138 : : const ExessStatus expected_status,
139 : : const size_t buf_size,
140 : : const char* const expected_string)
141 : : {
142 : 9 : EXESS_ALIGN char value[VALUE_SIZE] = {0};
143 : 9 : set_decimal_value(sizeof(value), value, number);
144 : :
145 : 9 : char buf[STRING_SIZE] = {42};
146 : 9 : init_out_buf(sizeof(buf), buf);
147 : :
148 [ - + ]: 9 : assert(buf_size <= sizeof(buf));
149 : :
150 : : const ExessResult r =
151 : 9 : exess_write_decimal(sizeof(value), value, buf_size, buf);
152 [ - + ]: 9 : assert(r.status == expected_status);
153 [ + + ]: 9 : if (!r.status) {
154 [ - + ]: 6 : assert(expected_string);
155 [ - + ]: 6 : assert(r.count == strlen(buf));
156 [ - + ]: 6 : assert(buf[0]);
157 [ - + ]: 6 : assert(!strcmp(buf, expected_string));
158 : 6 : check_canonical(buf);
159 : :
160 [ - + ]: 6 : assert(exess_write_decimal(sizeof(value), value, 0, NULL).count == r.count);
161 : :
162 [ + + ]: 668 : for (size_t space = 0; space < buf_size; ++space) {
163 : 662 : buf[0] = 1;
164 : :
165 : : const ExessResult s =
166 : 662 : exess_write_decimal(sizeof(value), value, space, buf);
167 : :
168 [ - + ]: 662 : assert(s.status == EXESS_NO_SPACE);
169 [ - + ]: 662 : assert(s.count == r.count);
170 [ - + ]: 662 : assert(buf[0] == (space ? 0 : 1));
171 : : }
172 : : } else {
173 [ - + ]: 3 : assert(!r.count);
174 [ - + ]: 3 : assert(!buf[0]);
175 [ - + ]: 3 : assert(!exess_write_decimal(sizeof(value), value, 0, NULL).count);
176 : : }
177 : 9 : }
178 : :
179 : : static void
180 : 1 : test_write_decimal(void)
181 : : {
182 : : // Numbers
183 : 1 : check_write(-100.25, EXESS_SUCCESS, 8, "-100.25");
184 : 1 : check_write(-0.0, EXESS_SUCCESS, 5, "-0.0");
185 : 1 : check_write(0.0, EXESS_SUCCESS, 4, "0.0");
186 : 1 : check_write(10.25, EXESS_SUCCESS, 6, "10.25");
187 : :
188 : : // Limits
189 : :
190 : 1 : check_write(
191 : : DBL_MIN,
192 : : EXESS_SUCCESS,
193 : : 327,
194 : : "0."
195 : : "00000000000000000000000000000000000000000000000000000000000000000000000000"
196 : : "00000000000000000000000000000000000000000000000000000000000000000000000000"
197 : : "00000000000000000000000000000000000000000000000000000000000000000000000000"
198 : : "00000000000000000000000000000000000000000000000000000000000000000000000000"
199 : : "0000000000022250738585072014");
200 : :
201 : 1 : check_write(DBL_MAX,
202 : : EXESS_SUCCESS,
203 : : 312,
204 : : "1797693134862315700000000000000000000000000000000000000000000000"
205 : : "0000000000000000000000000000000000000000000000000000000000000000"
206 : : "0000000000000000000000000000000000000000000000000000000000000000"
207 : : "0000000000000000000000000000000000000000000000000000000000000000"
208 : : "00000000000000000000000000000000000000000000000000000.0");
209 : :
210 : : // Bad values
211 : 1 : check_write((double)NAN, EXESS_BAD_VALUE, 4, "");
212 : 1 : check_write((double)-INFINITY, EXESS_BAD_VALUE, 5, "");
213 : 1 : check_write((double)INFINITY, EXESS_BAD_VALUE, 4, "");
214 : :
215 : : // Bad value size
216 : 1 : EXESS_ALIGN char value[VALUE_SIZE] = {0};
217 : 1 : char buf[STRING_SIZE] = {0};
218 : 1 : set_decimal_value(sizeof(value), value, 2.0);
219 [ - + ]: 1 : assert(exess_write_decimal(1, value, sizeof(buf), buf).status ==
220 : : EXESS_BAD_VALUE);
221 : 1 : }
222 : :
223 : : static void
224 : 4100 : check_round_trip(const double number)
225 : : {
226 : 4100 : EXESS_ALIGN uint8_t value[VALUE_SIZE] = {0};
227 : 4100 : EXESS_ALIGN uint8_t parsed_value[VALUE_SIZE] = {0};
228 : 4100 : char buf[STRING_SIZE] = {42};
229 : :
230 : 4100 : set_decimal_value(sizeof(value), value, number);
231 : 4100 : init_out_buf(sizeof(buf), buf);
232 : :
233 : : const ExessResult w =
234 : 4100 : exess_write_decimal(sizeof(value), value, sizeof(buf), buf);
235 : :
236 : : const ExessVariableResult r =
237 : 4100 : exess_read_decimal(buf, sizeof(parsed_value), parsed_value);
238 : :
239 [ - + ]: 4100 : assert(!w.status);
240 [ - + ]: 4100 : assert(r.status <= EXESS_LOSS);
241 [ - + ]: 4100 : assert(r.read_count == w.count);
242 [ - + ]: 4100 : assert(!memcmp(parsed_value, value, r.write_count));
243 : :
244 : 4100 : double parsed_number = (double)NAN;
245 [ - + ]: 4100 : assert(!exess_coerce_value(0U,
246 : : EXESS_DECIMAL,
247 : : sizeof(parsed_value),
248 : : &parsed_value,
249 : : EXESS_DOUBLE,
250 : : sizeof(parsed_number),
251 : : &parsed_number)
252 : : .status);
253 : :
254 [ - + ]: 4100 : assert(double_matches(parsed_number, number));
255 : 4100 : }
256 : :
257 : : static void
258 : 1 : test_round_trip(const ExessNumTestOptions opts)
259 : : {
260 : 1 : check_round_trip(DBL_MIN);
261 : 1 : check_round_trip(-0.0);
262 : 1 : check_round_trip(0.0);
263 : 1 : check_round_trip(DBL_MAX);
264 : :
265 : 1 : fprintf(stderr, "Testing xsd:decimal randomly with seed %u\n", opts.seed);
266 : :
267 : 1 : uint64_t state = opts.seed;
268 [ + + ]: 4097 : for (uint64_t i = 0; i < opts.n_tests; ++i) {
269 : 4096 : state = lcg64(state);
270 : :
271 : 4096 : const double value = random_double(state);
272 : :
273 : 4096 : check_round_trip(value);
274 : 4096 : print_num_test_progress(i, opts.n_tests);
275 : : }
276 : 1 : }
277 : :
278 : : int
279 : 1 : main(int argc, char** argv)
280 : : {
281 : : const ExessNumTestOptions opts =
282 : 1 : parse_num_test_options(argc, argv, 4096U, 0U, UINT32_MAX);
283 : :
284 [ + - ]: 1 : if (!opts.error) {
285 : 1 : test_read_decimal();
286 : 1 : test_write_decimal();
287 : 1 : test_round_trip(opts);
288 : : }
289 : :
290 : 1 : return (int)opts.error;
291 : : }
|