|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
26 * POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 #include "cx/test.h" |
|
30 #include "util_allocator.h" |
|
31 |
|
32 #include "cx/printf.h" |
|
33 #include "cx/buffer.h" |
|
34 |
|
35 #define ASSERT_ZERO_TERMINATED(str) CX_TEST_ASSERTM((str).ptr[(str).length] == '\0', \ |
|
36 #str " is not zero terminated") |
|
37 |
|
38 static size_t test_printf_write_func( |
|
39 void const *src, |
|
40 size_t esize, |
|
41 size_t ecount, |
|
42 void *target |
|
43 ) { |
|
44 memcpy(target, src, esize * ecount); |
|
45 return esize * ecount; |
|
46 } |
|
47 |
|
48 CX_TEST(test_bprintf) { |
|
49 CxTestingAllocator talloc; |
|
50 cx_testing_allocator_init(&talloc); |
|
51 CxAllocator *alloc = &talloc.base; |
|
52 CX_TEST_DO { |
|
53 CxBuffer buf; |
|
54 cxBufferInit(&buf, NULL, 64, alloc, 0); |
|
55 size_t r = cx_bprintf(&buf, "This %s aged %u years in a %2XSK.", "Test", 10, 0xca); |
|
56 CX_TEST_ASSERT(r == 34); |
|
57 CX_TEST_ASSERT(buf.size == 34); |
|
58 buf.space[r] = '\0'; |
|
59 CX_TEST_ASSERT(0 == strcmp(buf.space, "This Test aged 10 years in a CASK.")); |
|
60 cxBufferDestroy(&buf); |
|
61 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); |
|
62 } |
|
63 cx_testing_allocator_destroy(&talloc); |
|
64 } |
|
65 |
|
66 CX_TEST(test_bprintf_large_string) { |
|
67 CxTestingAllocator talloc; |
|
68 cx_testing_allocator_init(&talloc); |
|
69 CxAllocator *alloc = &talloc.base; |
|
70 char *aaa = malloc(512); |
|
71 char *bbb = malloc(512); |
|
72 char *expected = malloc(1040); |
|
73 memset(aaa, 'a', 511); |
|
74 aaa[511] = 0; |
|
75 memset(bbb, 'b', 511); |
|
76 bbb[511] = 0; |
|
77 sprintf(expected, "After %s comes %s.", aaa, bbb); |
|
78 CX_TEST_DO { |
|
79 CxBuffer buf; |
|
80 cxBufferInit(&buf, NULL, 64, alloc, CX_BUFFER_AUTO_EXTEND); |
|
81 size_t r = cx_bprintf(&buf, "After %s comes %s.", aaa, bbb); |
|
82 CX_TEST_ASSERT(r == 1036); |
|
83 CX_TEST_ASSERT(buf.size == 1036); |
|
84 cxBufferPut(&buf, 0); |
|
85 CX_TEST_ASSERT(0 == strcmp(expected, buf.space)); |
|
86 cxBufferDestroy(&buf); |
|
87 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); |
|
88 } |
|
89 free(aaa); |
|
90 free(bbb); |
|
91 free(expected); |
|
92 cx_testing_allocator_destroy(&talloc); |
|
93 } |
|
94 |
|
95 CX_TEST(test_bprintf_nocap) { |
|
96 CxTestingAllocator talloc; |
|
97 cx_testing_allocator_init(&talloc); |
|
98 CxAllocator *alloc = &talloc.base; |
|
99 char space[20]; |
|
100 memset(space, 'a', 20); |
|
101 CX_TEST_DO { |
|
102 CxBuffer buf; |
|
103 cxBufferInit(&buf, space, 16, alloc, 0); |
|
104 size_t r = cx_bprintf(&buf, "Hello %s with more than %d chars.", "string", 16); |
|
105 CX_TEST_ASSERT(r == 16); |
|
106 CX_TEST_ASSERT(buf.size == 16); |
|
107 CX_TEST_ASSERT(0 == memcmp(space, "Hello string witaaaa", 20)); |
|
108 cxBufferDestroy(&buf); |
|
109 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); |
|
110 } |
|
111 cx_testing_allocator_destroy(&talloc); |
|
112 } |
|
113 |
|
114 CX_TEST(test_fprintf) { |
|
115 char const *h = "Hello"; |
|
116 char buf[32]; |
|
117 size_t r; |
|
118 CX_TEST_DO { |
|
119 r = cx_fprintf(buf, test_printf_write_func, "teststring"); |
|
120 CX_TEST_ASSERT(r == 10); |
|
121 CX_TEST_ASSERT(0 == memcmp(buf, "teststring", r)); |
|
122 |
|
123 r = cx_fprintf(buf, test_printf_write_func, "[%10s]", h); |
|
124 CX_TEST_ASSERT(r == 12); |
|
125 CX_TEST_ASSERT(0 == memcmp(buf, "[ Hello]", r)); |
|
126 |
|
127 r = cx_fprintf(buf, test_printf_write_func, "[%-10s]", h); |
|
128 CX_TEST_ASSERT(r == 12); |
|
129 CX_TEST_ASSERT(0 == memcmp(buf, "[Hello ]", r)); |
|
130 |
|
131 r = cx_fprintf(buf, test_printf_write_func, "[%*s]", 10, h); |
|
132 CX_TEST_ASSERT(r == 12); |
|
133 CX_TEST_ASSERT(0 == memcmp(buf, "[ Hello]", r)); |
|
134 |
|
135 r = cx_fprintf(buf, test_printf_write_func, "[%-10.*s]", 4, h); |
|
136 CX_TEST_ASSERT(r == 12); |
|
137 CX_TEST_ASSERT(0 == memcmp(buf, "[Hell ]", r)); |
|
138 |
|
139 r = cx_fprintf(buf, test_printf_write_func, "[%-*.*s]", 10, 4, h); |
|
140 CX_TEST_ASSERT(r == 12); |
|
141 CX_TEST_ASSERT(0 == memcmp(buf, "[Hell ]", r)); |
|
142 |
|
143 r = cx_fprintf(buf, test_printf_write_func, "%c", 'A'); |
|
144 CX_TEST_ASSERT(r == 1); |
|
145 CX_TEST_ASSERT(0 == memcmp(buf, "A", r)); |
|
146 |
|
147 r = cx_fprintf(buf, test_printf_write_func, "%i %d %.6i %i %.0i %+i %i", 1, 2, 3, 0, 0, 4, -4); |
|
148 CX_TEST_ASSERT(r == 19); |
|
149 CX_TEST_ASSERT(0 == memcmp(buf, "1 2 000003 0 +4 -4", r)); |
|
150 |
|
151 r = cx_fprintf(buf, test_printf_write_func, "%x %x %X %#x", 5, 10, 10, 6); |
|
152 CX_TEST_ASSERT(r == 9); |
|
153 CX_TEST_ASSERT(0 == memcmp(buf, "5 a A 0x6", r)); |
|
154 |
|
155 r = cx_fprintf(buf, test_printf_write_func, "%o %#o %#o", 10, 10, 4); |
|
156 CX_TEST_ASSERT(r == 9); |
|
157 CX_TEST_ASSERT(0 == memcmp(buf, "12 012 04", r)); |
|
158 |
|
159 r = cx_fprintf(buf, test_printf_write_func, "%05.2f %.2f %5.2f", 1.5, 1.5, 1.5); |
|
160 CX_TEST_ASSERT(r == 16); |
|
161 CX_TEST_ASSERT(0 == memcmp(buf, "01.50 1.50 1.50", r)); |
|
162 |
|
163 r = cx_fprintf(buf, test_printf_write_func, "'%*c'", 5, 'x'); |
|
164 CX_TEST_ASSERT(r == 7); |
|
165 CX_TEST_ASSERT(0 == memcmp(buf, "' x'", r)); |
|
166 |
|
167 r = cx_fprintf(buf, test_printf_write_func, "'%*c'", -5, 'x'); |
|
168 CX_TEST_ASSERT(r == 7); |
|
169 CX_TEST_ASSERT(0 == memcmp(buf, "'x '", r)); |
|
170 } |
|
171 } |
|
172 |
|
173 CX_TEST(test_asprintf) { |
|
174 CxTestingAllocator talloc; |
|
175 cx_testing_allocator_init(&talloc); |
|
176 CxAllocator *alloc = &talloc.base; |
|
177 |
|
178 char const *h = "Hello"; |
|
179 |
|
180 int const specimen_count = 13; |
|
181 cxmutstr r[specimen_count]; |
|
182 int specimen = 0; |
|
183 |
|
184 CX_TEST_DO { |
|
185 r[specimen] = cx_asprintf_a(alloc, "teststring"); |
|
186 CX_TEST_ASSERT(r[specimen].length == 10); |
|
187 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
188 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "teststring")); |
|
189 specimen++; |
|
190 |
|
191 r[specimen] = cx_asprintf_a(alloc, "[%10s]", h); |
|
192 CX_TEST_ASSERT(r[specimen].length == 12); |
|
193 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
194 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "[ Hello]")); |
|
195 specimen++; |
|
196 |
|
197 r[specimen] = cx_asprintf_a(alloc, "[%-10s]", h); |
|
198 CX_TEST_ASSERT(r[specimen].length == 12); |
|
199 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
200 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "[Hello ]")); |
|
201 specimen++; |
|
202 |
|
203 r[specimen] = cx_asprintf_a(alloc, "[%*s]", 10, h); |
|
204 CX_TEST_ASSERT(r[specimen].length == 12); |
|
205 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
206 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "[ Hello]")); |
|
207 specimen++; |
|
208 |
|
209 r[specimen] = cx_asprintf_a(alloc, "[%-10.*s]", 4, h); |
|
210 CX_TEST_ASSERT(r[specimen].length == 12); |
|
211 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
212 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "[Hell ]")); |
|
213 specimen++; |
|
214 |
|
215 r[specimen] = cx_asprintf_a(alloc, "[%-*.*s]", 10, 4, h); |
|
216 CX_TEST_ASSERT(r[specimen].length == 12); |
|
217 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
218 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "[Hell ]")); |
|
219 specimen++; |
|
220 |
|
221 r[specimen] = cx_asprintf_a(alloc, "%c", 'A'); |
|
222 CX_TEST_ASSERT(r[specimen].length == 1); |
|
223 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
224 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "A")); |
|
225 specimen++; |
|
226 |
|
227 r[specimen] = cx_asprintf_a(alloc, "%i %d %.6i %i %.0i %+i %i", 1, 2, 3, 0, 0, 4, -4); |
|
228 CX_TEST_ASSERT(r[specimen].length == 19); |
|
229 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
230 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "1 2 000003 0 +4 -4")); |
|
231 specimen++; |
|
232 |
|
233 r[specimen] = cx_asprintf_a(alloc, "%x %x %X %#x", 5, 10, 10, 6); |
|
234 CX_TEST_ASSERT(r[specimen].length == 9); |
|
235 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
236 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "5 a A 0x6")); |
|
237 specimen++; |
|
238 |
|
239 r[specimen] = cx_asprintf_a(alloc, "%o %#o %#o", 10, 10, 4); |
|
240 CX_TEST_ASSERT(r[specimen].length == 9); |
|
241 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
242 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "12 012 04")); |
|
243 specimen++; |
|
244 |
|
245 r[specimen] = cx_asprintf_a(alloc, "%05.2f %.2f %5.2f", 1.5, 1.5, 1.5); |
|
246 CX_TEST_ASSERT(r[specimen].length == 16); |
|
247 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
248 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "01.50 1.50 1.50")); |
|
249 specimen++; |
|
250 |
|
251 r[specimen] = cx_asprintf_a(alloc, "'%*c'", 5, 'x'); |
|
252 CX_TEST_ASSERT(r[specimen].length == 7); |
|
253 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
254 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "' x'")); |
|
255 specimen++; |
|
256 |
|
257 r[specimen] = cx_asprintf_a(alloc, "'%*c'", -5, 'x'); |
|
258 CX_TEST_ASSERT(r[specimen].length == 7); |
|
259 ASSERT_ZERO_TERMINATED(r[specimen]); |
|
260 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "'x '")); |
|
261 specimen++; |
|
262 |
|
263 CX_TEST_ASSERT(specimen == specimen_count); // self-test |
|
264 |
|
265 for (int i = 0; i < specimen_count; i++) { |
|
266 cx_strfree_a(alloc, &r[i]); |
|
267 } |
|
268 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); |
|
269 } |
|
270 cx_testing_allocator_destroy(&talloc); |
|
271 } |
|
272 |
|
273 CX_TEST(test_asprintf_large_string) { |
|
274 char *aaa = malloc(512); |
|
275 char *bbb = malloc(512); |
|
276 char *expected = malloc(1040); |
|
277 memset(aaa, 'a', 511); |
|
278 aaa[511] = 0; |
|
279 memset(bbb, 'b', 511); |
|
280 bbb[511] = 0; |
|
281 sprintf(expected, "After %s comes %s.", aaa, bbb); |
|
282 CX_TEST_DO { |
|
283 cxmutstr r = cx_asprintf("After %s comes %s.", aaa, bbb); |
|
284 CX_TEST_ASSERT(r.length == 1036); |
|
285 ASSERT_ZERO_TERMINATED(r); |
|
286 CX_TEST_ASSERT(0 == strcmp(r.ptr, expected)); |
|
287 cx_strfree(&r); |
|
288 } |
|
289 free(aaa); |
|
290 free(bbb); |
|
291 free(expected); |
|
292 } |
|
293 |
|
294 CxTestSuite *cx_test_suite_printf(void) { |
|
295 CxTestSuite *suite = cx_test_suite_new("printf"); |
|
296 |
|
297 cx_test_register(suite, test_bprintf); |
|
298 cx_test_register(suite, test_bprintf_large_string); |
|
299 cx_test_register(suite, test_bprintf_nocap); |
|
300 cx_test_register(suite, test_fprintf); |
|
301 cx_test_register(suite, test_asprintf); |
|
302 cx_test_register(suite, test_asprintf_large_string); |
|
303 |
|
304 return suite; |
|
305 } |