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/string.h" 33 #include "cx/compare.h" 34 35 #include <limits.h> 36 #include <errno.h> 37 38 #define ASSERT_ZERO_TERMINATED(str) CX_TEST_ASSERTM((str).ptr[(str).length] == '\0', \ 39 #str " is not zero terminated") 40 41 CX_TEST(test_string_construct) { 42 cxstring s1 = CX_STR("1234"); 43 cxstring s2 = cx_strn("abcd", 2); 44 cxmutstr s3 = cx_mutstr((char *) "1234"); 45 cxmutstr s4 = cx_mutstrn((char *) "abcd", 2); 46 cxstring snull = cx_str(NULL); 47 cxmutstr mnull = cx_mutstr(NULL); 48 CX_TEST_DO { 49 CX_TEST_ASSERT(s1.length == 4); 50 CX_TEST_ASSERT(strncmp(s1.ptr, "1234", 4) == 0); 51 CX_TEST_ASSERT(s2.length == 2); 52 CX_TEST_ASSERT(strncmp(s2.ptr, "ab", 2) == 0); 53 CX_TEST_ASSERT(s3.length == 4); 54 CX_TEST_ASSERT(strncmp(s3.ptr, "1234", 4) == 0); 55 CX_TEST_ASSERT(s4.length == 2); 56 CX_TEST_ASSERT(strncmp(s4.ptr, "ab", 2) == 0); 57 CX_TEST_ASSERT(0 == snull.length); 58 CX_TEST_ASSERT(NULL == snull.ptr); 59 CX_TEST_ASSERT(0 == mnull.length); 60 CX_TEST_ASSERT(NULL == mnull.ptr); 61 CX_TEST_ASSERT(0 == cx_strcmp(snull, "")); 62 CX_TEST_ASSERT(0 == cx_strcmp(mnull, "")); 63 } 64 } 65 66 CX_TEST(test_string_cast) { 67 char *c1 = (char*) "123"; 68 const char *c2 = "abcde"; 69 unsigned char *c3 = (unsigned char*) "4711"; 70 unsigned const char *c4 = (unsigned const char*) "xyz0815"; 71 cxstring s1 = cx_strcast(c1); 72 cxstring s2 = cx_strcast(c2); 73 cxstring s3 = cx_strcast(c3); 74 cxstring s4 = cx_strcast(c4); 75 CX_TEST_DO { 76 CX_TEST_ASSERT(s1.length == 3); 77 CX_TEST_ASSERT(strncmp(s1.ptr, "123", 3) == 0); 78 CX_TEST_ASSERT(s2.length == 5); 79 CX_TEST_ASSERT(strncmp(s2.ptr, "abcde", 5) == 0); 80 CX_TEST_ASSERT(s3.length == 4); 81 CX_TEST_ASSERT(strncmp(s3.ptr, "4711", 4) == 0); 82 CX_TEST_ASSERT(s4.length == 7); 83 CX_TEST_ASSERT(strncmp(s4.ptr, "xyz0815", 7) == 0); 84 } 85 } 86 87 CX_TEST(test_strfree) { 88 CxTestingAllocator talloc; 89 cx_testing_allocator_init(&talloc); 90 CxAllocator *alloc = &talloc.base; 91 CX_TEST_DO { 92 char *test = cxMalloc(alloc, 16); 93 cxmutstr str = cx_mutstrn(test, 16); 94 CX_TEST_ASSERT(str.ptr == test); 95 CX_TEST_ASSERT(str.length == 16); 96 cx_strfree_a(alloc, &str); 97 CX_TEST_ASSERT(str.ptr == NULL); 98 CX_TEST_ASSERT(str.length == 0); 99 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 100 // check that this does not explode 101 cx_strfree(NULL); 102 cx_strfree_a(alloc, NULL); 103 } 104 cx_testing_allocator_destroy(&talloc); 105 } 106 107 CX_TEST(test_strdup) { 108 cxstring str = CX_STR("test"); 109 cxmutstr dup = cx_strdup(str); 110 CX_TEST_DO { 111 CX_TEST_ASSERT(dup.length == str.length); 112 CX_TEST_ASSERT(0 == strcmp(dup.ptr, str.ptr)); 113 ASSERT_ZERO_TERMINATED(dup); 114 } 115 cx_strfree(&dup); 116 } 117 118 CX_TEST(test_strdup_shortened) { 119 cxstring str = CX_STR("test"); 120 str.length = 2; 121 cxmutstr dup = cx_strdup(str); 122 CX_TEST_DO { 123 CX_TEST_ASSERT(dup.length == str.length); 124 CX_TEST_ASSERT(0 == strcmp(dup.ptr, "te")); 125 ASSERT_ZERO_TERMINATED(dup); 126 } 127 cx_strfree(&dup); 128 } 129 130 CX_TEST(test_strcpy) { 131 CxTestingAllocator talloc; 132 cx_testing_allocator_init(&talloc); 133 const CxAllocator *alloc = &talloc.base; 134 cxstring str = CX_STR("test string"); 135 str.length = 8; // test with a non-zero-terminated source 136 cxmutstr dup; 137 CX_TEST_DO { 138 // copy into a smaller string 139 dup = cx_strdup_a(alloc, CX_STR("hello")); 140 CX_TEST_ASSERT(0 == cx_strcpy_a(alloc, &dup, str)); 141 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(dup), CX_STR("test str"))); 142 ASSERT_ZERO_TERMINATED(dup); 143 cx_strfree_a(alloc, &dup); 144 145 // copy into a larger string 146 dup = cx_strdup_a(alloc, CX_STR("hello, world!")); 147 CX_TEST_ASSERT(0 == cx_strcpy_a(alloc, &dup, str)); 148 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(dup), CX_STR("test str"))); 149 ASSERT_ZERO_TERMINATED(dup); 150 cx_strfree_a(alloc, &dup); 151 152 // copy into an equal-length string 153 dup = cx_strdup_a(alloc, CX_STR("testing!")); 154 CX_TEST_ASSERT(0 == cx_strcpy_a(alloc, &dup, str)); 155 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(dup), CX_STR("test str"))); 156 ASSERT_ZERO_TERMINATED(dup); 157 cx_strfree_a(alloc, &dup); 158 159 // copy into a NULL-string 160 dup.ptr = NULL; 161 CX_TEST_ASSERT(0 == cx_strcpy_a(alloc, &dup, str)); 162 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(dup), CX_STR("test str"))); 163 ASSERT_ZERO_TERMINATED(dup); 164 cx_strfree_a(alloc, &dup); 165 } 166 cx_testing_allocator_destroy(&talloc); 167 } 168 169 CX_TEST(test_strlen) { 170 cxstring s1 = CX_STR("1234"); 171 cxstring s2 = CX_STR(".:.:."); 172 cxstring s3 = CX_STR("X"); 173 CX_TEST_DO { 174 size_t len0 = cx_strlen(0); 175 size_t len1 = cx_strlen(1, s1); 176 size_t len2 = cx_strlen(2, s1, s2); 177 size_t len3 = cx_strlen(3, s1, s2, s3); 178 179 CX_TEST_ASSERT(len0 == 0); 180 CX_TEST_ASSERT(len1 == 4); 181 CX_TEST_ASSERT(len2 == 9); 182 CX_TEST_ASSERT(len3 == 10); 183 } 184 } 185 186 CX_TEST(test_strsubs) { 187 cxstring str = CX_STR("A test string"); 188 189 CX_TEST_DO { 190 cxstring sub = cx_strsubs(str, 0); 191 CX_TEST_ASSERT(0 == cx_strcmp(sub, str)); 192 193 sub = cx_strsubs(str, 2); 194 CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str("test string"))); 195 196 sub = cx_strsubs(str, 7); 197 CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str("string"))); 198 199 sub = cx_strsubs(str, 15); 200 CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str(""))); 201 202 sub = cx_strsubsl(str, 2, 4); 203 CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str("test"))); 204 205 sub = cx_strsubsl(str, 7, 3); 206 CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str("str"))); 207 208 sub = cx_strsubsl(str, 7, 20); 209 CX_TEST_ASSERT(0 == cx_strcmp(sub, cx_str("string"))); 210 211 // just for coverage, call the _m variant 212 cxmutstr m = cx_strsubs_m(cx_mutstrn(NULL, 0), 0); 213 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(m), cx_str(""))); 214 } 215 } 216 217 CX_TEST(test_strchr) { 218 cxstring str = CX_STR("I will find you - and I will kill you"); 219 220 CX_TEST_DO { 221 cxstring notfound = cx_strchr(str, 'x'); 222 CX_TEST_ASSERT(notfound.length == 0); 223 224 cxstring result = cx_strchr(str, 'w'); 225 CX_TEST_ASSERT(result.length == 35); 226 CX_TEST_ASSERT(0 == strcmp(result.ptr, "will find you - and I will kill you")); 227 228 // just for coverage, call the _m variant 229 cxmutstr m = cx_strchr_m(cx_mutstrn(NULL, 0), 'a'); 230 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(m), cx_str(""))); 231 } 232 } 233 234 CX_TEST(test_strrchr) { 235 cxstring str = CX_STR("X will find you - and I will kill you"); 236 237 CX_TEST_DO { 238 cxstring notfound = cx_strrchr(str, 'x'); 239 CX_TEST_ASSERT(notfound.length == 0); 240 241 cxstring result = cx_strrchr(str, 'w'); 242 CX_TEST_ASSERT(result.length == 13); 243 CX_TEST_ASSERT(0 == strcmp(result.ptr, "will kill you")); 244 245 result = cx_strrchr(str, 'u'); 246 CX_TEST_ASSERT(result.length == 1); 247 CX_TEST_ASSERT(0 == strcmp(result.ptr, "u")); 248 249 result = cx_strrchr(str, 'X'); 250 CX_TEST_ASSERT(0 == cx_strcmp(result, str)); 251 252 // just for coverage, call the _m variant 253 cxmutstr m = cx_strrchr_m(cx_mutstrn(NULL, 0), 'a'); 254 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(m), cx_str(""))); 255 } 256 } 257 258 CX_TEST(test_strstr) { 259 cxstring str = CX_STR("find the match in this string"); 260 261 const size_t longstrpatternlen = 64 + cx_strstr_sbo_size; 262 const size_t longstrlen = 320 + longstrpatternlen + 14; 263 264 // it is more expensive to use calloc here, because we will overwrite 265 // the memory anyway in the test preparation - but it is more reliable 266 // in case we are doing something horribly wrong 267 char *longstrc = calloc(longstrlen+1, 1); 268 char *longstrpatternc = calloc(longstrpatternlen+1, 1); 269 270 memcpy(longstrc, 271 "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl" 272 "mnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx" 273 "yzabcdeababababnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij" 274 "klmnopqrstuvwxyzaababababababababrstuvwxyzabcdefghijklmnopqrstuv" 275 "abababababababababababababababababababababababababababababababab", 276 320 277 ); 278 memcpy(longstrpatternc, 279 "abababababababababababababababababababababababababababababababab", 280 64 281 ); 282 char x = 'a', y='b', z; 283 for (size_t i = 0; i < cx_strstr_sbo_size ; i++) { 284 longstrpatternc[64+i] = x; 285 longstrc[320+i] = x; 286 z=x; x=y; y=z; 287 } 288 longstrpatternc[longstrpatternlen] = '\0'; 289 memcpy(longstrc+longstrlen-14, "wxyz1234567890", 15); 290 291 cxmutstr longstr = cx_mutstrn(longstrc, longstrlen); 292 cxstring longstrpattern = cx_strn(longstrpatternc, longstrpatternlen); 293 cxmutstr longstrresult = cx_mutstrn(longstrc+256, longstrlen-256); 294 295 CX_TEST_DO { 296 cxstring notfound = cx_strstr(str, cx_str("no match")); 297 CX_TEST_ASSERT(notfound.length == 0); 298 299 cxstring result = cx_strstr(str, cx_str("match")); 300 CX_TEST_ASSERT(result.length == 20); 301 CX_TEST_ASSERT(0 == strcmp(result.ptr, "match in this string")); 302 303 result = cx_strstr(str, cx_str("")); 304 CX_TEST_ASSERT(result.length == str.length); 305 CX_TEST_ASSERT(0 == strcmp(result.ptr, str.ptr)); 306 307 cxmutstr resultm = cx_strstr_m(longstr, longstrpattern); 308 CX_TEST_ASSERT(resultm.length == longstrresult.length); 309 CX_TEST_ASSERT(0 == strcmp(resultm.ptr, longstrresult.ptr)); 310 } 311 312 free(longstrc); 313 free(longstrpatternc); 314 } 315 316 CX_TEST(test_strcmp) { 317 cxstring str = CX_STR("compare this"); 318 CX_TEST_DO { 319 CX_TEST_ASSERT(0 == cx_strcmp(cx_str(""), cx_str(""))); 320 CX_TEST_ASSERT(0 < cx_strcmp(str, cx_str(""))); 321 CX_TEST_ASSERT(0 == cx_strcmp(str, cx_str("compare this"))); 322 CX_TEST_ASSERT(0 != cx_strcmp(str, cx_str("Compare This"))); 323 CX_TEST_ASSERT(0 > cx_strcmp(str, cx_str("compare tool"))); 324 CX_TEST_ASSERT(0 < cx_strcmp(str, cx_str("compare shit"))); 325 CX_TEST_ASSERT(0 > cx_strcmp(str, cx_str("compare this not"))); 326 CX_TEST_ASSERT(0 < cx_strcmp(str, cx_str("compare"))); 327 CX_TEST_ASSERT(0 > cx_strcmp(str, cx_str("lex"))); 328 CX_TEST_ASSERT(0 < cx_strcmp(str, cx_str("another lex test"))); 329 CX_TEST_ASSERT(0 < cx_strcmp(str, cx_str("Lex"))); 330 CX_TEST_ASSERT(0 < cx_strcmp(str, cx_str("Another lex test"))); 331 332 cxstring str2 = CX_STR("Compare This"); 333 CX_TEST_ASSERT(0 != cx_strcmp_p(&str, &str2)); 334 str2 = CX_STR("compare this"); 335 CX_TEST_ASSERT(0 == cx_strcmp_p(&str, &str2)); 336 } 337 } 338 339 CX_TEST(test_strcasecmp) { 340 cxstring str = CX_STR("compare this"); 341 CX_TEST_DO { 342 CX_TEST_ASSERT(0 == cx_strcasecmp(cx_str(""), cx_str(""))); 343 CX_TEST_ASSERT(0 < cx_strcasecmp(str, cx_str(""))); 344 CX_TEST_ASSERT(0 == cx_strcasecmp(str, cx_str("compare this"))); 345 CX_TEST_ASSERT(0 == cx_strcasecmp(str, cx_str("Compare This"))); 346 CX_TEST_ASSERT(0 > cx_strcasecmp(str, cx_str("compare tool"))); 347 CX_TEST_ASSERT(0 < cx_strcasecmp(str, cx_str("compare shit"))); 348 CX_TEST_ASSERT(0 > cx_strcasecmp(str, cx_str("compare this not"))); 349 CX_TEST_ASSERT(0 < cx_strcasecmp(str, cx_str("compare"))); 350 CX_TEST_ASSERT(0 > cx_strcasecmp(str, cx_str("lex"))); 351 CX_TEST_ASSERT(0 < cx_strcasecmp(str, cx_str("another lex test"))); 352 CX_TEST_ASSERT(0 > cx_strcasecmp(str, cx_str("Lex"))); 353 CX_TEST_ASSERT(0 < cx_strcasecmp(str, cx_str("Another lex test"))); 354 355 cxstring str2 = CX_STR("Compare This"); 356 CX_TEST_ASSERT(0 == cx_strcasecmp_p(&str, &str2)); 357 str2 = CX_STR("Compare Tool"); 358 CX_TEST_ASSERT(0 > cx_strcasecmp_p(&str, &str2)); 359 } 360 } 361 362 CX_TEST(test_strcat) { 363 cxstring s1 = CX_STR("12"); 364 cxstring s2 = CX_STR("34"); 365 cxstring s3 = CX_STR("56"); 366 cxstring sn = {NULL, 0}; 367 368 CxTestingAllocator talloc; 369 cx_testing_allocator_init(&talloc); 370 CxAllocator *alloc = &talloc.base; 371 372 CX_TEST_DO { 373 cxmutstr t1 = cx_strcat_a(alloc, 2, s1, s2); 374 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t1), cx_str("1234"))); 375 ASSERT_ZERO_TERMINATED(t1); 376 cx_strfree_a(alloc, &t1); 377 378 cxmutstr t2 = cx_strcat_a(alloc, 3, s1, s2, s3); 379 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t2), cx_str("123456"))); 380 ASSERT_ZERO_TERMINATED(t2); 381 cx_strfree_a(alloc, &t2); 382 383 cxmutstr t3 = cx_strcat_a(alloc, 6, s1, sn, s2, sn, s3, sn); 384 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t3), cx_str("123456"))); 385 ASSERT_ZERO_TERMINATED(t3); 386 cx_strfree_a(alloc, &t3); 387 388 cxmutstr t4 = cx_strcat_a(alloc, 2, sn, sn); 389 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t4), cx_str(""))); 390 ASSERT_ZERO_TERMINATED(t4); 391 cx_strfree_a(alloc, &t4); 392 393 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 394 395 // use the macro 396 cxmutstr t5 = cx_strcat(3, s3, s1, s2); 397 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t5), cx_str("561234"))); 398 ASSERT_ZERO_TERMINATED(t5); 399 cx_strfree(&t5); 400 401 // use an initial string 402 cxmutstr t6 = cx_strdup(cx_str("Hello")); 403 t6 = cx_strcat_m(t6, 2, cx_str(", "), cx_str("World!")); 404 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t6), cx_str("Hello, World!"))); 405 ASSERT_ZERO_TERMINATED(t6); 406 cx_strfree(&t6); 407 408 // test overflow with fake strings 409 char *fakestr = NULL; 410 cxstring a = cx_strn(fakestr, SIZE_MAX / 3 - 10); 411 cxstring b = cx_strn(fakestr, SIZE_MAX / 3 - 5); 412 cxstring c = cx_strn(fakestr, SIZE_MAX / 3 + 20); 413 errno = 0; 414 cxmutstr z = cx_strcat(3, a, b, c); 415 CX_TEST_ASSERT(errno == EOVERFLOW); 416 CX_TEST_ASSERT(z.ptr == NULL); 417 CX_TEST_ASSERT(z.length == 0); 418 } 419 cx_testing_allocator_destroy(&talloc); 420 } 421 422 CX_TEST(test_strcat_more_than_eight) { 423 cxstring s1 = CX_STR("12"); 424 cxstring s2 = CX_STR("34"); 425 cxstring s3 = CX_STR("56"); 426 cxstring s4 = CX_STR("78"); 427 cxstring s5 = CX_STR("9a"); 428 cxstring s6 = CX_STR("bc"); 429 cxstring s7 = CX_STR("de"); 430 cxstring s8 = CX_STR("f0"); 431 cxstring s9 = CX_STR("xy"); 432 433 CX_TEST_DO { 434 cxmutstr r = cx_strcat(9, s1, s2, s3, s4, s5, s6, s7, s8, s9); 435 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(r), cx_str("123456789abcdef0xy"))); 436 ASSERT_ZERO_TERMINATED(r); 437 cx_strfree(&r); 438 } 439 } 440 441 CX_TEST(test_strsplit) { 442 cxstring test = CX_STR("this,is,a,csv,string"); 443 size_t capa = 8; 444 cxstring list[8]; 445 size_t n; 446 CX_TEST_DO { 447 // special case: empty string 448 n = cx_strsplit(test, cx_str(""), capa, list); 449 CX_TEST_ASSERT(n == 1); 450 CX_TEST_ASSERT(0 == cx_strcmp(list[0], test)); 451 452 // no delimiter occurrence 453 n = cx_strsplit(test, cx_str("z"), capa, list); 454 CX_TEST_ASSERT(n == 1); 455 CX_TEST_ASSERT(0 == cx_strcmp(list[0], test)); 456 457 // partially matching delimiter 458 n = cx_strsplit(test, cx_str("is,not"), capa, list); 459 CX_TEST_ASSERT(n == 1); 460 CX_TEST_ASSERT(0 == cx_strcmp(list[0], test)); 461 462 // matching single-char delimiter 463 n = cx_strsplit(test, cx_str(","), capa, list); 464 CX_TEST_ASSERT(n == 5); 465 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("this"))); 466 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str("is"))); 467 CX_TEST_ASSERT(0 == cx_strcmp(list[2], cx_str("a"))); 468 CX_TEST_ASSERT(0 == cx_strcmp(list[3], cx_str("csv"))); 469 CX_TEST_ASSERT(0 == cx_strcmp(list[4], cx_str("string"))); 470 471 // matching multi-char delimiter 472 n = cx_strsplit(test, cx_str("is"), capa, list); 473 CX_TEST_ASSERT(n == 3); 474 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("th"))); 475 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(","))); 476 CX_TEST_ASSERT(0 == cx_strcmp(list[2], cx_str(",a,csv,string"))); 477 478 // bounded list using single-char delimiter 479 n = cx_strsplit(test, cx_str(","), 3, list); 480 CX_TEST_ASSERT(n == 3); 481 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("this"))); 482 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str("is"))); 483 CX_TEST_ASSERT(0 == cx_strcmp(list[2], cx_str("a,csv,string"))); 484 485 // bounded list using multi-char delimiter 486 n = cx_strsplit(test, cx_str("is"), 2, list); 487 CX_TEST_ASSERT(n == 2); 488 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("th"))); 489 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(",is,a,csv,string"))); 490 491 // start with delimiter 492 n = cx_strsplit(test, cx_str("this"), capa, list); 493 CX_TEST_ASSERT(n == 2); 494 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str(""))); 495 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(",is,a,csv,string"))); 496 497 // end with delimiter 498 n = cx_strsplit(test, cx_str("string"), capa, list); 499 CX_TEST_ASSERT(n == 2); 500 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("this,is,a,csv,"))); 501 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(""))); 502 503 504 // end with delimiter exceed bound 505 n = cx_strsplit(cx_str("a,b,c,"), cx_str(","), 3, list); 506 CX_TEST_ASSERT(n == 3); 507 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("a"))); 508 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str("b"))); 509 CX_TEST_ASSERT(0 == cx_strcmp(list[2], cx_str("c,"))); 510 511 // exact match 512 n = cx_strsplit(test, cx_str("this,is,a,csv,string"), capa, list); 513 CX_TEST_ASSERT(n == 2); 514 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str(""))); 515 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(""))); 516 517 // string to be split is only substring 518 n = cx_strsplit(test, cx_str("this,is,a,csv,string,with,extension"), capa, list); 519 CX_TEST_ASSERT(n == 1); 520 CX_TEST_ASSERT(0 == cx_strcmp(list[0], test)); 521 522 // subsequent encounter of delimiter (the string between is empty) 523 n = cx_strsplit(test, cx_str("is,"), capa, list); 524 CX_TEST_ASSERT(n == 3); 525 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("th"))); 526 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(""))); 527 CX_TEST_ASSERT(0 == cx_strcmp(list[2], cx_str("a,csv,string"))); 528 529 // call the _m variant just for coverage 530 cxmutstr mtest = cx_strdup(test); 531 cxmutstr mlist[4]; 532 n = cx_strsplit_m(mtest, cx_str("is,"), 4, mlist); 533 CX_TEST_ASSERT(n == 3); 534 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[0]), cx_str("th"))); 535 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[1]), cx_str(""))); 536 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[2]), cx_str("a,csv,string"))); 537 cx_strfree(&mtest); 538 } 539 } 540 541 CX_TEST(test_strsplit_a) { 542 CxTestingAllocator talloc; 543 cx_testing_allocator_init(&talloc); 544 CxAllocator *alloc = &talloc.base; 545 546 cxstring test = CX_STR("this,is,a,csv,string"); 547 size_t capa = 8; 548 cxstring *list; 549 size_t n; 550 CX_TEST_DO { 551 // special case: empty string 552 n = cx_strsplit_a(alloc, test, cx_str(""), capa, &list); 553 CX_TEST_ASSERT(n == 1); 554 CX_TEST_ASSERT(0 == cx_strcmp(list[0], test)); 555 cxFree(alloc, list); 556 557 // no delimiter occurrence 558 n = cx_strsplit_a(alloc, test, cx_str("z"), capa, &list); 559 CX_TEST_ASSERT(n == 1); 560 CX_TEST_ASSERT(0 == cx_strcmp(list[0], test)); 561 cxFree(alloc, list); 562 563 // partially matching delimiter 564 n = cx_strsplit_a(alloc, test, cx_str("is,not"), capa, &list); 565 CX_TEST_ASSERT(n == 1); 566 CX_TEST_ASSERT(0 == cx_strcmp(list[0], test)); 567 cxFree(alloc, list); 568 569 // matching single-char delimiter 570 n = cx_strsplit_a(alloc, test, cx_str(","), capa, &list); 571 CX_TEST_ASSERT(n == 5); 572 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("this"))); 573 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str("is"))); 574 CX_TEST_ASSERT(0 == cx_strcmp(list[2], cx_str("a"))); 575 CX_TEST_ASSERT(0 == cx_strcmp(list[3], cx_str("csv"))); 576 CX_TEST_ASSERT(0 == cx_strcmp(list[4], cx_str("string"))); 577 cxFree(alloc, list); 578 579 // matching multi-char delimiter 580 n = cx_strsplit_a(alloc, test, cx_str("is"), capa, &list); 581 CX_TEST_ASSERT(n == 3); 582 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("th"))); 583 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(","))); 584 CX_TEST_ASSERT(0 == cx_strcmp(list[2], cx_str(",a,csv,string"))); 585 cxFree(alloc, list); 586 587 // bounded list using single-char delimiter 588 n = cx_strsplit_a(alloc, test, cx_str(","), 3, &list); 589 CX_TEST_ASSERT(n == 3); 590 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("this"))); 591 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str("is"))); 592 CX_TEST_ASSERT(0 == cx_strcmp(list[2], cx_str("a,csv,string"))); 593 cxFree(alloc, list); 594 595 // bounded list using multi-char delimiter 596 n = cx_strsplit_a(alloc, test, cx_str("is"), 2, &list); 597 CX_TEST_ASSERT(n == 2); 598 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("th"))); 599 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(",is,a,csv,string"))); 600 cxFree(alloc, list); 601 602 // start with delimiter 603 n = cx_strsplit_a(alloc, test, cx_str("this"), capa, &list); 604 CX_TEST_ASSERT(n == 2); 605 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str(""))); 606 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(",is,a,csv,string"))); 607 cxFree(alloc, list); 608 609 // end with delimiter 610 n = cx_strsplit_a(alloc, test, cx_str("string"), capa, &list); 611 CX_TEST_ASSERT(n == 2); 612 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("this,is,a,csv,"))); 613 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(""))); 614 cxFree(alloc, list); 615 616 // end with delimiter exceed bound 617 n = cx_strsplit_a(alloc, cx_str("a,b,c,"), cx_str(","), 3, &list); 618 CX_TEST_ASSERT(n == 3); 619 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("a"))); 620 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str("b"))); 621 CX_TEST_ASSERT(0 == cx_strcmp(list[2], cx_str("c,"))); 622 cxFree(alloc, list); 623 624 // exact match 625 n = cx_strsplit_a(alloc, test, cx_str("this,is,a,csv,string"), capa, &list); 626 CX_TEST_ASSERT(n == 2); 627 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str(""))); 628 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(""))); 629 cxFree(alloc, list); 630 631 // string to be split is only substring 632 n = cx_strsplit_a(alloc, test, cx_str("this,is,a,csv,string,with,extension"), capa, &list); 633 CX_TEST_ASSERT(n == 1); 634 CX_TEST_ASSERT(0 == cx_strcmp(list[0], test)); 635 cxFree(alloc, list); 636 637 // subsequent encounter of delimiter (the string between is empty) 638 n = cx_strsplit_a(alloc, test, cx_str("is,"), capa, &list); 639 CX_TEST_ASSERT(n == 3); 640 CX_TEST_ASSERT(0 == cx_strcmp(list[0], cx_str("th"))); 641 CX_TEST_ASSERT(0 == cx_strcmp(list[1], cx_str(""))); 642 CX_TEST_ASSERT(0 == cx_strcmp(list[2], cx_str("a,csv,string"))); 643 cxFree(alloc, list); 644 645 // call the _m variant just for coverage 646 cxmutstr mtest = cx_strdup(test); 647 cxmutstr *mlist; 648 n = cx_strsplit_ma(alloc, mtest, cx_str("is,"), 4, &mlist); 649 CX_TEST_ASSERT(n == 3); 650 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[0]), cx_str("th"))); 651 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[1]), cx_str(""))); 652 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[2]), cx_str("a,csv,string"))); 653 cxFree(alloc, mlist); 654 cx_strfree(&mtest); 655 656 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 657 } 658 cx_testing_allocator_destroy(&talloc); 659 } 660 661 CX_TEST(test_strtrim) { 662 cxstring t1 = cx_strtrim(cx_str(" ein test \t ")); 663 cxstring t2 = cx_strtrim(cx_str("abc")); 664 cxstring t3 = cx_strtrim(cx_str(" 123")); 665 cxstring t4 = cx_strtrim(cx_str("xyz ")); 666 cxstring t5 = cx_strtrim(cx_str(" ")); 667 cxstring empty = cx_strtrim(cx_str("")); 668 669 CX_TEST_DO { 670 CX_TEST_ASSERT(0 == cx_strcmp(t1, cx_str("ein test"))); 671 CX_TEST_ASSERT(0 == cx_strcmp(t2, cx_str("abc"))); 672 CX_TEST_ASSERT(0 == cx_strcmp(t3, cx_str("123"))); 673 CX_TEST_ASSERT(0 == cx_strcmp(t4, cx_str("xyz"))); 674 CX_TEST_ASSERT(0 == cx_strcmp(t5, cx_str(""))); 675 CX_TEST_ASSERT(0 == cx_strcmp(empty, cx_str(""))); 676 677 // call the _m variant just for coverage 678 cxmutstr m1 = cx_strtrim_m(cx_mutstr((char *) " ein test \t ")); 679 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(m1), cx_str("ein test"))); 680 } 681 } 682 683 CX_TEST(test_strprefix) { 684 cxstring str = CX_STR("test my prefix and my suffix"); 685 cxstring empty = CX_STR(""); 686 CX_TEST_DO { 687 CX_TEST_ASSERT(!cx_strprefix(empty, cx_str("pref"))); 688 CX_TEST_ASSERT(cx_strprefix(str, empty)); 689 CX_TEST_ASSERT(cx_strprefix(empty, empty)); 690 CX_TEST_ASSERT(cx_strprefix(str, cx_str("test "))); 691 CX_TEST_ASSERT(!cx_strprefix(str, cx_str("8-) fsck "))); 692 } 693 } 694 695 CX_TEST(test_strsuffix) { 696 cxstring str = CX_STR("test my prefix and my suffix"); 697 cxstring empty = CX_STR(""); 698 CX_TEST_DO { 699 CX_TEST_ASSERT(!cx_strsuffix(empty, cx_str("suf"))); 700 CX_TEST_ASSERT(cx_strsuffix(str, empty)); 701 CX_TEST_ASSERT(cx_strsuffix(empty, empty)); 702 CX_TEST_ASSERT(cx_strsuffix(str, cx_str("fix"))); 703 CX_TEST_ASSERT(!cx_strsuffix(str, cx_str("fox"))); 704 } 705 } 706 707 CX_TEST(test_strcaseprefix) { 708 cxstring str = CX_STR("test my prefix and my suffix"); 709 cxstring empty = CX_STR(""); 710 CX_TEST_DO { 711 CX_TEST_ASSERT(!cx_strcaseprefix(empty, cx_str("pREf"))); 712 CX_TEST_ASSERT(cx_strcaseprefix(str, empty)); 713 CX_TEST_ASSERT(cx_strcaseprefix(empty, empty)); 714 CX_TEST_ASSERT(cx_strcaseprefix(str, cx_str("TEST "))); 715 CX_TEST_ASSERT(!cx_strcaseprefix(str, cx_str("8-) fsck "))); 716 } 717 } 718 719 CX_TEST(test_strcasesuffix) { 720 cxstring str = CX_STR("test my prefix and my suffix"); 721 cxstring empty = CX_STR(""); 722 CX_TEST_DO { 723 CX_TEST_ASSERT(!cx_strcasesuffix(empty, cx_str("sUf"))); 724 CX_TEST_ASSERT(cx_strcasesuffix(str, empty)); 725 CX_TEST_ASSERT(cx_strcasesuffix(empty, empty)); 726 CX_TEST_ASSERT(cx_strcasesuffix(str, cx_str("FIX"))); 727 CX_TEST_ASSERT(!cx_strcasesuffix(str, cx_str("fox"))); 728 } 729 } 730 731 CX_TEST(test_strreplace) { 732 CxTestingAllocator talloc; 733 cx_testing_allocator_init(&talloc); 734 CxAllocator *alloc = &talloc.base; 735 736 cxstring str = CX_STR("test ababab string aba"); 737 cxstring longstr = CX_STR( 738 "xyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacd"); 739 cxstring notrail = CX_STR("test abab"); 740 cxstring empty = CX_STR(""); 741 cxstring astr = CX_STR("aaaaaaaaaa"); 742 cxstring csstr = CX_STR("test AB ab TEST xyz"); 743 744 cxmutstr repl = cx_strreplace(str, cx_str("abab"), cx_str("muchlonger")); 745 const char *expected = "test muchlongerab string aba"; 746 747 cxmutstr repln = cx_strreplacen(str, cx_str("ab"), cx_str("c"), 2); 748 const char *expectedn = "test ccab string aba"; 749 750 cxmutstr longrepl = cx_strreplace(longstr, cx_str("a"), cx_str("z")); 751 const char *longexpect = "xyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzcd"; 752 753 cxmutstr replnotrail = cx_strreplace(notrail, cx_str("ab"), cx_str("z")); 754 const char *notrailexpect = "test zz"; 755 756 cxmutstr repleq = cx_strreplace(str, str, cx_str("hello")); 757 const char *eqexpect = "hello"; 758 759 cxmutstr replempty1 = cx_strreplace(empty, cx_str("ab"), cx_str("c")); // expect: empty 760 cxmutstr replempty2 = cx_strreplace(str, cx_str("abab"), empty); 761 const char *emptyexpect2 = "test ab string aba"; 762 763 cxmutstr replpre = cx_strreplace(str, cx_str("test "), cx_str("TEST ")); 764 const char *preexpected = "TEST ababab string aba"; 765 766 cxmutstr replan1 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 1); 767 const char *an1expected = "xaaaaaaaaa"; 768 769 cxmutstr replan4 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 4); 770 const char *an4expected = "xxxxaaaaaa"; 771 772 cxmutstr replan9 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 9); 773 const char *an9expected = "xxxxxxxxxa"; 774 775 cxmutstr replan10 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 10); 776 const char *an10expected = "xxxxxxxxxx"; 777 778 cxmutstr norepl = cx_strreplace(cx_strn("hello world", 11), cx_str("worlds"), cx_str("test")); 779 const char *noreplexpect = "hello world"; 780 781 CX_TEST_DO { 782 cxmutstr repl1_a = cx_strreplace_a(alloc, csstr, cx_str("AB"), cx_str("*")); 783 const char *expeced1_a = "test * ab TEST xyz"; 784 785 cxmutstr repl2_a = cx_strreplace_a(alloc, csstr, cx_str("test"), cx_str("TEST")); 786 const char *expected2_a = "TEST AB ab TEST xyz"; 787 788 CX_TEST_ASSERT(repl.ptr != str.ptr); 789 ASSERT_ZERO_TERMINATED(repl); 790 CX_TEST_ASSERT(0 == strcmp(repl.ptr, expected)); 791 ASSERT_ZERO_TERMINATED(repln); 792 CX_TEST_ASSERT(0 == strcmp(repln.ptr, expectedn)); 793 ASSERT_ZERO_TERMINATED(longrepl); 794 CX_TEST_ASSERT(0 == strcmp(longrepl.ptr, longexpect)); 795 ASSERT_ZERO_TERMINATED(replnotrail); 796 CX_TEST_ASSERT(0 == strcmp(replnotrail.ptr, notrailexpect)); 797 ASSERT_ZERO_TERMINATED(repleq); 798 CX_TEST_ASSERT(0 == strcmp(repleq.ptr, eqexpect)); 799 ASSERT_ZERO_TERMINATED(replempty1); 800 CX_TEST_ASSERT(0 == strcmp(replempty1.ptr, "")); 801 ASSERT_ZERO_TERMINATED(replempty2); 802 CX_TEST_ASSERT(0 == strcmp(replempty2.ptr, emptyexpect2)); 803 ASSERT_ZERO_TERMINATED(replpre); 804 CX_TEST_ASSERT(0 == strcmp(replpre.ptr, preexpected)); 805 ASSERT_ZERO_TERMINATED(replan1); 806 CX_TEST_ASSERT(0 == strcmp(replan1.ptr, an1expected)); 807 ASSERT_ZERO_TERMINATED(replan4); 808 CX_TEST_ASSERT(0 == strcmp(replan4.ptr, an4expected)); 809 ASSERT_ZERO_TERMINATED(replan9); 810 CX_TEST_ASSERT(0 == strcmp(replan9.ptr, an9expected)); 811 ASSERT_ZERO_TERMINATED(replan10); 812 CX_TEST_ASSERT(0 == strcmp(replan10.ptr, an10expected)); 813 ASSERT_ZERO_TERMINATED(repl1_a); 814 CX_TEST_ASSERT(0 == strcmp(repl1_a.ptr, expeced1_a)); 815 ASSERT_ZERO_TERMINATED(repl2_a); 816 CX_TEST_ASSERT(0 == strcmp(repl2_a.ptr, expected2_a)); 817 ASSERT_ZERO_TERMINATED(norepl); 818 CX_TEST_ASSERT(0 == strcmp(norepl.ptr, noreplexpect)); 819 820 cx_strfree_a(alloc, &repl1_a); 821 cx_strfree_a(alloc, &repl2_a); 822 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); 823 } 824 825 cx_strfree(&repl); 826 cx_strfree(&repln); 827 cx_strfree(&longrepl); 828 cx_strfree(&replnotrail); 829 cx_strfree(&repleq); 830 cx_strfree(&replempty1); 831 cx_strfree(&replempty2); 832 cx_strfree(&replpre); 833 cx_strfree(&replan1); 834 cx_strfree(&replan4); 835 cx_strfree(&replan9); 836 cx_strfree(&replan10); 837 cx_strfree(&norepl); 838 cx_testing_allocator_destroy(&talloc); 839 } 840 841 CX_TEST(test_strtok) { 842 cxstring str = CX_STR("a,comma,separated,string"); 843 cxstring delim = CX_STR(","); 844 CX_TEST_DO { 845 CxStrtokCtx ctx = cx_strtok(str, delim, 3); 846 CX_TEST_ASSERT(ctx.str.ptr == str.ptr); 847 CX_TEST_ASSERT(ctx.str.length == str.length); 848 CX_TEST_ASSERT(ctx.delim.ptr == delim.ptr); 849 CX_TEST_ASSERT(ctx.delim.length == delim.length); 850 CX_TEST_ASSERT(ctx.limit == 3); 851 CX_TEST_ASSERT(ctx.found == 0); 852 CX_TEST_ASSERT(ctx.pos == 0); 853 CX_TEST_ASSERT(ctx.next_pos == 0); 854 CX_TEST_ASSERT(ctx.delim_more == NULL); 855 CX_TEST_ASSERT(ctx.delim_more_count == 0); 856 } 857 } 858 859 CX_TEST(test_strtok_delim) { 860 cxstring str = CX_STR("an,arbitrarily|separated;string"); 861 cxstring delim = CX_STR(","); 862 cxstring delim_more[2] = {CX_STR("|"), CX_STR(";")}; 863 CX_TEST_DO { 864 CxStrtokCtx ctx = cx_strtok(str, delim, 3); 865 cx_strtok_delim(&ctx, delim_more, 2); 866 CX_TEST_ASSERT(ctx.str.ptr == str.ptr); 867 CX_TEST_ASSERT(ctx.str.length == str.length); 868 CX_TEST_ASSERT(ctx.delim.ptr == delim.ptr); 869 CX_TEST_ASSERT(ctx.delim.length == delim.length); 870 CX_TEST_ASSERT(ctx.limit == 3); 871 CX_TEST_ASSERT(ctx.found == 0); 872 CX_TEST_ASSERT(ctx.pos == 0); 873 CX_TEST_ASSERT(ctx.next_pos == 0); 874 CX_TEST_ASSERT(ctx.delim_more == delim_more); 875 CX_TEST_ASSERT(ctx.delim_more_count == 2); 876 } 877 } 878 879 CX_TEST(test_strtok_next_easy) { 880 cxstring str = CX_STR("a,comma,separated,string"); 881 cxstring delim = CX_STR(","); 882 CX_TEST_DO { 883 CxStrtokCtx ctx = cx_strtok(str, delim, 3); 884 bool ret; 885 cxstring tok; 886 887 ret = cx_strtok_next(&ctx, &tok); 888 CX_TEST_ASSERT(ret); 889 CX_TEST_ASSERT(0 == cx_strcmp(tok, cx_str("a"))); 890 CX_TEST_ASSERT(ctx.pos == 0); 891 CX_TEST_ASSERT(ctx.next_pos == 2); 892 CX_TEST_ASSERT(ctx.delim_pos == 1); 893 CX_TEST_ASSERT(ctx.found == 1); 894 895 ret = cx_strtok_next(&ctx, &tok); 896 CX_TEST_ASSERT(ret); 897 CX_TEST_ASSERT(0 == cx_strcmp(tok, cx_str("comma"))); 898 CX_TEST_ASSERT(ctx.pos == 2); 899 CX_TEST_ASSERT(ctx.next_pos == 8); 900 CX_TEST_ASSERT(ctx.delim_pos == 7); 901 CX_TEST_ASSERT(ctx.found == 2); 902 903 ret = cx_strtok_next(&ctx, &tok); 904 CX_TEST_ASSERT(ret); 905 CX_TEST_ASSERT(0 == cx_strcmp(tok, cx_str("separated"))); 906 CX_TEST_ASSERT(ctx.pos == 8); 907 CX_TEST_ASSERT(ctx.next_pos == 18); 908 CX_TEST_ASSERT(ctx.delim_pos == 17); 909 CX_TEST_ASSERT(ctx.found == 3); 910 911 ret = cx_strtok_next(&ctx, &tok); 912 CX_TEST_ASSERT(!ret); 913 CX_TEST_ASSERT(ctx.pos == 8); 914 CX_TEST_ASSERT(ctx.next_pos == 18); 915 CX_TEST_ASSERT(ctx.delim_pos == 17); 916 CX_TEST_ASSERT(ctx.found == 3); 917 } 918 } 919 920 CX_TEST(test_strtok_next_unlimited) { 921 cxstring str = CX_STR("some;-;otherwise;-;separated;-;string;-;"); 922 cxstring delim = CX_STR(";-;"); 923 CX_TEST_DO { 924 CxStrtokCtx ctx = cx_strtok(str, delim, SIZE_MAX); 925 bool ret; 926 cxstring tok; 927 928 ret = cx_strtok_next(&ctx, &tok); 929 CX_TEST_ASSERT(ret); 930 CX_TEST_ASSERT(0 == cx_strcmp(tok, cx_str("some"))); 931 CX_TEST_ASSERT(ctx.pos == 0); 932 CX_TEST_ASSERT(ctx.next_pos == 7); 933 CX_TEST_ASSERT(ctx.delim_pos == 4); 934 CX_TEST_ASSERT(ctx.found == 1); 935 936 ret = cx_strtok_next(&ctx, &tok); 937 CX_TEST_ASSERT(ret); 938 CX_TEST_ASSERT(0 == cx_strcmp(tok, cx_str("otherwise"))); 939 CX_TEST_ASSERT(ctx.pos == 7); 940 CX_TEST_ASSERT(ctx.next_pos == 19); 941 CX_TEST_ASSERT(ctx.delim_pos == 16); 942 CX_TEST_ASSERT(ctx.found == 2); 943 944 ret = cx_strtok_next(&ctx, &tok); 945 CX_TEST_ASSERT(ret); 946 CX_TEST_ASSERT(0 == cx_strcmp(tok, cx_str("separated"))); 947 CX_TEST_ASSERT(ctx.pos == 19); 948 CX_TEST_ASSERT(ctx.next_pos == 31); 949 CX_TEST_ASSERT(ctx.delim_pos == 28); 950 CX_TEST_ASSERT(ctx.found == 3); 951 952 ret = cx_strtok_next(&ctx, &tok); 953 CX_TEST_ASSERT(ret); 954 CX_TEST_ASSERT(0 == cx_strcmp(tok, cx_str("string"))); 955 CX_TEST_ASSERT(ctx.pos == 31); 956 CX_TEST_ASSERT(ctx.next_pos == 40); 957 CX_TEST_ASSERT(ctx.delim_pos == 37); 958 CX_TEST_ASSERT(ctx.found == 4); 959 960 ret = cx_strtok_next(&ctx, &tok); 961 CX_TEST_ASSERT(ret); 962 CX_TEST_ASSERT(0 == cx_strcmp(tok, cx_str(""))); 963 CX_TEST_ASSERT(ctx.pos == 40); 964 CX_TEST_ASSERT(ctx.next_pos == 40); 965 CX_TEST_ASSERT(ctx.delim_pos == 40); 966 CX_TEST_ASSERT(ctx.found == 5); 967 968 ret = cx_strtok_next(&ctx, &tok); 969 CX_TEST_ASSERT(!ret); 970 CX_TEST_ASSERT(ctx.pos == 40); 971 CX_TEST_ASSERT(ctx.delim_pos == 40); 972 CX_TEST_ASSERT(ctx.found == 5); 973 } 974 } 975 976 static void test_toupper(cxmutstr string) { 977 for (size_t i = 0; i < string.length; i++) { 978 if ((unsigned int)(string.ptr[i] - 'a') < 26u) { 979 string.ptr[i] += 'A' - 'a'; 980 } 981 } 982 } 983 984 CX_TEST(test_strtok_next_advanced) { 985 cxmutstr str = cx_strdup(cx_str("an,arbitrarily;||separated;string")); 986 cxstring delim = CX_STR(","); 987 cxstring delim_more[2] = {CX_STR("||"), CX_STR(";")}; 988 CX_TEST_DO { 989 CxStrtokCtx ctx = cx_strtok(str, delim, 10); 990 cx_strtok_delim(&ctx, delim_more, 2); 991 bool ret; 992 cxmutstr tok; 993 994 ret = cx_strtok_next_m(&ctx, &tok); 995 CX_TEST_ASSERT(ret); 996 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(tok), cx_str("an"))); 997 CX_TEST_ASSERT(ctx.pos == 0); 998 CX_TEST_ASSERT(ctx.next_pos == 3); 999 CX_TEST_ASSERT(ctx.delim_pos == 2); 1000 CX_TEST_ASSERT(ctx.found == 1); 1001 test_toupper(tok); 1002 1003 ret = cx_strtok_next_m(&ctx, &tok); 1004 CX_TEST_ASSERT(ret); 1005 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(tok), cx_str("arbitrarily"))); 1006 CX_TEST_ASSERT(ctx.pos == 3); 1007 CX_TEST_ASSERT(ctx.next_pos == 15); 1008 CX_TEST_ASSERT(ctx.delim_pos == 14); 1009 CX_TEST_ASSERT(ctx.found == 2); 1010 test_toupper(tok); 1011 1012 ret = cx_strtok_next_m(&ctx, &tok); 1013 CX_TEST_ASSERT(ret); 1014 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(tok), cx_str(""))); 1015 CX_TEST_ASSERT(ctx.pos == 15); 1016 CX_TEST_ASSERT(ctx.next_pos == 17); 1017 CX_TEST_ASSERT(ctx.delim_pos == 15); 1018 CX_TEST_ASSERT(ctx.found == 3); 1019 test_toupper(tok); 1020 1021 ret = cx_strtok_next_m(&ctx, &tok); 1022 CX_TEST_ASSERT(ret); 1023 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(tok), cx_str("separated"))); 1024 CX_TEST_ASSERT(ctx.pos == 17); 1025 CX_TEST_ASSERT(ctx.next_pos == 27); 1026 CX_TEST_ASSERT(ctx.delim_pos == 26); 1027 CX_TEST_ASSERT(ctx.found == 4); 1028 test_toupper(tok); 1029 1030 ret = cx_strtok_next_m(&ctx, &tok); 1031 CX_TEST_ASSERT(ret); 1032 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(tok), cx_str("string"))); 1033 CX_TEST_ASSERT(ctx.pos == 27); 1034 CX_TEST_ASSERT(ctx.next_pos == 33); 1035 CX_TEST_ASSERT(ctx.delim_pos == 33); 1036 CX_TEST_ASSERT(ctx.found == 5); 1037 test_toupper(tok); 1038 1039 ret = cx_strtok_next_m(&ctx, &tok); 1040 CX_TEST_ASSERT(!ret); 1041 CX_TEST_ASSERT(ctx.pos == 27); 1042 CX_TEST_ASSERT(ctx.next_pos == 33); 1043 CX_TEST_ASSERT(ctx.delim_pos == 33); 1044 CX_TEST_ASSERT(ctx.found == 5); 1045 1046 CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(str), cx_str("AN,ARBITRARILY;||SEPARATED;STRING"))); 1047 } 1048 cx_strfree(&str); 1049 } 1050 1051 #define test_strtoint_impl(suffix, num, base, var, min, max) \ 1052 do { \ 1053 errno = 0; \ 1054 int r = cx_strto##var(cx_str( #num), &var, base); \ 1055 if ((min) <= (num##suffix) && (num##suffix) <= (max)) { \ 1056 CX_TEST_ASSERTM(0 == r, "failed for "#num); \ 1057 CX_TEST_ASSERT(0 == errno); \ 1058 CX_TEST_ASSERT((num##suffix) == (0##suffix)+(var)); \ 1059 } else { \ 1060 CX_TEST_ASSERTM(0 != r, "out-of-range not detected for "#num " in variant "#var); \ 1061 CX_TEST_ASSERT(ERANGE == errno); \ 1062 } \ 1063 } while (0) 1064 1065 #define test_strtoint_rollout_signed_impl(num, base) \ 1066 test_strtoint_impl(LL, num, base, s, SHRT_MIN, SHRT_MAX); \ 1067 test_strtoint_impl(LL, num, base, i, INT_MIN, INT_MAX); \ 1068 test_strtoint_impl(LL, num, base, l, LONG_MIN, LONG_MAX); \ 1069 test_strtoint_impl(LL, num, base, ll, LLONG_MIN, LLONG_MAX); \ 1070 test_strtoint_impl(LL, num, base, i8, INT8_MIN, INT8_MAX); \ 1071 test_strtoint_impl(LL, num, base, i16, INT16_MIN, INT16_MAX); \ 1072 test_strtoint_impl(LL, num, base, i32, INT32_MIN, INT32_MAX); \ 1073 test_strtoint_impl(LL, num, base, i64, INT64_MIN, INT64_MAX); 1074 1075 #define test_strtoint_rollout_signed(num, base) \ 1076 test_strtoint_rollout_signed_impl(num, base); \ 1077 test_strtoint_rollout_signed_impl(-num, base) 1078 1079 #define test_strtoint_rollout(num, base) \ 1080 test_strtoint_impl(ULL, num, base, us, 0, USHRT_MAX); \ 1081 test_strtoint_impl(ULL, num, base, u, 0, UINT_MAX); \ 1082 test_strtoint_impl(ULL, num, base, ul, 0, ULONG_MAX); \ 1083 test_strtoint_impl(ULL, num, base, ull, 0, ULLONG_MAX); \ 1084 test_strtoint_impl(ULL, num, base, u8, 0, UINT8_MAX); \ 1085 test_strtoint_impl(ULL, num, base, u16, 0, UINT16_MAX); \ 1086 test_strtoint_impl(ULL, num, base, u32, 0, UINT32_MAX); \ 1087 test_strtoint_impl(ULL, num, base, u64, 0, UINT64_MAX); \ 1088 test_strtoint_impl(ULL, num, base, z, 0, SIZE_MAX) 1089 1090 CX_TEST(test_string_to_signed_integer) { 1091 short s; 1092 int i; 1093 long l; 1094 long long ll; 1095 int8_t i8; 1096 int16_t i16; 1097 int32_t i32; 1098 int64_t i64; 1099 CX_TEST_DO { 1100 // do some brute force tests with all ranges 1101 test_strtoint_rollout_signed(5, 10); 1102 test_strtoint_rollout_signed(47, 10); 1103 test_strtoint_rollout_signed(210, 10); 1104 test_strtoint_rollout_signed(5678, 10); 1105 test_strtoint_rollout_signed(40678, 10); 1106 test_strtoint_rollout_signed(1350266537, 10); 1107 test_strtoint_rollout_signed(3350266537, 10); 1108 test_strtoint_rollout_signed(473350266537, 10); 1109 test_strtoint_rollout_signed(057, 8); 1110 test_strtoint_rollout_signed(0322, 8); 1111 test_strtoint_rollout_signed(013056, 8); 1112 test_strtoint_rollout_signed(0117346, 8); 1113 test_strtoint_rollout_signed(012036667251, 8); 1114 test_strtoint_rollout_signed(030754201251, 8); 1115 test_strtoint_rollout_signed(06706567757251, 8); 1116 test_strtoint_rollout_signed(0767716340165362204025, 8); 1117 test_strtoint_rollout_signed(0x65, 16); 1118 test_strtoint_rollout_signed(0xf5, 16); 1119 test_strtoint_rollout_signed(0xABC5, 16); 1120 test_strtoint_rollout_signed(0xFBC5, 16); 1121 test_strtoint_rollout_signed(0x6df9CE03, 16); 1122 test_strtoint_rollout_signed(0xFdf9CE03, 16); 1123 test_strtoint_rollout_signed(0x6df9CE03AbC90815, 16); 1124 #if __STDC_VERSION__ >= 202300L 1125 test_strtoint_rollout_signed(0b01000010100100101110101001110101, 2); 1126 test_strtoint_rollout_signed(0b00011010101100001111111001010100, 2); 1127 test_strtoint_rollout_signed(0b10110001001001010001010111010011, 2); 1128 #endif 1129 1130 // do some special case tests 1131 // -------------------------- 1132 1133 // can fit only in unsigned long long 1134 errno = 0; 1135 CX_TEST_ASSERT(0 != cx_strtoll(cx_str("0x8df9CE03AbC90815"), &ll, 16)); 1136 CX_TEST_ASSERT(errno == ERANGE); 1137 1138 // negative overflow (we only test for 64 bit long long) 1139 #if LLONG_MAX == 9223372036854775807ll 1140 errno = 0; 1141 CX_TEST_ASSERT(0 == cx_strtoll(cx_str("-9223372036854775808"), &ll, 10)); 1142 CX_TEST_ASSERT(ll == LLONG_MIN); 1143 CX_TEST_ASSERT(errno == 0); 1144 CX_TEST_ASSERT(0 != cx_strtoll(cx_str("-9223372036854775809"), &ll, 10)); 1145 CX_TEST_ASSERT(errno == ERANGE); 1146 #endif 1147 1148 // edge case: empty and NULL string 1149 errno = 0; 1150 CX_TEST_ASSERT(0 != cx_strtoll(cx_str(""), &ll, 16)); 1151 CX_TEST_ASSERT(errno == EINVAL); 1152 errno = 0; 1153 CX_TEST_ASSERT(0 != cx_strtoll(cx_str(NULL), &ll, 16)); 1154 CX_TEST_ASSERT(errno == EINVAL); 1155 1156 // edge case: unsupported base 1157 errno = 0; 1158 CX_TEST_ASSERT(0 != cx_strtoll(cx_str("7"), &ll, 12)); 1159 CX_TEST_ASSERT(errno == EINVAL); 1160 1161 // edge case: incorrect sign characters 1162 errno = 0; 1163 CX_TEST_ASSERT(0 != cx_strtoll(cx_str("-"), &ll, 10)); 1164 CX_TEST_ASSERT(errno == EINVAL); 1165 errno = 0; 1166 CX_TEST_ASSERT(0 != cx_strtoll(cx_str("+"), &ll, 10)); 1167 CX_TEST_ASSERT(errno == EINVAL); 1168 errno = 0; 1169 CX_TEST_ASSERT(0 != cx_strtoll(cx_str("-+15"), &ll, 10)); 1170 CX_TEST_ASSERT(errno == EINVAL); 1171 errno = 0; 1172 CX_TEST_ASSERT(0 != cx_strtoll(cx_str("--15"), &ll, 10)); 1173 CX_TEST_ASSERT(errno == EINVAL); 1174 errno = 0; 1175 CX_TEST_ASSERT(0 != cx_strtoll(cx_str("+-15"), &ll, 10)); 1176 CX_TEST_ASSERT(errno == EINVAL); 1177 errno = 0; 1178 CX_TEST_ASSERT(0 != cx_strtoll(cx_str("++15"), &ll, 10)); 1179 CX_TEST_ASSERT(errno == EINVAL); 1180 1181 // edge case: only the sign bit is set 1182 errno = 0; 1183 CX_TEST_ASSERT(0 != cx_strtoi16(cx_str("0x8000"), &i16, 16)); 1184 CX_TEST_ASSERT(errno == ERANGE); 1185 errno = 0; 1186 CX_TEST_ASSERT(0 == cx_strtoi16(cx_str("-0x8000"), &i16, 16)); 1187 CX_TEST_ASSERT(errno == 0); 1188 CX_TEST_ASSERT(i16 == INT16_MIN); 1189 errno = 0; 1190 CX_TEST_ASSERT(0 != cx_strtoi64(cx_str("X8000000000000000"), &i64, 16)); 1191 CX_TEST_ASSERT(errno == ERANGE); 1192 errno = 0; 1193 CX_TEST_ASSERT(0 == cx_strtoi64(cx_str("-X8000000000000000"), &i64, 16)); 1194 CX_TEST_ASSERT(errno == 0); 1195 CX_TEST_ASSERT(i64 == INT64_MIN); 1196 1197 // group separators 1198 CX_TEST_ASSERT(0 == cx_strtoi32(cx_str("-123,456"), &i32, 10)); 1199 CX_TEST_ASSERT(i32 == -123456); 1200 errno = 0; 1201 CX_TEST_ASSERT(0 != cx_strtoi16_lc(cx_str("-Xab,cd"), &i16, 16, "''")); 1202 CX_TEST_ASSERT(errno == EINVAL); 1203 errno = 0; 1204 CX_TEST_ASSERT(0 != cx_strtoi16_lc(cx_str("-X''ab''cd"), &i16, 16, "''")); 1205 CX_TEST_ASSERT(errno == ERANGE); 1206 errno = 0; 1207 CX_TEST_ASSERT(0 == cx_strtoi16_lc(cx_str("-X''67''89"), &i16, 16, "''")); 1208 CX_TEST_ASSERT(errno == 0); 1209 CX_TEST_ASSERT(i16 == -0x6789); 1210 1211 // binary and (unusual notation of) signed binary 1212 errno = 0; 1213 CX_TEST_ASSERT(0 != cx_strtoi8_lc(cx_str("-1010 1011"), &i8, 2, " ")); 1214 CX_TEST_ASSERT(errno == ERANGE); 1215 errno = 0; 1216 CX_TEST_ASSERT(0 != cx_strtoi8_lc(cx_str("1010 1011"), &i8, 2, " ")); 1217 CX_TEST_ASSERT(errno == ERANGE); 1218 errno = 0; 1219 CX_TEST_ASSERT(0 == cx_strtoi8_lc(cx_str("-0101 0101"), &i8, 2, " ")); 1220 CX_TEST_ASSERT(errno == 0); 1221 CX_TEST_ASSERT(i8 == -0x55); 1222 } 1223 } 1224 1225 CX_TEST(test_string_to_unsigned_integer) { 1226 unsigned short us; 1227 unsigned int u; 1228 unsigned long ul; 1229 unsigned long long ull; 1230 uint8_t u8; 1231 uint16_t u16; 1232 uint32_t u32; 1233 uint64_t u64; 1234 size_t z; 1235 CX_TEST_DO { 1236 // do some brute force tests with all ranges 1237 test_strtoint_rollout(0, 10); 1238 test_strtoint_rollout(47, 10); 1239 test_strtoint_rollout(210, 10); 1240 test_strtoint_rollout(5678, 10); 1241 test_strtoint_rollout(40678, 10); 1242 test_strtoint_rollout(1350266537, 10); 1243 test_strtoint_rollout(3350266537, 10); 1244 test_strtoint_rollout(473350266537, 10); 1245 test_strtoint_rollout(0, 8); 1246 test_strtoint_rollout(057, 8); 1247 test_strtoint_rollout(0322, 8); 1248 test_strtoint_rollout(013056, 8); 1249 test_strtoint_rollout(0117346, 8); 1250 test_strtoint_rollout(012036667251, 8); 1251 test_strtoint_rollout(030754201251, 8); 1252 test_strtoint_rollout(06706567757251, 8); 1253 test_strtoint_rollout(01767716340165362204025, 8); 1254 test_strtoint_rollout(0x0, 16); 1255 test_strtoint_rollout(0, 16); 1256 test_strtoint_rollout(0x65, 16); 1257 test_strtoint_rollout(0xf5, 16); 1258 test_strtoint_rollout(0xABC5, 16); 1259 test_strtoint_rollout(0xFBC5, 16); 1260 test_strtoint_rollout(0x6df9CE03, 16); 1261 test_strtoint_rollout(0xFdf9CE03, 16); 1262 test_strtoint_rollout(0x6df9CE03AbC90815, 16); 1263 test_strtoint_rollout(0xfdf9CE03AbC90815, 16); 1264 #if __STDC_VERSION__ >= 202300L 1265 test_strtoint_rollout(0b0, 2); 1266 test_strtoint_rollout(0, 2); 1267 test_strtoint_rollout(0b01000010100100101110101001110101, 2); 1268 test_strtoint_rollout(0b00011010101100001111111001010100, 2); 1269 test_strtoint_rollout(0b10110001001001010001010111010011, 2); 1270 #endif 1271 1272 // do some special case tests 1273 // -------------------------- 1274 1275 // leading plus 1276 CX_TEST_ASSERT(0 == cx_strtou32(cx_str("+5"), &u32, 10)); 1277 CX_TEST_ASSERT(u32 == 5); 1278 1279 // group separators 1280 CX_TEST_ASSERT(0 == cx_strtou32(cx_str("123,456"), &u32, 10)); 1281 CX_TEST_ASSERT(u32 == 123456); 1282 errno = 0; 1283 CX_TEST_ASSERT(0 != cx_strtou16_lc(cx_str("ab,cd"), &u16, 16, "''")); 1284 CX_TEST_ASSERT(errno == EINVAL); 1285 errno = 0; 1286 CX_TEST_ASSERT(0 == cx_strtou16_lc(cx_str("ab''cd"), &u16, 16, "''")); 1287 CX_TEST_ASSERT(errno == 0); 1288 CX_TEST_ASSERT(u16 == 0xabcd); 1289 1290 // binary 1291 errno = 0; 1292 CX_TEST_ASSERT(0 != cx_strtou8_lc(cx_str("1 1010 1011"), &u8, 2, " ")); 1293 CX_TEST_ASSERT(errno == ERANGE); 1294 errno = 0; 1295 CX_TEST_ASSERT(0 == cx_strtou8_lc(cx_str("1010 1011"), &u8, 2, " ")); 1296 CX_TEST_ASSERT(errno == 0); 1297 CX_TEST_ASSERT(u8 == 0xAB); 1298 1299 // edge case: empty and NULL string 1300 errno = 0; 1301 CX_TEST_ASSERT(0 != cx_strtoull(cx_str(""), &ull, 16)); 1302 CX_TEST_ASSERT(errno == EINVAL); 1303 errno = 0; 1304 CX_TEST_ASSERT(0 != cx_strtoull(cx_str(NULL), &ull, 16)); 1305 CX_TEST_ASSERT(errno == EINVAL); 1306 1307 // edge case: unsupported base 1308 errno = 0; 1309 CX_TEST_ASSERT(0 != cx_strtoull(cx_str("7"), &ull, 12)); 1310 CX_TEST_ASSERT(errno == EINVAL); 1311 1312 // edge case: prefix only 1313 errno = 0; 1314 CX_TEST_ASSERT(0 != cx_strtoull(cx_str("0b"), &ull, 2)); 1315 CX_TEST_ASSERT(errno == EINVAL); 1316 errno = 0; 1317 CX_TEST_ASSERT(0 != cx_strtoull(cx_str("b"), &ull, 2)); 1318 CX_TEST_ASSERT(errno == EINVAL); 1319 errno = 0; 1320 CX_TEST_ASSERT(0 != cx_strtoull(cx_str("0x"), &ull, 16)); 1321 CX_TEST_ASSERT(errno == EINVAL); 1322 errno = 0; 1323 CX_TEST_ASSERT(0 != cx_strtoull(cx_str("x"), &ull, 16)); 1324 CX_TEST_ASSERT(errno == EINVAL); 1325 errno = 0; 1326 CX_TEST_ASSERT(0 != cx_strtoull(cx_str("#"), &ull, 16)); 1327 CX_TEST_ASSERT(errno == EINVAL); 1328 } 1329 } 1330 1331 CX_TEST(test_string_to_float) { 1332 float f; 1333 CX_TEST_DO { 1334 CX_TEST_ASSERT(0 == cx_strtof(cx_str("11.3"), &f)); 1335 CX_TEST_ASSERT(0 == cx_vcmp_float(11.3f, f)); 1336 1337 CX_TEST_ASSERT(0 == cx_strtof(cx_str("-4.711e+1"), &f)); 1338 CX_TEST_ASSERT(0 == cx_vcmp_float(-47.11f, f)); 1339 1340 CX_TEST_ASSERT(0 == cx_strtof(cx_str("1.67262192595e-27"), &f)); 1341 CX_TEST_ASSERT(0 == cx_vcmp_float(1.67262192595e-27f, f)); 1342 1343 CX_TEST_ASSERT(0 == cx_strtof_lc(cx_str("138,339.4"), &f, '.', ",")); 1344 CX_TEST_ASSERT(0 == cx_vcmp_float(138339.4f, f)); 1345 1346 CX_TEST_ASSERT(0 == cx_strtof_lc(cx_str("138,339.4"), &f, ',', ".")); 1347 CX_TEST_ASSERT(0 == cx_vcmp_float(138.3394f, f)); 1348 1349 errno = 0; 1350 CX_TEST_ASSERT(0 != cx_strtof(cx_str("15e"), &f)); 1351 CX_TEST_ASSERT(errno == EINVAL); 1352 errno = 0; 1353 CX_TEST_ASSERT(0 != cx_strtof(cx_str("15e+"), &f)); 1354 CX_TEST_ASSERT(errno == EINVAL); 1355 errno = 0; 1356 CX_TEST_ASSERT(0 != cx_strtof(cx_str("15e-"), &f)); 1357 CX_TEST_ASSERT(errno == EINVAL); 1358 CX_TEST_ASSERT(0 == cx_strtof(cx_str("15e-0"), &f)); 1359 CX_TEST_ASSERT(0 == cx_vcmp_float(15.f, f)); 1360 1361 CX_TEST_ASSERT(0 == cx_strtof(cx_str("3e38"), &f)); 1362 CX_TEST_ASSERT(0 == cx_vcmp_float(3e38f, f)); 1363 errno = 0; 1364 CX_TEST_ASSERT(0 != cx_strtof(cx_str("3e39"), &f)); 1365 CX_TEST_ASSERT(errno == ERANGE); 1366 CX_TEST_ASSERT(0 == cx_strtof(cx_str("-3e38"), &f)); 1367 CX_TEST_ASSERT(0 == cx_vcmp_float(-3e38f, f)); 1368 errno = 0; 1369 CX_TEST_ASSERT(0 != cx_strtof(cx_str("-3e39"), &f)); 1370 CX_TEST_ASSERT(errno == ERANGE); 1371 CX_TEST_ASSERT(0 == cx_strtof(cx_str("1.18e-38"), &f)); 1372 CX_TEST_ASSERT(0 == cx_vcmp_float(1.18e-38f, f)); 1373 errno = 0; 1374 CX_TEST_ASSERT(0 != cx_strtof(cx_str("1.17e-38"), &f)); 1375 CX_TEST_ASSERT(errno == ERANGE); 1376 } 1377 } 1378 1379 CX_TEST(test_string_to_double) { 1380 double d; 1381 CX_TEST_DO { 1382 CX_TEST_ASSERT(0 == cx_strtod(cx_str("11.3"), &d)); 1383 CX_TEST_ASSERT(0 == cx_vcmp_double(11.3, d)); 1384 1385 CX_TEST_ASSERT(0 == cx_strtod(cx_str("13."), &d)); 1386 CX_TEST_ASSERT(0 == cx_vcmp_double(13.0, d)); 1387 1388 CX_TEST_ASSERT(0 == cx_strtod(cx_str("47"), &d)); 1389 CX_TEST_ASSERT(0 == cx_vcmp_double(47.0, d)); 1390 1391 CX_TEST_ASSERT(0 == cx_strtod(cx_str("-13.37"), &d)); 1392 CX_TEST_ASSERT(0 == cx_vcmp_double(-13.37, d)); 1393 1394 CX_TEST_ASSERT(0 == cx_strtod(cx_str("-4.711e+1"), &d)); 1395 CX_TEST_ASSERT(0 == cx_vcmp_double(-47.11, d)); 1396 1397 CX_TEST_ASSERT(0 == cx_strtod(cx_str("1.67262192595e-27"), &d)); 1398 CX_TEST_ASSERT(0 == cx_vcmp_double(1.67262192595e-27, d)); 1399 1400 CX_TEST_ASSERT(0 == cx_strtod_lc(cx_str("138,339.4"), &d, '.', ",")); 1401 CX_TEST_ASSERT(0 == cx_vcmp_double(138339.4, d)); 1402 1403 CX_TEST_ASSERT(0 == cx_strtod_lc(cx_str("138,339.4"), &d, ',', ".")); 1404 CX_TEST_ASSERT(0 == cx_vcmp_double(138.3394, d)); 1405 1406 CX_TEST_ASSERT(0 == cx_strtod_lc(cx_str("13.37e04.7"), &d, ',', ".")); 1407 CX_TEST_ASSERT(0 == cx_vcmp_double(1337e47, d)); 1408 1409 d = 47.11; 1410 errno = 0; 1411 CX_TEST_ASSERT(0 != cx_strtod(cx_str(""), &d)); 1412 CX_TEST_ASSERT(errno == EINVAL); 1413 CX_TEST_ASSERT(d == 47.11); 1414 errno = 0; 1415 CX_TEST_ASSERT(0 != cx_strtod(cx_str(NULL), &d)); 1416 CX_TEST_ASSERT(errno == EINVAL); 1417 CX_TEST_ASSERT(d == 47.11); 1418 errno = 0; 1419 CX_TEST_ASSERT(0 != cx_strtod(cx_str("+"), &d)); 1420 CX_TEST_ASSERT(errno == EINVAL); 1421 CX_TEST_ASSERT(d == 47.11); 1422 errno = 0; 1423 CX_TEST_ASSERT(0 != cx_strtod(cx_str("-"), &d)); 1424 CX_TEST_ASSERT(errno == EINVAL); 1425 CX_TEST_ASSERT(d == 47.11); 1426 errno = 0; 1427 CX_TEST_ASSERT(0 != cx_strtod(cx_str("+-5"), &d)); 1428 CX_TEST_ASSERT(errno == EINVAL); 1429 CX_TEST_ASSERT(d == 47.11); 1430 errno = 0; 1431 CX_TEST_ASSERT(0 != cx_strtod(cx_str("-+5"), &d)); 1432 CX_TEST_ASSERT(errno == EINVAL); 1433 CX_TEST_ASSERT(d == 47.11); 1434 errno = 0; 1435 CX_TEST_ASSERT(0 != cx_strtod(cx_str("++5"), &d)); 1436 CX_TEST_ASSERT(errno == EINVAL); 1437 CX_TEST_ASSERT(d == 47.11); 1438 errno = 0; 1439 CX_TEST_ASSERT(0 != cx_strtod(cx_str("--5"), &d)); 1440 CX_TEST_ASSERT(errno == EINVAL); 1441 CX_TEST_ASSERT(d == 47.11); 1442 1443 errno = 0; 1444 CX_TEST_ASSERT(0 != cx_strtod_lc(cx_str("."), &d, '.', "''")); 1445 CX_TEST_ASSERT(errno == EINVAL); 1446 CX_TEST_ASSERT(d == 47.11); 1447 1448 errno = 0; 1449 CX_TEST_ASSERT(0 != cx_strtod(cx_str("19e0x5"), &d)); 1450 CX_TEST_ASSERT(errno == EINVAL); 1451 CX_TEST_ASSERT(d == 47.11); 1452 1453 // TODO: test and improve support for big numbers, precision, and out-of-range detection 1454 } 1455 } 1456 1457 CX_TEST(test_string_to_number_notrim) { 1458 long long i; 1459 unsigned long long u; 1460 float f; 1461 double d; 1462 CX_TEST_DO { 1463 CX_TEST_ASSERT(0 != cx_strtoll(cx_str("-42 "), &i, 10)); 1464 CX_TEST_ASSERT(0 != cx_strtoll(cx_str(" -42"), &i, 10)); 1465 CX_TEST_ASSERT(0 == cx_strtoll(cx_str("-42"), &i, 10)); 1466 CX_TEST_ASSERT(i == -42); 1467 1468 CX_TEST_ASSERT(0 != cx_strtoull(cx_str("42 "), &u, 10)); 1469 CX_TEST_ASSERT(0 != cx_strtoull(cx_str(" 42"), &u, 10)); 1470 CX_TEST_ASSERT(0 == cx_strtoull(cx_str("42"), &u, 10)); 1471 CX_TEST_ASSERT(u == 42); 1472 1473 CX_TEST_ASSERT(0 != cx_strtof(cx_str("13.37 "), &f)); 1474 CX_TEST_ASSERT(0 != cx_strtof(cx_str(" 13.37"), &f)); 1475 CX_TEST_ASSERT(0 == cx_strtof(cx_str("13.37"), &f)); 1476 CX_TEST_ASSERT(0 == cx_vcmp_float(f, 13.37f)); 1477 1478 CX_TEST_ASSERT(0 != cx_strtod(cx_str("13.37 "), &d)); 1479 CX_TEST_ASSERT(0 != cx_strtod(cx_str(" 13.37"), &d)); 1480 CX_TEST_ASSERT(0 == cx_strtod(cx_str("13.37"), &d)); 1481 CX_TEST_ASSERT(0 == cx_vcmp_double(d, 13.37)); 1482 } 1483 } 1484 1485 CX_TEST(test_strformat) { 1486 cxstring str = CX_STR("Hello, World!"); 1487 CX_TEST_DO { 1488 char actual[64]; 1489 snprintf(actual, 64, "Test %"CX_PRIstr " Success.", CX_SFMT(str)); 1490 CX_TEST_ASSERT(0 == strncmp("Test Hello, World! Success.", actual, 64)); 1491 } 1492 } 1493 1494 CxTestSuite *cx_test_suite_string(void) { 1495 CxTestSuite *suite = cx_test_suite_new("string"); 1496 1497 cx_test_register(suite, test_string_construct); 1498 cx_test_register(suite, test_string_cast); 1499 cx_test_register(suite, test_strfree); 1500 cx_test_register(suite, test_strdup); 1501 cx_test_register(suite, test_strdup_shortened); 1502 cx_test_register(suite, test_strcpy); 1503 cx_test_register(suite, test_strlen); 1504 cx_test_register(suite, test_strsubs); 1505 cx_test_register(suite, test_strchr); 1506 cx_test_register(suite, test_strrchr); 1507 cx_test_register(suite, test_strstr); 1508 cx_test_register(suite, test_strcmp); 1509 cx_test_register(suite, test_strcasecmp); 1510 cx_test_register(suite, test_strcat); 1511 cx_test_register(suite, test_strcat_more_than_eight); 1512 cx_test_register(suite, test_strsplit); 1513 cx_test_register(suite, test_strsplit_a); 1514 cx_test_register(suite, test_strtrim); 1515 cx_test_register(suite, test_strprefix); 1516 cx_test_register(suite, test_strsuffix); 1517 cx_test_register(suite, test_strcaseprefix); 1518 cx_test_register(suite, test_strcasesuffix); 1519 cx_test_register(suite, test_strreplace); 1520 cx_test_register(suite, test_strtok); 1521 cx_test_register(suite, test_strtok_delim); 1522 cx_test_register(suite, test_strtok_next_easy); 1523 cx_test_register(suite, test_strtok_next_unlimited); 1524 cx_test_register(suite, test_strtok_next_advanced); 1525 cx_test_register(suite, test_strformat); 1526 1527 return suite; 1528 } 1529 1530 CxTestSuite *cx_test_suite_string_to_number(void) { 1531 CxTestSuite *suite = cx_test_suite_new("string to number"); 1532 1533 cx_test_register(suite, test_string_to_signed_integer); 1534 cx_test_register(suite, test_string_to_unsigned_integer); 1535 cx_test_register(suite, test_string_to_float); 1536 cx_test_register(suite, test_string_to_double); 1537 cx_test_register(suite, test_string_to_number_notrim); 1538 1539 return suite; 1540 } 1541