UNIXworkcode

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 const void *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 unsigned len = cx_printf_sbo_size; 68 CxTestingAllocator talloc; 69 cx_testing_allocator_init(&talloc); 70 CxAllocator *alloc = &talloc.base; 71 char *aaa = malloc(len); 72 char *bbb = malloc(len); 73 char *expected = malloc(2*len+16); 74 memset(aaa, 'a', len-1); 75 aaa[len-1] = 0; 76 memset(bbb, 'b', len-1); 77 bbb[len-1] = 0; 78 sprintf(expected, "After %s comes %s.", aaa, bbb); 79 CX_TEST_DO { 80 CxBuffer buf; 81 cxBufferInit(&buf, NULL, 64, alloc, CX_BUFFER_AUTO_EXTEND); 82 size_t r = cx_bprintf(&buf, "After %s comes %s.", aaa, bbb); 83 size_t er = 2*len-2+14; 84 CX_TEST_ASSERT(r == er); 85 CX_TEST_ASSERT(buf.size == er); 86 cxBufferPut(&buf, 0); 87 CX_TEST_ASSERT(0 == strcmp(expected, buf.space)); 88 cxBufferDestroy(&buf); 89 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 90 } 91 free(aaa); 92 free(bbb); 93 free(expected); 94 cx_testing_allocator_destroy(&talloc); 95 } 96 97 CX_TEST(test_bprintf_nocap) { 98 CxTestingAllocator talloc; 99 cx_testing_allocator_init(&talloc); 100 CxAllocator *alloc = &talloc.base; 101 char space[20]; 102 memset(space, 'a', 20); 103 CX_TEST_DO { 104 CxBuffer buf; 105 cxBufferInit(&buf, space, 16, alloc, 0); 106 size_t r = cx_bprintf(&buf, "Hello %s with more than %d chars.", "string", 16); 107 CX_TEST_ASSERT(r == 16); 108 CX_TEST_ASSERT(buf.size == 16); 109 CX_TEST_ASSERT(0 == memcmp(space, "Hello string witaaaa", 20)); 110 cxBufferDestroy(&buf); 111 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 112 } 113 cx_testing_allocator_destroy(&talloc); 114 } 115 116 CX_TEST(test_fprintf) { 117 const char *h = "Hello"; 118 char buf[32]; 119 size_t r; 120 CX_TEST_DO { 121 r = cx_fprintf(buf, test_printf_write_func, "teststring"); 122 CX_TEST_ASSERT(r == 10); 123 CX_TEST_ASSERT(0 == memcmp(buf, "teststring", r)); 124 125 r = cx_fprintf(buf, test_printf_write_func, "[%10s]", h); 126 CX_TEST_ASSERT(r == 12); 127 CX_TEST_ASSERT(0 == memcmp(buf, "[ Hello]", r)); 128 129 r = cx_fprintf(buf, test_printf_write_func, "[%-10s]", h); 130 CX_TEST_ASSERT(r == 12); 131 CX_TEST_ASSERT(0 == memcmp(buf, "[Hello ]", r)); 132 133 r = cx_fprintf(buf, test_printf_write_func, "[%*s]", 10, h); 134 CX_TEST_ASSERT(r == 12); 135 CX_TEST_ASSERT(0 == memcmp(buf, "[ Hello]", r)); 136 137 r = cx_fprintf(buf, test_printf_write_func, "[%-10.*s]", 4, h); 138 CX_TEST_ASSERT(r == 12); 139 CX_TEST_ASSERT(0 == memcmp(buf, "[Hell ]", r)); 140 141 r = cx_fprintf(buf, test_printf_write_func, "[%-*.*s]", 10, 4, h); 142 CX_TEST_ASSERT(r == 12); 143 CX_TEST_ASSERT(0 == memcmp(buf, "[Hell ]", r)); 144 145 r = cx_fprintf(buf, test_printf_write_func, "%c", 'A'); 146 CX_TEST_ASSERT(r == 1); 147 CX_TEST_ASSERT(0 == memcmp(buf, "A", r)); 148 149 r = cx_fprintf(buf, test_printf_write_func, "%i %d %.6i %i %.0i %+i %i", 1, 2, 3, 0, 0, 4, -4); 150 CX_TEST_ASSERT(r == 19); 151 CX_TEST_ASSERT(0 == memcmp(buf, "1 2 000003 0 +4 -4", r)); 152 153 r = cx_fprintf(buf, test_printf_write_func, "%x %x %X %#x", 5, 10, 10, 6); 154 CX_TEST_ASSERT(r == 9); 155 CX_TEST_ASSERT(0 == memcmp(buf, "5 a A 0x6", r)); 156 157 r = cx_fprintf(buf, test_printf_write_func, "%o %#o %#o", 10, 10, 4); 158 CX_TEST_ASSERT(r == 9); 159 CX_TEST_ASSERT(0 == memcmp(buf, "12 012 04", r)); 160 161 r = cx_fprintf(buf, test_printf_write_func, "%05.2f %.2f %5.2f", 1.5, 1.5, 1.5); 162 CX_TEST_ASSERT(r == 16); 163 CX_TEST_ASSERT(0 == memcmp(buf, "01.50 1.50 1.50", r)); 164 165 r = cx_fprintf(buf, test_printf_write_func, "''%*c''", 5, 'x'); 166 CX_TEST_ASSERT(r == 7); 167 CX_TEST_ASSERT(0 == memcmp(buf, "'' x''", r)); 168 169 r = cx_fprintf(buf, test_printf_write_func, "''%*c''", -5, 'x'); 170 CX_TEST_ASSERT(r == 7); 171 CX_TEST_ASSERT(0 == memcmp(buf, "''x ''", r)); 172 } 173 } 174 175 CX_TEST(test_asprintf) { 176 CxTestingAllocator talloc; 177 cx_testing_allocator_init(&talloc); 178 CxAllocator *alloc = &talloc.base; 179 180 const char *h = "Hello"; 181 182 cxmutstr r[13]; 183 size_t specimen_count = cx_nmemb(r); 184 size_t specimen = 0; 185 186 CX_TEST_DO { 187 r[specimen] = cx_asprintf_a(alloc, "teststring"); 188 CX_TEST_ASSERT(r[specimen].length == 10); 189 ASSERT_ZERO_TERMINATED(r[specimen]); 190 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "teststring")); 191 specimen++; 192 193 r[specimen] = cx_asprintf_a(alloc, "[%10s]", h); 194 CX_TEST_ASSERT(r[specimen].length == 12); 195 ASSERT_ZERO_TERMINATED(r[specimen]); 196 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "[ Hello]")); 197 specimen++; 198 199 r[specimen] = cx_asprintf_a(alloc, "[%-10s]", h); 200 CX_TEST_ASSERT(r[specimen].length == 12); 201 ASSERT_ZERO_TERMINATED(r[specimen]); 202 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "[Hello ]")); 203 specimen++; 204 205 r[specimen] = cx_asprintf_a(alloc, "[%*s]", 10, h); 206 CX_TEST_ASSERT(r[specimen].length == 12); 207 ASSERT_ZERO_TERMINATED(r[specimen]); 208 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "[ Hello]")); 209 specimen++; 210 211 r[specimen] = cx_asprintf_a(alloc, "[%-10.*s]", 4, h); 212 CX_TEST_ASSERT(r[specimen].length == 12); 213 ASSERT_ZERO_TERMINATED(r[specimen]); 214 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "[Hell ]")); 215 specimen++; 216 217 r[specimen] = cx_asprintf_a(alloc, "[%-*.*s]", 10, 4, h); 218 CX_TEST_ASSERT(r[specimen].length == 12); 219 ASSERT_ZERO_TERMINATED(r[specimen]); 220 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "[Hell ]")); 221 specimen++; 222 223 r[specimen] = cx_asprintf_a(alloc, "%c", 'A'); 224 CX_TEST_ASSERT(r[specimen].length == 1); 225 ASSERT_ZERO_TERMINATED(r[specimen]); 226 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "A")); 227 specimen++; 228 229 r[specimen] = cx_asprintf_a(alloc, "%i %d %.6i %i %.0i %+i %i", 1, 2, 3, 0, 0, 4, -4); 230 CX_TEST_ASSERT(r[specimen].length == 19); 231 ASSERT_ZERO_TERMINATED(r[specimen]); 232 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "1 2 000003 0 +4 -4")); 233 specimen++; 234 235 r[specimen] = cx_asprintf_a(alloc, "%x %x %X %#x", 5, 10, 10, 6); 236 CX_TEST_ASSERT(r[specimen].length == 9); 237 ASSERT_ZERO_TERMINATED(r[specimen]); 238 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "5 a A 0x6")); 239 specimen++; 240 241 r[specimen] = cx_asprintf_a(alloc, "%o %#o %#o", 10, 10, 4); 242 CX_TEST_ASSERT(r[specimen].length == 9); 243 ASSERT_ZERO_TERMINATED(r[specimen]); 244 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "12 012 04")); 245 specimen++; 246 247 r[specimen] = cx_asprintf_a(alloc, "%05.2f %.2f %5.2f", 1.5, 1.5, 1.5); 248 CX_TEST_ASSERT(r[specimen].length == 16); 249 ASSERT_ZERO_TERMINATED(r[specimen]); 250 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "01.50 1.50 1.50")); 251 specimen++; 252 253 r[specimen] = cx_asprintf_a(alloc, "''%*c''", 5, 'x'); 254 CX_TEST_ASSERT(r[specimen].length == 7); 255 ASSERT_ZERO_TERMINATED(r[specimen]); 256 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "'' x''")); 257 specimen++; 258 259 r[specimen] = cx_asprintf_a(alloc, "''%*c''", -5, 'x'); 260 CX_TEST_ASSERT(r[specimen].length == 7); 261 ASSERT_ZERO_TERMINATED(r[specimen]); 262 CX_TEST_ASSERT(0 == strcmp(r[specimen].ptr, "''x ''")); 263 specimen++; 264 265 CX_TEST_ASSERT(specimen == specimen_count); // self-test 266 267 for (size_t i = 0; i < specimen_count; i++) { 268 cx_strfree_a(alloc, &r[i]); 269 } 270 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 271 } 272 cx_testing_allocator_destroy(&talloc); 273 } 274 275 CX_TEST(test_asprintf_large_string) { 276 unsigned len = cx_printf_sbo_size; 277 char *aaa = malloc(len); 278 char *bbb = malloc(len); 279 char *expected = malloc(2*len+16); 280 memset(aaa, 'a', len-1); 281 aaa[len-1] = 0; 282 memset(bbb, 'b', len-1); 283 bbb[len-1] = 0; 284 sprintf(expected, "After %s comes %s.", aaa, bbb); 285 CX_TEST_DO { 286 cxmutstr r = cx_asprintf("After %s comes %s.", aaa, bbb); 287 CX_TEST_ASSERT(r.length == 2*len-2+14); 288 ASSERT_ZERO_TERMINATED(r); 289 CX_TEST_ASSERT(0 == strcmp(r.ptr, expected)); 290 cx_strfree(&r); 291 } 292 free(aaa); 293 free(bbb); 294 free(expected); 295 } 296 297 CX_TEST(test_sprintf_no_realloc) { 298 char *buf = malloc(16); 299 CxTestingAllocator talloc; 300 cx_testing_allocator_init(&talloc); 301 CxAllocator *alloc = &talloc.base; 302 CX_TEST_DO { 303 char *oldbuf = buf; 304 size_t buflen = 16; 305 size_t len = cx_sprintf_a(alloc, &buf, &buflen, "Test %d %s", 47, "string"); 306 CX_TEST_ASSERT(oldbuf == buf); 307 CX_TEST_ASSERT(len == 14); 308 CX_TEST_ASSERT(buflen == 16); 309 CX_TEST_ASSERT(0 == memcmp(buf, "Test 47 string", 15)); 310 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 311 } 312 cx_testing_allocator_destroy(&talloc); 313 free(buf); 314 } 315 316 CX_TEST(test_sprintf_realloc) { 317 CxTestingAllocator talloc; 318 cx_testing_allocator_init(&talloc); 319 CxAllocator *alloc = &talloc.base; 320 char *buf = cxMalloc(alloc, 8); 321 CX_TEST_DO { 322 size_t buflen = 8; 323 size_t len = cx_sprintf_a(alloc, &buf, &buflen, "Test %d %s", 47, "foobar"); 324 CX_TEST_ASSERT(len == 14); 325 CX_TEST_ASSERT(buflen == 15); 326 CX_TEST_ASSERT(0 == memcmp(buf, "Test 47 foobar", 15)); 327 cxFree(alloc, buf); 328 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 329 } 330 cx_testing_allocator_destroy(&talloc); 331 } 332 333 CX_TEST(test_sprintf_realloc_to_fit_terminator) { 334 CxTestingAllocator talloc; 335 cx_testing_allocator_init(&talloc); 336 CxAllocator *alloc = &talloc.base; 337 // make it so that only the zero-terminator does not fit 338 char *buf = cxMalloc(alloc, 14); 339 CX_TEST_DO { 340 size_t buflen = 14; 341 size_t len = cx_sprintf_a(alloc, &buf, &buflen, "Test %d %s", 13, "string"); 342 CX_TEST_ASSERT(len == 14); 343 CX_TEST_ASSERT(buflen == 15); 344 CX_TEST_ASSERT(0 == memcmp(buf, "Test 13 string", 15)); 345 cxFree(alloc, buf); 346 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 347 } 348 cx_testing_allocator_destroy(&talloc); 349 } 350 351 CX_TEST(test_sprintf_s_no_alloc) { 352 char buf[16]; 353 CxTestingAllocator talloc; 354 cx_testing_allocator_init(&talloc); 355 CxAllocator *alloc = &talloc.base; 356 CX_TEST_DO { 357 char *str; 358 size_t buflen = 16; 359 size_t len = cx_sprintf_sa(alloc, buf, &buflen, &str, "Test %d %s", 47, "string"); 360 CX_TEST_ASSERT(str == buf); 361 CX_TEST_ASSERT(buflen == 16); 362 CX_TEST_ASSERT(len == 14); 363 CX_TEST_ASSERT(0 == memcmp(buf, "Test 47 string", 15)); 364 CX_TEST_ASSERT(0 == memcmp(str, "Test 47 string", 15)); 365 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 366 } 367 cx_testing_allocator_destroy(&talloc); 368 } 369 370 CX_TEST(test_sprintf_s_alloc) { 371 char buf[16]; 372 memcpy(buf, "0123456789abcdef", 16); 373 CxTestingAllocator talloc; 374 cx_testing_allocator_init(&talloc); 375 CxAllocator *alloc = &talloc.base; 376 CX_TEST_DO { 377 char *str; 378 size_t buflen = 16; 379 size_t len = cx_sprintf_sa(alloc, buf, &buflen, &str, "Hello %d %s", 4711, "larger string"); 380 CX_TEST_ASSERT(str != buf); 381 CX_TEST_ASSERT(buflen == 25); 382 CX_TEST_ASSERT(len == 24); 383 CX_TEST_ASSERT(0 == memcmp(str, "Hello 4711 larger string", 25)); 384 cxFree(alloc, str); 385 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 386 } 387 cx_testing_allocator_destroy(&talloc); 388 } 389 390 CX_TEST(test_sprintf_s_alloc_to_fit_terminator) { 391 char buf[16]; 392 memcpy(buf, "0123456789abcdef", 16); 393 CxTestingAllocator talloc; 394 cx_testing_allocator_init(&talloc); 395 CxAllocator *alloc = &talloc.base; 396 CX_TEST_DO { 397 char *str; 398 size_t buflen = 16; 399 size_t len = cx_sprintf_sa(alloc, buf,&buflen, &str, "Hello %d %s", 112, "string"); 400 CX_TEST_ASSERT(str != buf); 401 CX_TEST_ASSERT(len == 16); 402 CX_TEST_ASSERT(buflen == 17); 403 CX_TEST_ASSERT(0 == memcmp(str, "Hello 112 string", 17)); // include terminator 404 cxFree(alloc, str); 405 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 406 } 407 cx_testing_allocator_destroy(&talloc); 408 } 409 410 CxTestSuite *cx_test_suite_printf(void) { 411 CxTestSuite *suite = cx_test_suite_new("printf"); 412 413 cx_test_register(suite, test_bprintf); 414 cx_test_register(suite, test_bprintf_large_string); 415 cx_test_register(suite, test_bprintf_nocap); 416 cx_test_register(suite, test_fprintf); 417 cx_test_register(suite, test_asprintf); 418 cx_test_register(suite, test_asprintf_large_string); 419 cx_test_register(suite, test_sprintf_no_realloc); 420 cx_test_register(suite, test_sprintf_realloc); 421 cx_test_register(suite, test_sprintf_realloc_to_fit_terminator); 422 cx_test_register(suite, test_sprintf_s_no_alloc); 423 cx_test_register(suite, test_sprintf_s_alloc); 424 cx_test_register(suite, test_sprintf_s_alloc_to_fit_terminator); 425 426 return suite; 427 } 428