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 "int_test_utils.h"
8 : : #include "integer_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 <math.h>
16 : : #include <stdint.h>
17 : : #include <stdio.h>
18 : : #include <string.h>
19 : :
20 : : #define VALUE_SIZE 28U
21 : : #define STRING_SIZE 28U
22 : :
23 : : static void
24 : 24 : check_read(const char* const string,
25 : : const ExessStatus expected_status,
26 : : const int64_t expected_number,
27 : : const size_t expected_count)
28 : : {
29 : 24 : EXESS_ALIGN uint8_t value[VALUE_SIZE] = {0};
30 : 24 : EXESS_ALIGN uint8_t expected_value[VALUE_SIZE] = {0};
31 : 24 : set_integer_value(sizeof(expected_value), expected_value, expected_number);
32 : :
33 : : const ExessVariableResult r =
34 : 24 : exess_read_integer(string, sizeof(value), value);
35 : :
36 [ - + ]: 24 : assert(r.status == expected_status);
37 [ - + ]: 24 : assert(r.read_count == expected_count);
38 [ + + - + ]: 24 : assert(expected_status || !memcmp(expected_value, value, r.write_count));
39 : 24 : }
40 : :
41 : : static void
42 : 1 : test_read_integer(void)
43 : : {
44 : : // No value
45 : 1 : check_read("", EXESS_EXPECTED_DIGIT, 0, 0);
46 : 1 : check_read("\t\n\r ", EXESS_EXPECTED_DIGIT, 0, 4);
47 : :
48 : : // Basic values
49 : 1 : check_read("-34000", EXESS_SUCCESS, -34000, 6);
50 : 1 : check_read("-1200", EXESS_SUCCESS, -1200, 5);
51 : 1 : check_read("0", EXESS_SUCCESS, 0, 1);
52 : 1 : check_read("1", EXESS_SUCCESS, 1, 1);
53 : 1 : check_read("1200", EXESS_SUCCESS, 1200, 4);
54 : 1 : check_read("34000", EXESS_SUCCESS, 34000, 5);
55 : :
56 : : // Non-canonical form
57 : 1 : check_read("\t\n\r 42 ", EXESS_SUCCESS, 42, 6);
58 : 1 : check_read("+0", EXESS_SUCCESS, 0, 2);
59 : 1 : check_read("-0", EXESS_SUCCESS, 0, 2);
60 : 1 : check_read("012", EXESS_SUCCESS, 12, 3);
61 : 1 : check_read("-034", EXESS_SUCCESS, -34, 4);
62 : 1 : check_read("+56", EXESS_SUCCESS, 56, 3);
63 : 1 : check_read("+0078", EXESS_SUCCESS, 78, 5);
64 : :
65 : : // Trailing dot
66 : 1 : check_read("0.1", EXESS_SUCCESS, 0, 1);
67 : 1 : check_read("1.2", EXESS_SUCCESS, 1, 1);
68 : 1 : check_read("2.3", EXESS_SUCCESS, 2, 1);
69 : :
70 : : // Garbage
71 : 1 : check_read("NaN", EXESS_EXPECTED_DIGIT, 0, 0);
72 : 1 : check_read("INF", EXESS_EXPECTED_DIGIT, 0, 0);
73 : 1 : check_read("-INF", EXESS_EXPECTED_DIGIT, 0, 1);
74 : 1 : check_read("true", EXESS_EXPECTED_DIGIT, 0, 0);
75 : 1 : check_read("+true", EXESS_EXPECTED_DIGIT, 0, 1);
76 : 1 : check_read("-false", EXESS_EXPECTED_DIGIT, 0, 1);
77 : :
78 : : // Insufficient space for any value
79 : 1 : EXESS_ALIGN char buf[6] = {0};
80 [ - + ]: 1 : assert(exess_read_integer("1", 4, buf).status == EXESS_NO_SPACE);
81 : :
82 : : // Insufficient space for this value
83 [ - + ]: 1 : assert(exess_read_integer("1234567", sizeof(buf), buf).status ==
84 : : EXESS_OUT_OF_RANGE);
85 : 1 : }
86 : :
87 : : /// Check that `str` is a canonical xsd:integer string
88 : : static void
89 : 7 : check_canonical(const char* const str)
90 : : {
91 [ + + - + ]: 7 : assert(str[0] == '-' || is_digit(str[0]));
92 : :
93 : 7 : const int first_digit = str[0] == '-' ? 1 : 0;
94 : :
95 [ + + ]: 28 : for (const char* s = str + first_digit; *s; ++s) {
96 [ + - - + ]: 21 : assert(*s == '.' || is_digit(*s));
97 : : }
98 : 7 : }
99 : :
100 : : static void
101 : 7 : check_write_integer(const int64_t number,
102 : : const ExessStatus expected_status,
103 : : const size_t buf_size,
104 : : const char* const expected_string)
105 : : {
106 : 7 : EXESS_ALIGN char value[VALUE_SIZE] = {0};
107 : 7 : set_integer_value(sizeof(value), value, number);
108 : :
109 : 7 : char buf[STRING_SIZE] = {42};
110 : 7 : init_out_buf(sizeof(buf), buf);
111 : :
112 [ - + ]: 7 : assert(buf_size <= sizeof(buf));
113 : :
114 : : const ExessResult r =
115 : 7 : exess_write_integer(sizeof(value), value, buf_size, buf);
116 [ - + ]: 7 : assert(r.status == expected_status);
117 [ + + ]: 7 : if (!r.status) {
118 [ - + ]: 6 : assert(expected_string);
119 [ - + ]: 6 : assert(r.count == strlen(buf));
120 [ - + ]: 6 : assert(buf[0]);
121 [ - + ]: 6 : assert(!strcmp(buf, expected_string));
122 : 6 : check_canonical(buf);
123 : :
124 [ - + ]: 6 : assert(exess_write_integer(sizeof(value), value, 0, NULL).count == r.count);
125 : :
126 [ + + ]: 34 : for (size_t space = 0; space < buf_size; ++space) {
127 : 28 : buf[0] = 1;
128 [ - + ]: 28 : assert(exess_write_integer(sizeof(value), value, space, buf).status ==
129 : : EXESS_NO_SPACE);
130 [ + + - + ]: 28 : assert(!space || !buf[0]);
131 : : }
132 : : } else {
133 [ - + ]: 1 : assert(!buf[0]);
134 [ - + ]: 1 : assert(exess_write_integer(sizeof(value), value, 0, NULL).count == r.count);
135 : : }
136 : 7 : }
137 : :
138 : : static void
139 : 1 : test_write_integer(void)
140 : : {
141 : : // Numbers
142 : 1 : check_write_integer(-34000, EXESS_SUCCESS, 7, "-34000");
143 : 1 : check_write_integer(-1200, EXESS_SUCCESS, 6, "-1200");
144 : 1 : check_write_integer(0, EXESS_SUCCESS, 2, "0");
145 : 1 : check_write_integer(1, EXESS_SUCCESS, 2, "1");
146 : 1 : check_write_integer(1200, EXESS_SUCCESS, 5, "1200");
147 : 1 : check_write_integer(34000, EXESS_SUCCESS, 6, "34000");
148 : 1 : check_write_integer(34000, EXESS_NO_SPACE, 5, "34000");
149 : :
150 : : // Bad value size
151 : 1 : EXESS_ALIGN char value[VALUE_SIZE] = {0};
152 : 1 : char buf[STRING_SIZE] = {0};
153 : 1 : set_integer_value(sizeof(value), value, 2);
154 [ - + ]: 1 : assert(exess_write_integer(1, value, sizeof(buf), buf).status ==
155 : : EXESS_BAD_VALUE);
156 : 1 : }
157 : :
158 : : static void
159 : 6 : check_write_decimal(const double number,
160 : : const ExessStatus expected_status,
161 : : const size_t buf_size,
162 : : const char* const expected_string)
163 : : {
164 : 6 : EXESS_ALIGN char value[VALUE_SIZE] = {0};
165 : 6 : set_decimal_value(sizeof(value), value, number);
166 : :
167 : 6 : char buf[STRING_SIZE] = {42};
168 : 6 : init_out_buf(sizeof(buf), buf);
169 : :
170 [ - + ]: 6 : assert(buf_size <= sizeof(buf));
171 : :
172 : : const ExessResult r =
173 : 6 : exess_write_integer(sizeof(value), value, buf_size, buf);
174 [ - + ]: 6 : assert(r.status == expected_status);
175 [ + + ]: 6 : if (!r.status) {
176 [ - + ]: 1 : assert(expected_string);
177 [ - + ]: 1 : assert(r.count == strlen(buf));
178 [ - + ]: 1 : assert(buf[0]);
179 [ - + ]: 1 : assert(!strcmp(buf, expected_string));
180 : 1 : check_canonical(buf);
181 : :
182 [ - + ]: 1 : assert(exess_write_integer(sizeof(value), value, 0, NULL).count == r.count);
183 : :
184 [ + + ]: 3 : for (size_t space = 0; space < buf_size; ++space) {
185 : 2 : buf[0] = 1;
186 [ - + ]: 2 : assert(exess_write_integer(sizeof(value), value, space, buf).status ==
187 : : EXESS_NO_SPACE);
188 [ + + - + ]: 2 : assert(!space || !buf[0]);
189 : : }
190 : : } else {
191 [ - + ]: 5 : assert(!buf[0]);
192 [ - + ]: 5 : assert(exess_write_integer(sizeof(value), value, 0, NULL).count == r.count);
193 : : }
194 : 6 : }
195 : :
196 : : static void
197 : 1 : test_write_decimal(void)
198 : : {
199 : 1 : check_write_decimal((double)-INFINITY, EXESS_BAD_VALUE, 1, "");
200 : 1 : check_write_decimal((double)INFINITY, EXESS_BAD_VALUE, 1, "");
201 : 1 : check_write_decimal((double)NAN, EXESS_BAD_VALUE, 1, "");
202 : 1 : check_write_decimal(0.01, EXESS_BAD_VALUE, 1, "");
203 : 1 : check_write_decimal(1.0, EXESS_SUCCESS, 2, "1");
204 : 1 : check_write_decimal(1.5, EXESS_BAD_VALUE, 1, "");
205 : 1 : }
206 : :
207 : : static void
208 : 4099 : check_round_trip(const int64_t number)
209 : : {
210 : 4099 : EXESS_ALIGN uint8_t value[VALUE_SIZE] = {0};
211 : 4099 : EXESS_ALIGN uint8_t parsed_value[VALUE_SIZE] = {0};
212 : 4099 : char buf[STRING_SIZE] = {0};
213 : :
214 : 4099 : set_integer_value(sizeof(value), value, number);
215 : 4099 : init_out_buf(sizeof(buf), buf);
216 : :
217 : : const ExessResult w =
218 : 4099 : exess_write_integer(sizeof(value), value, sizeof(buf), buf);
219 : :
220 : : const ExessVariableResult r =
221 : 4099 : exess_read_integer(buf, sizeof(parsed_value), parsed_value);
222 : :
223 [ - + ]: 4099 : assert(!w.status);
224 [ - + ]: 4099 : assert(!r.status);
225 [ - + ]: 4099 : assert(r.read_count == w.count);
226 [ - + ]: 4099 : assert(!memcmp(parsed_value, value, r.write_count));
227 : 4099 : }
228 : :
229 : : static void
230 : 1 : test_round_trip(const ExessNumTestOptions opts)
231 : : {
232 : 1 : check_round_trip(-1);
233 : 1 : check_round_trip(0);
234 : 1 : check_round_trip(1);
235 : :
236 : 1 : fprintf(stderr, "Testing xsd:integer randomly with seed %u\n", opts.seed);
237 : :
238 : 1 : uint64_t state = opts.seed;
239 [ + + ]: 4097 : for (uint64_t i = 0; i < opts.n_tests; ++i) {
240 : 4096 : state = lcg64(state);
241 : :
242 : 4096 : const uint64_t clamped = (state < INT64_MAX) ? state : INT64_MAX;
243 : 4096 : const int64_t value = (int64_t)clamped - (INT64_MAX / 2U);
244 : :
245 : 4096 : check_round_trip(value);
246 : 4096 : print_num_test_progress(i, opts.n_tests);
247 : : }
248 : 1 : }
249 : :
250 : : int
251 : 1 : main(int argc, char** argv)
252 : : {
253 : : const ExessNumTestOptions opts =
254 : 1 : parse_num_test_options(argc, argv, 4096U, 0U, UINT32_MAX);
255 : :
256 [ + - ]: 1 : if (!opts.error) {
257 : 1 : test_read_integer();
258 : 1 : test_write_integer();
259 : 1 : test_write_decimal();
260 : 1 : test_round_trip(opts);
261 : : }
262 : :
263 : 1 : return (int)opts.error;
264 : : }
|