migrate string tests - relates to #342

Thu, 28 Dec 2023 20:37:53 +0100

author
Mike Becker <universe@uap-core.de>
date
Thu, 28 Dec 2023 20:37:53 +0100
changeset 777
e5b29e6f0615
parent 776
874cc78cdffd
child 778
4a72bc4f09f4

migrate string tests - relates to #342

tests/Makefile file | annotate | diff | comparison | revisions
tests/test_string.c file | annotate | diff | comparison | revisions
tests/test_string.cpp file | annotate | diff | comparison | revisions
tests/ucxtest.c file | annotate | diff | comparison | revisions
     1.1 --- a/tests/Makefile	Thu Dec 28 19:17:45 2023 +0100
     1.2 +++ b/tests/Makefile	Thu Dec 28 20:37:53 2023 +0100
     1.3 @@ -23,11 +23,11 @@
     1.4  
     1.5  include ../config.mk
     1.6  
     1.7 -CFLAGS += -I../src
     1.8 +CFLAGS += -I../src -Wno-clobbered
     1.9  
    1.10  TEST_DIR=$(build_dir)/tests
    1.11  
    1.12 -SRC = util_allocator.c test_utils.c test_hash_key.c ucxtest.c
    1.13 +SRC = util_allocator.c test_utils.c test_hash_key.c test_string.c ucxtest.c
    1.14  
    1.15  OBJ_EXT=.o
    1.16  OBJ=$(SRC:%.c=$(TEST_DIR)/%$(OBJ_EXT))
    1.17 @@ -59,6 +59,10 @@
    1.18  	@echo "Compiling $<"
    1.19  	$(CC) -o $@ $(CFLAGS) -c $<
    1.20  
    1.21 +$(TEST_DIR)/test_string$(OBJ_EXT): test_string.c
    1.22 +	@echo "Compiling $<"
    1.23 +	$(CC) -o $@ $(CFLAGS) -c $<
    1.24 +
    1.25  $(TEST_DIR)/test_utils$(OBJ_EXT): test_utils.c ../src/cx/test.h \
    1.26   ../src/cx/utils.h ../src/cx/common.h ../src/cx/buffer.h \
    1.27   ../src/cx/allocator.h ../src/szmul.c
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/tests/test_string.c	Thu Dec 28 20:37:53 2023 +0100
     2.3 @@ -0,0 +1,986 @@
     2.4 +/*
     2.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     2.6 + *
     2.7 + * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved.
     2.8 + *
     2.9 + * Redistribution and use in source and binary forms, with or without
    2.10 + * modification, are permitted provided that the following conditions are met:
    2.11 + *
    2.12 + *   1. Redistributions of source code must retain the above copyright
    2.13 + *      notice, this list of conditions and the following disclaimer.
    2.14 + *
    2.15 + *   2. Redistributions in binary form must reproduce the above copyright
    2.16 + *      notice, this list of conditions and the following disclaimer in the
    2.17 + *      documentation and/or other materials provided with the distribution.
    2.18 + *
    2.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    2.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    2.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    2.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    2.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    2.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    2.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    2.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    2.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    2.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    2.29 + * POSSIBILITY OF SUCH DAMAGE.
    2.30 + */
    2.31 +
    2.32 +#include "cx/test.h"
    2.33 +#include "util_allocator.h"
    2.34 +
    2.35 +#include "cx/string.h"
    2.36 +
    2.37 +#define ASSERT_ZERO_TERMINATED(str) CX_TEST_ASSERTM((str).ptr[(str).length] == '\0', \
    2.38 +    #str " is not zero terminated")
    2.39 +
    2.40 +CX_TEST(test_string_construct) {
    2.41 +    cxstring s1 = CX_STR("1234");
    2.42 +    cxstring s2 = cx_strn("abcd", 2);
    2.43 +    cxmutstr s3 = cx_mutstr((char *) "1234");
    2.44 +    cxmutstr s4 = cx_mutstrn((char *) "abcd", 2);
    2.45 +    CX_TEST_DO {
    2.46 +        CX_TEST_ASSERT(s1.length == 4);
    2.47 +        CX_TEST_ASSERT(strncmp(s1.ptr, "1234", 4) == 0);
    2.48 +        CX_TEST_ASSERT(s2.length == 2);
    2.49 +        CX_TEST_ASSERT(strncmp(s2.ptr, "ab", 2) == 0);
    2.50 +        CX_TEST_ASSERT(s3.length == 4);
    2.51 +        CX_TEST_ASSERT(strncmp(s3.ptr, "1234", 4) == 0);
    2.52 +        CX_TEST_ASSERT(s4.length == 2);
    2.53 +        CX_TEST_ASSERT(strncmp(s4.ptr, "ab", 2) == 0);
    2.54 +    }
    2.55 +}
    2.56 +
    2.57 +CX_TEST(test_strfree) {
    2.58 +    CxTestingAllocator talloc;
    2.59 +    cx_testing_allocator_init(&talloc);
    2.60 +    CxAllocator *alloc = &talloc.base;
    2.61 +    CX_TEST_DO {
    2.62 +        char *test = cxMalloc(alloc, 16);
    2.63 +        cxmutstr str = cx_mutstrn(test, 16);
    2.64 +        CX_TEST_ASSERT(str.ptr == test);
    2.65 +        CX_TEST_ASSERT(str.length == 16);
    2.66 +        cx_strfree_a(alloc, &str);
    2.67 +        CX_TEST_ASSERT(str.ptr == NULL);
    2.68 +        CX_TEST_ASSERT(str.length == 0);
    2.69 +        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
    2.70 +    }
    2.71 +    cx_testing_allocator_destroy(&talloc);
    2.72 +}
    2.73 +
    2.74 +CX_TEST(test_strdup) {
    2.75 +    cxstring str = CX_STR("test");
    2.76 +    cxmutstr dup = cx_strdup(str);
    2.77 +    CX_TEST_DO {
    2.78 +        CX_TEST_ASSERT(dup.length == str.length);
    2.79 +        CX_TEST_ASSERT(0 == strcmp(dup.ptr, str.ptr));
    2.80 +        ASSERT_ZERO_TERMINATED(dup);
    2.81 +    }
    2.82 +    cx_strfree(&dup);
    2.83 +}
    2.84 +
    2.85 +CX_TEST(test_strdup_shortened) {
    2.86 +    cxstring str = CX_STR("test");
    2.87 +    str.length = 2;
    2.88 +    cxmutstr dup = cx_strdup(str);
    2.89 +    CX_TEST_DO {
    2.90 +        CX_TEST_ASSERT(dup.length == str.length);
    2.91 +        CX_TEST_ASSERT(0 == strcmp(dup.ptr, "te"));
    2.92 +        ASSERT_ZERO_TERMINATED(dup);
    2.93 +    }
    2.94 +    cx_strfree(&dup);
    2.95 +}
    2.96 +
    2.97 +CX_TEST(test_strlen) {
    2.98 +    cxstring s1 = CX_STR("1234");
    2.99 +    cxstring s2 = CX_STR(".:.:.");
   2.100 +    cxstring s3 = CX_STR("X");
   2.101 +    CX_TEST_DO {
   2.102 +        size_t len0 = cx_strlen(0);
   2.103 +        size_t len1 = cx_strlen(1, s1);
   2.104 +        size_t len2 = cx_strlen(2, s1, s2);
   2.105 +        size_t len3 = cx_strlen(3, s1, s2, s3);
   2.106 +
   2.107 +        CX_TEST_ASSERT(len0 == 0);
   2.108 +        CX_TEST_ASSERT(len1 == 4);
   2.109 +        CX_TEST_ASSERT(len2 == 9);
   2.110 +        CX_TEST_ASSERT(len3 == 10);
   2.111 +    }
   2.112 +}
   2.113 +
   2.114 +CX_TEST(test_strsubs) {
   2.115 +    cxstring str = CX_STR("A test string");
   2.116 +
   2.117 +    CX_TEST_DO {
   2.118 +        cxstring sub = cx_strsubs(str, 0);
   2.119 +        CX_TEST_ASSERT(0 == cx_strcmp(sub, str));
   2.120 +
   2.121 +        sub = cx_strsubs(str, 2);
   2.122 +        CX_TEST_ASSERT(0 == cx_strcmp(sub, CX_STR("test string")));
   2.123 +
   2.124 +        sub = cx_strsubs(str, 7);
   2.125 +        CX_TEST_ASSERT(0 == cx_strcmp(sub, CX_STR("string")));
   2.126 +
   2.127 +        sub = cx_strsubs(str, 15);
   2.128 +        CX_TEST_ASSERT(0 == cx_strcmp(sub, CX_STR("")));
   2.129 +
   2.130 +        sub = cx_strsubsl(str, 2, 4);
   2.131 +        CX_TEST_ASSERT(0 == cx_strcmp(sub, CX_STR("test")));
   2.132 +
   2.133 +        sub = cx_strsubsl(str, 7, 3);
   2.134 +        CX_TEST_ASSERT(0 == cx_strcmp(sub, CX_STR("str")));
   2.135 +
   2.136 +        sub = cx_strsubsl(str, 7, 20);
   2.137 +        CX_TEST_ASSERT(0 == cx_strcmp(sub, CX_STR("string")));
   2.138 +
   2.139 +        // just for coverage, call the _m variant
   2.140 +        cxmutstr m = cx_strsubs_m(cx_mutstrn(NULL, 0), 0);
   2.141 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(m), CX_STR("")));
   2.142 +    }
   2.143 +}
   2.144 +
   2.145 +CX_TEST(test_strchr) {
   2.146 +    cxstring str = CX_STR("I will find you - and I will kill you");
   2.147 +
   2.148 +    CX_TEST_DO {
   2.149 +        cxstring notfound = cx_strchr(str, 'x');
   2.150 +        CX_TEST_ASSERT(notfound.length == 0);
   2.151 +
   2.152 +        cxstring result = cx_strchr(str, 'w');
   2.153 +        CX_TEST_ASSERT(result.length == 35);
   2.154 +        CX_TEST_ASSERT(0 == strcmp(result.ptr, "will find you - and I will kill you"));
   2.155 +
   2.156 +        // just for coverage, call the _m variant
   2.157 +        cxmutstr m = cx_strchr_m(cx_mutstrn(NULL, 0), 'a');
   2.158 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(m), CX_STR("")));
   2.159 +    }
   2.160 +}
   2.161 +
   2.162 +CX_TEST(test_strrchr) {
   2.163 +    cxstring str = CX_STR("I will find you - and I will kill you");
   2.164 +
   2.165 +    CX_TEST_DO {
   2.166 +        cxstring notfound = cx_strrchr(str, 'x');
   2.167 +        CX_TEST_ASSERT(notfound.length == 0);
   2.168 +
   2.169 +        cxstring result = cx_strrchr(str, 'w');
   2.170 +        CX_TEST_ASSERT(result.length == 13);
   2.171 +        CX_TEST_ASSERT(0 == strcmp(result.ptr, "will kill you"));
   2.172 +
   2.173 +        // just for coverage, call the _m variant
   2.174 +        cxmutstr m = cx_strrchr_m(cx_mutstrn(NULL, 0), 'a');
   2.175 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(m), CX_STR("")));
   2.176 +    }
   2.177 +}
   2.178 +
   2.179 +CX_TEST(test_strstr) {
   2.180 +    cxstring str = CX_STR("find the match in this string");
   2.181 +    cxstring longstr = CX_STR(
   2.182 +            "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl"
   2.183 +            "mnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx"
   2.184 +            "yzabcdeababababnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij"
   2.185 +            "klmnopqrstuvwxyzaababababababababrstuvwxyzabcdefghijklmnopqrstuv"
   2.186 +            "abababababababababababababababababababababababababababababababab"
   2.187 +            "abababababababababababababababababababababababababababababababab"
   2.188 +            "abababababababababababababababababababababababababababababababab"
   2.189 +            "abababababababababababababababababababababababababababababababab"
   2.190 +            "abababababababababababababababababababababababababababababababab"
   2.191 +            "abababababababababababababababababababababababababababababababab"
   2.192 +            "wxyz1234567890");
   2.193 +    cxstring longstrpattern = CX_STR(
   2.194 +            "abababababababababababababababababababababababababababababababab"
   2.195 +            "abababababababababababababababababababababababababababababababab"
   2.196 +            "abababababababababababababababababababababababababababababababab"
   2.197 +            "abababababababababababababababababababababababababababababababab"
   2.198 +            "abababababababababababababababababababababababababababababababab"
   2.199 +    );
   2.200 +    cxstring longstrresult = CX_STR(
   2.201 +            "abababababababababababababababababababababababababababababababab"
   2.202 +            "abababababababababababababababababababababababababababababababab"
   2.203 +            "abababababababababababababababababababababababababababababababab"
   2.204 +            "abababababababababababababababababababababababababababababababab"
   2.205 +            "abababababababababababababababababababababababababababababababab"
   2.206 +            "abababababababababababababababababababababababababababababababab"
   2.207 +            "wxyz1234567890"
   2.208 +    );
   2.209 +
   2.210 +    CX_TEST_DO {
   2.211 +        cxstring notfound = cx_strstr(str, CX_STR("no match"));
   2.212 +        CX_TEST_ASSERT(notfound.length == 0);
   2.213 +
   2.214 +        cxstring result = cx_strstr(str, CX_STR("match"));
   2.215 +        CX_TEST_ASSERT(result.length == 20);
   2.216 +        CX_TEST_ASSERT(0 == strcmp(result.ptr, "match in this string"));
   2.217 +
   2.218 +        result = cx_strstr(str, CX_STR(""));
   2.219 +        CX_TEST_ASSERT(result.length == str.length);
   2.220 +        CX_TEST_ASSERT(0 == strcmp(result.ptr, str.ptr));
   2.221 +
   2.222 +        result = cx_strstr(longstr, longstrpattern);
   2.223 +        CX_TEST_ASSERT(result.length == longstrresult.length);
   2.224 +        CX_TEST_ASSERT(0 == strcmp(result.ptr, longstrresult.ptr));
   2.225 +
   2.226 +        // just for coverage, call the _m variant
   2.227 +        cxmutstr mstr = cx_strdup(longstr);
   2.228 +        cxmutstr m = cx_strstr_m(mstr, longstrpattern);
   2.229 +        CX_TEST_ASSERT(m.length == longstrresult.length);
   2.230 +        CX_TEST_ASSERT(0 == strcmp(m.ptr, longstrresult.ptr));
   2.231 +        cx_strfree(&mstr);
   2.232 +    }
   2.233 +}
   2.234 +
   2.235 +CX_TEST(test_strcmp) {
   2.236 +    cxstring str = CX_STR("compare this");
   2.237 +    CX_TEST_DO {
   2.238 +        CX_TEST_ASSERT(0 == cx_strcmp(CX_STR(""), CX_STR("")));
   2.239 +        CX_TEST_ASSERT(0 < cx_strcmp(str, CX_STR("")));
   2.240 +        CX_TEST_ASSERT(0 == cx_strcmp(str, CX_STR("compare this")));
   2.241 +        CX_TEST_ASSERT(0 != cx_strcmp(str, CX_STR("Compare This")));
   2.242 +        CX_TEST_ASSERT(0 > cx_strcmp(str, CX_STR("compare tool")));
   2.243 +        CX_TEST_ASSERT(0 < cx_strcmp(str, CX_STR("compare shit")));
   2.244 +        CX_TEST_ASSERT(0 > cx_strcmp(str, CX_STR("compare this not")));
   2.245 +        CX_TEST_ASSERT(0 < cx_strcmp(str, CX_STR("compare")));
   2.246 +
   2.247 +        cxstring str2 = CX_STR("Compare This");
   2.248 +        CX_TEST_ASSERT(0 != cx_strcmp_p(&str, &str2));
   2.249 +        str2 = CX_STR("compare this");
   2.250 +        CX_TEST_ASSERT(0 == cx_strcmp_p(&str, &str2));
   2.251 +    }
   2.252 +}
   2.253 +
   2.254 +CX_TEST(test_strcasecmp) {
   2.255 +    cxstring str = CX_STR("compare this");
   2.256 +    CX_TEST_DO {
   2.257 +        CX_TEST_ASSERT(0 == cx_strcasecmp(CX_STR(""), CX_STR("")));
   2.258 +        CX_TEST_ASSERT(0 < cx_strcasecmp(str, CX_STR("")));
   2.259 +        CX_TEST_ASSERT(0 == cx_strcasecmp(str, CX_STR("compare this")));
   2.260 +        CX_TEST_ASSERT(0 == cx_strcasecmp(str, CX_STR("Compare This")));
   2.261 +        CX_TEST_ASSERT(0 > cx_strcasecmp(str, CX_STR("compare tool")));
   2.262 +        CX_TEST_ASSERT(0 < cx_strcasecmp(str, CX_STR("compare shit")));
   2.263 +        CX_TEST_ASSERT(0 > cx_strcasecmp(str, CX_STR("compare this not")));
   2.264 +        CX_TEST_ASSERT(0 < cx_strcasecmp(str, CX_STR("compare")));
   2.265 +
   2.266 +        cxstring str2 = CX_STR("Compare This");
   2.267 +        CX_TEST_ASSERT(0 == cx_strcasecmp_p(&str, &str2));
   2.268 +        str2 = CX_STR("Compare Tool");
   2.269 +        CX_TEST_ASSERT(0 > cx_strcasecmp_p(&str, &str2));
   2.270 +    }
   2.271 +}
   2.272 +
   2.273 +CX_TEST(test_strcat) {
   2.274 +    cxstring s1 = CX_STR("12");
   2.275 +    cxstring s2 = CX_STR("34");
   2.276 +    cxstring s3 = CX_STR("56");
   2.277 +    cxstring sn = {NULL, 0};
   2.278 +
   2.279 +    CxTestingAllocator talloc;
   2.280 +    cx_testing_allocator_init(&talloc);
   2.281 +    CxAllocator *alloc = &talloc.base;
   2.282 +
   2.283 +    CX_TEST_DO {
   2.284 +        cxmutstr t1 = cx_strcat_a(alloc, 2, s1, s2);
   2.285 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t1), CX_STR("1234")));
   2.286 +        ASSERT_ZERO_TERMINATED(t1);
   2.287 +        cx_strfree_a(alloc, &t1);
   2.288 +
   2.289 +        cxmutstr t2 = cx_strcat_a(alloc, 3, s1, s2, s3);
   2.290 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t2), CX_STR("123456")));
   2.291 +        ASSERT_ZERO_TERMINATED(t2);
   2.292 +        cx_strfree_a(alloc, &t2);
   2.293 +
   2.294 +        cxmutstr t3 = cx_strcat_a(alloc, 6, s1, sn, s2, sn, s3, sn);
   2.295 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t3), CX_STR("123456")));
   2.296 +        ASSERT_ZERO_TERMINATED(t3);
   2.297 +        cx_strfree_a(alloc, &t3);
   2.298 +
   2.299 +        cxmutstr t4 = cx_strcat_a(alloc, 2, sn, sn);
   2.300 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t4), CX_STR("")));
   2.301 +        ASSERT_ZERO_TERMINATED(t4);
   2.302 +        cx_strfree_a(alloc, &t4);
   2.303 +
   2.304 +        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   2.305 +
   2.306 +        // use the macro
   2.307 +        cxmutstr t5 = cx_strcat(3, s3, s1, s2);
   2.308 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t5), CX_STR("561234")));
   2.309 +        ASSERT_ZERO_TERMINATED(t5);
   2.310 +        cx_strfree(&t5);
   2.311 +
   2.312 +        // use an initial string
   2.313 +        cxmutstr t6 = cx_strdup(CX_STR("Hello"));
   2.314 +        t6 = cx_strcat_m(t6, 2, CX_STR(", "), CX_STR("World!"));
   2.315 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(t6), CX_STR("Hello, World!")));
   2.316 +        ASSERT_ZERO_TERMINATED(t6);
   2.317 +        cx_strfree(&t6);
   2.318 +    }
   2.319 +    cx_testing_allocator_destroy(&talloc);
   2.320 +}
   2.321 +
   2.322 +CX_TEST(test_strsplit) {
   2.323 +    cxstring test = CX_STR("this,is,a,csv,string");
   2.324 +    size_t capa = 8;
   2.325 +    cxstring list[8];
   2.326 +    size_t n;
   2.327 +    CX_TEST_DO {
   2.328 +        // special case: empty string
   2.329 +        n = cx_strsplit(test, CX_STR(""), capa, list);
   2.330 +        CX_TEST_ASSERT(n == 1);
   2.331 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], test));
   2.332 +
   2.333 +        // no delimiter occurrence
   2.334 +        n = cx_strsplit(test, CX_STR("z"), capa, list);
   2.335 +        CX_TEST_ASSERT(n == 1);
   2.336 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], test));
   2.337 +
   2.338 +        // partially matching delimiter
   2.339 +        n = cx_strsplit(test, CX_STR("is,not"), capa, list);
   2.340 +        CX_TEST_ASSERT(n == 1);
   2.341 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], test));
   2.342 +
   2.343 +        // matching single-char delimiter
   2.344 +        n = cx_strsplit(test, CX_STR(","), capa, list);
   2.345 +        CX_TEST_ASSERT(n == 5);
   2.346 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("this")));
   2.347 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("is")));
   2.348 +        CX_TEST_ASSERT(0 == cx_strcmp(list[2], CX_STR("a")));
   2.349 +        CX_TEST_ASSERT(0 == cx_strcmp(list[3], CX_STR("csv")));
   2.350 +        CX_TEST_ASSERT(0 == cx_strcmp(list[4], CX_STR("string")));
   2.351 +
   2.352 +        // matching multi-char delimiter
   2.353 +        n = cx_strsplit(test, CX_STR("is"), capa, list);
   2.354 +        CX_TEST_ASSERT(n == 3);
   2.355 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("th")));
   2.356 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR(",")));
   2.357 +        CX_TEST_ASSERT(0 == cx_strcmp(list[2], CX_STR(",a,csv,string")));
   2.358 +
   2.359 +        // bounded list using single-char delimiter
   2.360 +        n = cx_strsplit(test, CX_STR(","), 3, list);
   2.361 +        CX_TEST_ASSERT(n == 3);
   2.362 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("this")));
   2.363 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("is")));
   2.364 +        CX_TEST_ASSERT(0 == cx_strcmp(list[2], CX_STR("a,csv,string")));
   2.365 +
   2.366 +        // bounded list using multi-char delimiter
   2.367 +        n = cx_strsplit(test, CX_STR("is"), 2, list);
   2.368 +        CX_TEST_ASSERT(n == 2);
   2.369 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("th")));
   2.370 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR(",is,a,csv,string")));
   2.371 +
   2.372 +        // start with delimiter
   2.373 +        n = cx_strsplit(test, CX_STR("this"), capa, list);
   2.374 +        CX_TEST_ASSERT(n == 2);
   2.375 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("")));
   2.376 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR(",is,a,csv,string")));
   2.377 +
   2.378 +        // end with delimiter
   2.379 +        n = cx_strsplit(test, CX_STR("string"), capa, list);
   2.380 +        CX_TEST_ASSERT(n == 2);
   2.381 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("this,is,a,csv,")));
   2.382 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("")));
   2.383 +
   2.384 +
   2.385 +        // end with delimiter exceed bound
   2.386 +        n = cx_strsplit(CX_STR("a,b,c,"), CX_STR(","), 3, list);
   2.387 +        CX_TEST_ASSERT(n == 3);
   2.388 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("a")));
   2.389 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("b")));
   2.390 +        CX_TEST_ASSERT(0 == cx_strcmp(list[2], CX_STR("c,")));
   2.391 +
   2.392 +        // exact match
   2.393 +        n = cx_strsplit(test, CX_STR("this,is,a,csv,string"), capa, list);
   2.394 +        CX_TEST_ASSERT(n == 2);
   2.395 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("")));
   2.396 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("")));
   2.397 +
   2.398 +        // string to be split is only substring
   2.399 +        n = cx_strsplit(test, CX_STR("this,is,a,csv,string,with,extension"), capa, list);
   2.400 +        CX_TEST_ASSERT(n == 1);
   2.401 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], test));
   2.402 +
   2.403 +        // subsequent encounter of delimiter (the string between is empty)
   2.404 +        n = cx_strsplit(test, CX_STR("is,"), capa, list);
   2.405 +        CX_TEST_ASSERT(n == 3);
   2.406 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("th")));
   2.407 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("")));
   2.408 +        CX_TEST_ASSERT(0 == cx_strcmp(list[2], CX_STR("a,csv,string")));
   2.409 +
   2.410 +        // call the _m variant just for coverage
   2.411 +        cxmutstr mtest = cx_strdup(test);
   2.412 +        cxmutstr mlist[4];
   2.413 +        n = cx_strsplit_m(mtest, CX_STR("is,"), 4, mlist);
   2.414 +        CX_TEST_ASSERT(n == 3);
   2.415 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[0]), CX_STR("th")));
   2.416 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[1]), CX_STR("")));
   2.417 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[2]), CX_STR("a,csv,string")));
   2.418 +        cx_strfree(&mtest);
   2.419 +    }
   2.420 +}
   2.421 +
   2.422 +CX_TEST(test_strsplit_a) {
   2.423 +    CxTestingAllocator talloc;
   2.424 +    cx_testing_allocator_init(&talloc);
   2.425 +    CxAllocator *alloc = &talloc.base;
   2.426 +
   2.427 +    cxstring test = CX_STR("this,is,a,csv,string");
   2.428 +    size_t capa = 8;
   2.429 +    cxstring *list;
   2.430 +    size_t n;
   2.431 +    CX_TEST_DO {
   2.432 +        // special case: empty string
   2.433 +        n = cx_strsplit_a(alloc, test, CX_STR(""), capa, &list);
   2.434 +        CX_TEST_ASSERT(n == 1);
   2.435 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], test));
   2.436 +        cxFree(alloc, list);
   2.437 +
   2.438 +        // no delimiter occurrence
   2.439 +        n = cx_strsplit_a(alloc, test, CX_STR("z"), capa, &list);
   2.440 +        CX_TEST_ASSERT(n == 1);
   2.441 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], test));
   2.442 +        cxFree(alloc, list);
   2.443 +
   2.444 +        // partially matching delimiter
   2.445 +        n = cx_strsplit_a(alloc, test, CX_STR("is,not"), capa, &list);
   2.446 +        CX_TEST_ASSERT(n == 1);
   2.447 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], test));
   2.448 +        cxFree(alloc, list);
   2.449 +
   2.450 +        // matching single-char delimiter
   2.451 +        n = cx_strsplit_a(alloc, test, CX_STR(","), capa, &list);
   2.452 +        CX_TEST_ASSERT(n == 5);
   2.453 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("this")));
   2.454 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("is")));
   2.455 +        CX_TEST_ASSERT(0 == cx_strcmp(list[2], CX_STR("a")));
   2.456 +        CX_TEST_ASSERT(0 == cx_strcmp(list[3], CX_STR("csv")));
   2.457 +        CX_TEST_ASSERT(0 == cx_strcmp(list[4], CX_STR("string")));
   2.458 +        cxFree(alloc, list);
   2.459 +
   2.460 +        // matching multi-char delimiter
   2.461 +        n = cx_strsplit_a(alloc, test, CX_STR("is"), capa, &list);
   2.462 +        CX_TEST_ASSERT(n == 3);
   2.463 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("th")));
   2.464 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR(",")));
   2.465 +        CX_TEST_ASSERT(0 == cx_strcmp(list[2], CX_STR(",a,csv,string")));
   2.466 +        cxFree(alloc, list);
   2.467 +
   2.468 +        // bounded list using single-char delimiter
   2.469 +        n = cx_strsplit_a(alloc, test, CX_STR(","), 3, &list);
   2.470 +        CX_TEST_ASSERT(n == 3);
   2.471 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("this")));
   2.472 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("is")));
   2.473 +        CX_TEST_ASSERT(0 == cx_strcmp(list[2], CX_STR("a,csv,string")));
   2.474 +        cxFree(alloc, list);
   2.475 +
   2.476 +        // bounded list using multi-char delimiter
   2.477 +        n = cx_strsplit_a(alloc, test, CX_STR("is"), 2, &list);
   2.478 +        CX_TEST_ASSERT(n == 2);
   2.479 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("th")));
   2.480 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR(",is,a,csv,string")));
   2.481 +        cxFree(alloc, list);
   2.482 +
   2.483 +        // start with delimiter
   2.484 +        n = cx_strsplit_a(alloc, test, CX_STR("this"), capa, &list);
   2.485 +        CX_TEST_ASSERT(n == 2);
   2.486 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("")));
   2.487 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR(",is,a,csv,string")));
   2.488 +        cxFree(alloc, list);
   2.489 +
   2.490 +        // end with delimiter
   2.491 +        n = cx_strsplit_a(alloc, test, CX_STR("string"), capa, &list);
   2.492 +        CX_TEST_ASSERT(n == 2);
   2.493 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("this,is,a,csv,")));
   2.494 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("")));
   2.495 +        cxFree(alloc, list);
   2.496 +
   2.497 +        // end with delimiter exceed bound
   2.498 +        n = cx_strsplit_a(alloc, CX_STR("a,b,c,"), CX_STR(","), 3, &list);
   2.499 +        CX_TEST_ASSERT(n == 3);
   2.500 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("a")));
   2.501 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("b")));
   2.502 +        CX_TEST_ASSERT(0 == cx_strcmp(list[2], CX_STR("c,")));
   2.503 +        cxFree(alloc, list);
   2.504 +
   2.505 +        // exact match
   2.506 +        n = cx_strsplit_a(alloc, test, CX_STR("this,is,a,csv,string"), capa, &list);
   2.507 +        CX_TEST_ASSERT(n == 2);
   2.508 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("")));
   2.509 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("")));
   2.510 +        cxFree(alloc, list);
   2.511 +
   2.512 +        // string to be split is only substring
   2.513 +        n = cx_strsplit_a(alloc, test, CX_STR("this,is,a,csv,string,with,extension"), capa, &list);
   2.514 +        CX_TEST_ASSERT(n == 1);
   2.515 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], test));
   2.516 +        cxFree(alloc, list);
   2.517 +
   2.518 +        // subsequent encounter of delimiter (the string between is empty)
   2.519 +        n = cx_strsplit_a(alloc, test, CX_STR("is,"), capa, &list);
   2.520 +        CX_TEST_ASSERT(n == 3);
   2.521 +        CX_TEST_ASSERT(0 == cx_strcmp(list[0], CX_STR("th")));
   2.522 +        CX_TEST_ASSERT(0 == cx_strcmp(list[1], CX_STR("")));
   2.523 +        CX_TEST_ASSERT(0 == cx_strcmp(list[2], CX_STR("a,csv,string")));
   2.524 +        cxFree(alloc, list);
   2.525 +
   2.526 +        // call the _m variant just for coverage
   2.527 +        cxmutstr mtest = cx_strdup(test);
   2.528 +        cxmutstr *mlist;
   2.529 +        n = cx_strsplit_ma(alloc, mtest, CX_STR("is,"), 4, &mlist);
   2.530 +        CX_TEST_ASSERT(n == 3);
   2.531 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[0]), CX_STR("th")));
   2.532 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[1]), CX_STR("")));
   2.533 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(mlist[2]), CX_STR("a,csv,string")));
   2.534 +        cxFree(alloc, mlist);
   2.535 +        cx_strfree(&mtest);
   2.536 +
   2.537 +        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   2.538 +    }
   2.539 +    cx_testing_allocator_destroy(&talloc);
   2.540 +}
   2.541 +
   2.542 +CX_TEST(test_strtrim) {
   2.543 +    cxstring t1 = cx_strtrim(CX_STR("  ein test  \t "));
   2.544 +    cxstring t2 = cx_strtrim(CX_STR("abc"));
   2.545 +    cxstring t3 = cx_strtrim(CX_STR(" 123"));
   2.546 +    cxstring t4 = cx_strtrim(CX_STR("xyz "));
   2.547 +    cxstring t5 = cx_strtrim(CX_STR("   "));
   2.548 +    cxstring empty = cx_strtrim(CX_STR(""));
   2.549 +
   2.550 +    CX_TEST_DO {
   2.551 +        CX_TEST_ASSERT(0 == cx_strcmp(t1, CX_STR("ein test")));
   2.552 +        CX_TEST_ASSERT(0 == cx_strcmp(t2, CX_STR("abc")));
   2.553 +        CX_TEST_ASSERT(0 == cx_strcmp(t3, CX_STR("123")));
   2.554 +        CX_TEST_ASSERT(0 == cx_strcmp(t4, CX_STR("xyz")));
   2.555 +        CX_TEST_ASSERT(0 == cx_strcmp(t5, CX_STR("")));
   2.556 +        CX_TEST_ASSERT(0 == cx_strcmp(empty, CX_STR("")));
   2.557 +
   2.558 +        // call the _m variant just for coverage
   2.559 +        cxmutstr m1 = cx_strtrim_m(cx_mutstr((char *) "  ein test  \t "));
   2.560 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(m1), CX_STR("ein test")));
   2.561 +    }
   2.562 +}
   2.563 +
   2.564 +CX_TEST(test_strprefix) {
   2.565 +    cxstring str = CX_STR("test my prefix and my suffix");
   2.566 +    cxstring empty = CX_STR("");
   2.567 +    CX_TEST_DO {
   2.568 +        CX_TEST_ASSERT(!cx_strprefix(empty, CX_STR("pref")));
   2.569 +        CX_TEST_ASSERT(cx_strprefix(str, empty));
   2.570 +        CX_TEST_ASSERT(cx_strprefix(empty, empty));
   2.571 +        CX_TEST_ASSERT(cx_strprefix(str, CX_STR("test ")));
   2.572 +        CX_TEST_ASSERT(!cx_strprefix(str, CX_STR("8-) fsck ")));
   2.573 +    }
   2.574 +}
   2.575 +
   2.576 +CX_TEST(test_strsuffix) {
   2.577 +    cxstring str = CX_STR("test my prefix and my suffix");
   2.578 +    cxstring empty = CX_STR("");
   2.579 +    CX_TEST_DO {
   2.580 +        CX_TEST_ASSERT(!cx_strsuffix(empty, CX_STR("suf")));
   2.581 +        CX_TEST_ASSERT(cx_strsuffix(str, empty));
   2.582 +        CX_TEST_ASSERT(cx_strsuffix(empty, empty));
   2.583 +        CX_TEST_ASSERT(cx_strsuffix(str, CX_STR("fix")));
   2.584 +        CX_TEST_ASSERT(!cx_strsuffix(str, CX_STR("fox")));
   2.585 +    }
   2.586 +}
   2.587 +
   2.588 +CX_TEST(test_strcaseprefix) {
   2.589 +    cxstring str = CX_STR("test my prefix and my suffix");
   2.590 +    cxstring empty = CX_STR("");
   2.591 +    CX_TEST_DO {
   2.592 +        CX_TEST_ASSERT(!cx_strcaseprefix(empty, CX_STR("pREf")));
   2.593 +        CX_TEST_ASSERT(cx_strcaseprefix(str, empty));
   2.594 +        CX_TEST_ASSERT(cx_strcaseprefix(empty, empty));
   2.595 +        CX_TEST_ASSERT(cx_strcaseprefix(str, CX_STR("TEST ")));
   2.596 +        CX_TEST_ASSERT(!cx_strcaseprefix(str, CX_STR("8-) fsck ")));
   2.597 +    }
   2.598 +}
   2.599 +
   2.600 +CX_TEST(test_strcasesuffix) {
   2.601 +    cxstring str = CX_STR("test my prefix and my suffix");
   2.602 +    cxstring empty = CX_STR("");
   2.603 +    CX_TEST_DO {
   2.604 +        CX_TEST_ASSERT(!cx_strcasesuffix(empty, CX_STR("sUf")));
   2.605 +        CX_TEST_ASSERT(cx_strcasesuffix(str, empty));
   2.606 +        CX_TEST_ASSERT(cx_strcasesuffix(empty, empty));
   2.607 +        CX_TEST_ASSERT(cx_strcasesuffix(str, CX_STR("FIX")));
   2.608 +        CX_TEST_ASSERT(!cx_strcasesuffix(str, CX_STR("fox")));
   2.609 +    }
   2.610 +}
   2.611 +
   2.612 +CX_TEST(test_strreplace) {
   2.613 +    CxTestingAllocator talloc;
   2.614 +    cx_testing_allocator_init(&talloc);
   2.615 +    CxAllocator *alloc = &talloc.base;
   2.616 +
   2.617 +    cxstring str = CX_STR("test ababab string aba");
   2.618 +    cxstring longstr = CX_STR(
   2.619 +            "xyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacd");
   2.620 +    cxstring notrail = CX_STR("test abab");
   2.621 +    cxstring empty = CX_STR("");
   2.622 +    cxstring astr = CX_STR("aaaaaaaaaa");
   2.623 +    cxstring csstr = CX_STR("test AB ab TEST xyz");
   2.624 +
   2.625 +    cxmutstr repl = cx_strreplace(str, CX_STR("abab"), CX_STR("muchlonger"));
   2.626 +    char const *expected = "test muchlongerab string aba";
   2.627 +
   2.628 +    cxmutstr repln = cx_strreplacen(str, CX_STR("ab"), CX_STR("c"), 2);
   2.629 +    char const *expectedn = "test ccab string aba";
   2.630 +
   2.631 +    cxmutstr longrepl = cx_strreplace(longstr, CX_STR("a"), CX_STR("z"));
   2.632 +    char const *longexpect = "xyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzcd";
   2.633 +
   2.634 +    cxmutstr replnotrail = cx_strreplace(notrail, CX_STR("ab"), CX_STR("z"));
   2.635 +    char const *notrailexpect = "test zz";
   2.636 +
   2.637 +    cxmutstr repleq = cx_strreplace(str, str, CX_STR("hello"));
   2.638 +    char const *eqexpect = "hello";
   2.639 +
   2.640 +    cxmutstr replempty1 = cx_strreplace(empty, CX_STR("ab"), CX_STR("c")); // expect: empty
   2.641 +    cxmutstr replempty2 = cx_strreplace(str, CX_STR("abab"), empty);
   2.642 +    char const *emptyexpect2 = "test ab string aba";
   2.643 +
   2.644 +    cxmutstr replpre = cx_strreplace(str, CX_STR("test "), CX_STR("TEST "));
   2.645 +    char const *preexpected = "TEST ababab string aba";
   2.646 +
   2.647 +    cxmutstr replan1 = cx_strreplacen(astr, CX_STR("a"), CX_STR("x"), 1);
   2.648 +    char const *an1expected = "xaaaaaaaaa";
   2.649 +
   2.650 +    cxmutstr replan4 = cx_strreplacen(astr, CX_STR("a"), CX_STR("x"), 4);
   2.651 +    char const *an4expected = "xxxxaaaaaa";
   2.652 +
   2.653 +    cxmutstr replan9 = cx_strreplacen(astr, CX_STR("a"), CX_STR("x"), 9);
   2.654 +    char const *an9expected = "xxxxxxxxxa";
   2.655 +
   2.656 +    cxmutstr replan10 = cx_strreplacen(astr, CX_STR("a"), CX_STR("x"), 10);
   2.657 +    char const *an10expected = "xxxxxxxxxx";
   2.658 +
   2.659 +    CX_TEST_DO {
   2.660 +        cxmutstr repl1_a = cx_strreplace_a(alloc, csstr, CX_STR("AB"), CX_STR("*"));
   2.661 +        char const *expeced1_a = "test * ab TEST xyz";
   2.662 +
   2.663 +        cxmutstr repl2_a = cx_strreplace_a(alloc, csstr, CX_STR("test"), CX_STR("TEST"));
   2.664 +        char const *expected2_a = "TEST AB ab TEST xyz";
   2.665 +
   2.666 +        CX_TEST_ASSERT(repl.ptr != str.ptr);
   2.667 +        ASSERT_ZERO_TERMINATED(repl);
   2.668 +        CX_TEST_ASSERT(0 == strcmp(repl.ptr, expected));
   2.669 +        ASSERT_ZERO_TERMINATED(repln);
   2.670 +        CX_TEST_ASSERT(0 == strcmp(repln.ptr, expectedn));
   2.671 +        ASSERT_ZERO_TERMINATED(longrepl);
   2.672 +        CX_TEST_ASSERT(0 == strcmp(longrepl.ptr, longexpect));
   2.673 +        ASSERT_ZERO_TERMINATED(replnotrail);
   2.674 +        CX_TEST_ASSERT(0 == strcmp(replnotrail.ptr, notrailexpect));
   2.675 +        ASSERT_ZERO_TERMINATED(repleq);
   2.676 +        CX_TEST_ASSERT(0 == strcmp(repleq.ptr, eqexpect));
   2.677 +        ASSERT_ZERO_TERMINATED(replempty1);
   2.678 +        CX_TEST_ASSERT(0 == strcmp(replempty1.ptr, ""));
   2.679 +        ASSERT_ZERO_TERMINATED(replempty2);
   2.680 +        CX_TEST_ASSERT(0 == strcmp(replempty2.ptr, emptyexpect2));
   2.681 +        ASSERT_ZERO_TERMINATED(replpre);
   2.682 +        CX_TEST_ASSERT(0 == strcmp(replpre.ptr, preexpected));
   2.683 +        ASSERT_ZERO_TERMINATED(replan1);
   2.684 +        CX_TEST_ASSERT(0 == strcmp(replan1.ptr, an1expected));
   2.685 +        ASSERT_ZERO_TERMINATED(replan4);
   2.686 +        CX_TEST_ASSERT(0 == strcmp(replan4.ptr, an4expected));
   2.687 +        ASSERT_ZERO_TERMINATED(replan9);
   2.688 +        CX_TEST_ASSERT(0 == strcmp(replan9.ptr, an9expected));
   2.689 +        ASSERT_ZERO_TERMINATED(replan10);
   2.690 +        CX_TEST_ASSERT(0 == strcmp(replan10.ptr, an10expected));
   2.691 +        ASSERT_ZERO_TERMINATED(repl1_a);
   2.692 +        CX_TEST_ASSERT(0 == strcmp(repl1_a.ptr, expeced1_a));
   2.693 +        ASSERT_ZERO_TERMINATED(repl2_a);
   2.694 +        CX_TEST_ASSERT(0 == strcmp(repl2_a.ptr, expected2_a));
   2.695 +
   2.696 +        cx_strfree_a(alloc, &repl1_a);
   2.697 +        cx_strfree_a(alloc, &repl2_a);
   2.698 +        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   2.699 +    }
   2.700 +
   2.701 +    cx_strfree(&repl);
   2.702 +    cx_strfree(&repln);
   2.703 +    cx_strfree(&longrepl);
   2.704 +    cx_strfree(&replnotrail);
   2.705 +    cx_strfree(&repleq);
   2.706 +    cx_strfree(&replempty1);
   2.707 +    cx_strfree(&replempty2);
   2.708 +    cx_strfree(&replpre);
   2.709 +    cx_strfree(&replan1);
   2.710 +    cx_strfree(&replan4);
   2.711 +    cx_strfree(&replan9);
   2.712 +    cx_strfree(&replan10);
   2.713 +    cx_testing_allocator_destroy(&talloc);
   2.714 +}
   2.715 +
   2.716 +CX_TEST(test_strupper) {
   2.717 +    cxmutstr str = cx_strdup(CX_STR("thIs 1s @ Te$t"));
   2.718 +    CX_TEST_DO {
   2.719 +        cx_strupper(str);
   2.720 +        CX_TEST_ASSERT(0 == strcmp(str.ptr, "THIS 1S @ TE$T"));
   2.721 +    }
   2.722 +    cx_strfree(&str);
   2.723 +}
   2.724 +
   2.725 +CX_TEST(test_strlower) {
   2.726 +    cxmutstr str = cx_strdup(CX_STR("thIs 1s @ Te$t"));
   2.727 +    CX_TEST_DO {
   2.728 +        cx_strlower(str);
   2.729 +        CX_TEST_ASSERT(0 == strcmp(str.ptr, "this 1s @ te$t"));
   2.730 +    }
   2.731 +    cx_strfree(&str);
   2.732 +}
   2.733 +
   2.734 +CX_TEST(test_strtok) {
   2.735 +    cxstring str = CX_STR("a,comma,separated,string");
   2.736 +    cxstring delim = CX_STR(",");
   2.737 +    CX_TEST_DO {
   2.738 +        CxStrtokCtx ctx = cx_strtok(str, delim, 3);
   2.739 +        CX_TEST_ASSERT(ctx.str.ptr == str.ptr);
   2.740 +        CX_TEST_ASSERT(ctx.str.length == str.length);
   2.741 +        CX_TEST_ASSERT(ctx.delim.ptr == delim.ptr);
   2.742 +        CX_TEST_ASSERT(ctx.delim.length == delim.length);
   2.743 +        CX_TEST_ASSERT(ctx.limit == 3);
   2.744 +        CX_TEST_ASSERT(ctx.found == 0);
   2.745 +        CX_TEST_ASSERT(ctx.pos == 0);
   2.746 +        CX_TEST_ASSERT(ctx.next_pos == 0);
   2.747 +        CX_TEST_ASSERT(ctx.delim_more == NULL);
   2.748 +        CX_TEST_ASSERT(ctx.delim_more_count == 0);
   2.749 +    }
   2.750 +}
   2.751 +
   2.752 +CX_TEST(test_strtok_m) {
   2.753 +    cxmutstr str = cx_strdup(CX_STR("a,comma,separated,string"));
   2.754 +    cxstring delim = CX_STR(",");
   2.755 +    CX_TEST_DO {
   2.756 +        CxStrtokCtx ctx = cx_strtok_m(str, delim, 3);
   2.757 +        CX_TEST_ASSERT(ctx.str.ptr == str.ptr);
   2.758 +        CX_TEST_ASSERT(ctx.str.length == str.length);
   2.759 +        CX_TEST_ASSERT(ctx.delim.ptr == delim.ptr);
   2.760 +        CX_TEST_ASSERT(ctx.delim.length == delim.length);
   2.761 +        CX_TEST_ASSERT(ctx.limit == 3);
   2.762 +        CX_TEST_ASSERT(ctx.found == 0);
   2.763 +        CX_TEST_ASSERT(ctx.pos == 0);
   2.764 +        CX_TEST_ASSERT(ctx.next_pos == 0);
   2.765 +        CX_TEST_ASSERT(ctx.delim_more == NULL);
   2.766 +        CX_TEST_ASSERT(ctx.delim_more_count == 0);
   2.767 +    }
   2.768 +    cx_strfree(&str);
   2.769 +}
   2.770 +
   2.771 +CX_TEST(test_strtok_delim) {
   2.772 +    cxstring str = CX_STR("an,arbitrarily|separated;string");
   2.773 +    cxstring delim = CX_STR(",");
   2.774 +    cxstring delim_more[2] = {CX_STR("|"), CX_STR(";")};
   2.775 +    CX_TEST_DO {
   2.776 +        CxStrtokCtx ctx = cx_strtok(str, delim, 3);
   2.777 +        cx_strtok_delim(&ctx, delim_more, 2);
   2.778 +        CX_TEST_ASSERT(ctx.str.ptr == str.ptr);
   2.779 +        CX_TEST_ASSERT(ctx.str.length == str.length);
   2.780 +        CX_TEST_ASSERT(ctx.delim.ptr == delim.ptr);
   2.781 +        CX_TEST_ASSERT(ctx.delim.length == delim.length);
   2.782 +        CX_TEST_ASSERT(ctx.limit == 3);
   2.783 +        CX_TEST_ASSERT(ctx.found == 0);
   2.784 +        CX_TEST_ASSERT(ctx.pos == 0);
   2.785 +        CX_TEST_ASSERT(ctx.next_pos == 0);
   2.786 +        CX_TEST_ASSERT(ctx.delim_more == delim_more);
   2.787 +        CX_TEST_ASSERT(ctx.delim_more_count == 2);
   2.788 +    }
   2.789 +}
   2.790 +
   2.791 +CX_TEST(test_strtok_next_easy) {
   2.792 +    cxstring str = CX_STR("a,comma,separated,string");
   2.793 +    cxstring delim = CX_STR(",");
   2.794 +    CX_TEST_DO {
   2.795 +        CxStrtokCtx ctx = cx_strtok(str, delim, 3);
   2.796 +        bool ret;
   2.797 +        cxstring tok;
   2.798 +
   2.799 +        ret = cx_strtok_next(&ctx, &tok);
   2.800 +        CX_TEST_ASSERT(ret);
   2.801 +        CX_TEST_ASSERT(0 == cx_strcmp(tok, CX_STR("a")));
   2.802 +        CX_TEST_ASSERT(ctx.pos == 0);
   2.803 +        CX_TEST_ASSERT(ctx.next_pos == 2);
   2.804 +        CX_TEST_ASSERT(ctx.delim_pos == 1);
   2.805 +        CX_TEST_ASSERT(ctx.found == 1);
   2.806 +
   2.807 +        ret = cx_strtok_next(&ctx, &tok);
   2.808 +        CX_TEST_ASSERT(ret);
   2.809 +        CX_TEST_ASSERT(0 == cx_strcmp(tok, CX_STR("comma")));
   2.810 +        CX_TEST_ASSERT(ctx.pos == 2);
   2.811 +        CX_TEST_ASSERT(ctx.next_pos == 8);
   2.812 +        CX_TEST_ASSERT(ctx.delim_pos == 7);
   2.813 +        CX_TEST_ASSERT(ctx.found == 2);
   2.814 +
   2.815 +        ret = cx_strtok_next(&ctx, &tok);
   2.816 +        CX_TEST_ASSERT(ret);
   2.817 +        CX_TEST_ASSERT(0 == cx_strcmp(tok, CX_STR("separated")));
   2.818 +        CX_TEST_ASSERT(ctx.pos == 8);
   2.819 +        CX_TEST_ASSERT(ctx.next_pos == 18);
   2.820 +        CX_TEST_ASSERT(ctx.delim_pos == 17);
   2.821 +        CX_TEST_ASSERT(ctx.found == 3);
   2.822 +
   2.823 +        ret = cx_strtok_next(&ctx, &tok);
   2.824 +        CX_TEST_ASSERT(!ret);
   2.825 +        CX_TEST_ASSERT(ctx.pos == 8);
   2.826 +        CX_TEST_ASSERT(ctx.next_pos == 18);
   2.827 +        CX_TEST_ASSERT(ctx.delim_pos == 17);
   2.828 +        CX_TEST_ASSERT(ctx.found == 3);
   2.829 +    }
   2.830 +}
   2.831 +
   2.832 +CX_TEST(test_strtok_next_unlimited) {
   2.833 +    cxstring str = CX_STR("some;-;otherwise;-;separated;-;string;-;");
   2.834 +    cxstring delim = CX_STR(";-;");
   2.835 +    CX_TEST_DO {
   2.836 +        CxStrtokCtx ctx = cx_strtok(str, delim, SIZE_MAX);
   2.837 +        bool ret;
   2.838 +        cxstring tok;
   2.839 +
   2.840 +        ret = cx_strtok_next(&ctx, &tok);
   2.841 +        CX_TEST_ASSERT(ret);
   2.842 +        CX_TEST_ASSERT(0 == cx_strcmp(tok, CX_STR("some")));
   2.843 +        CX_TEST_ASSERT(ctx.pos == 0);
   2.844 +        CX_TEST_ASSERT(ctx.next_pos == 7);
   2.845 +        CX_TEST_ASSERT(ctx.delim_pos == 4);
   2.846 +        CX_TEST_ASSERT(ctx.found == 1);
   2.847 +
   2.848 +        ret = cx_strtok_next(&ctx, &tok);
   2.849 +        CX_TEST_ASSERT(ret);
   2.850 +        CX_TEST_ASSERT(0 == cx_strcmp(tok, CX_STR("otherwise")));
   2.851 +        CX_TEST_ASSERT(ctx.pos == 7);
   2.852 +        CX_TEST_ASSERT(ctx.next_pos == 19);
   2.853 +        CX_TEST_ASSERT(ctx.delim_pos == 16);
   2.854 +        CX_TEST_ASSERT(ctx.found == 2);
   2.855 +
   2.856 +        ret = cx_strtok_next(&ctx, &tok);
   2.857 +        CX_TEST_ASSERT(ret);
   2.858 +        CX_TEST_ASSERT(0 == cx_strcmp(tok, CX_STR("separated")));
   2.859 +        CX_TEST_ASSERT(ctx.pos == 19);
   2.860 +        CX_TEST_ASSERT(ctx.next_pos == 31);
   2.861 +        CX_TEST_ASSERT(ctx.delim_pos == 28);
   2.862 +        CX_TEST_ASSERT(ctx.found == 3);
   2.863 +
   2.864 +        ret = cx_strtok_next(&ctx, &tok);
   2.865 +        CX_TEST_ASSERT(ret);
   2.866 +        CX_TEST_ASSERT(0 == cx_strcmp(tok, CX_STR("string")));
   2.867 +        CX_TEST_ASSERT(ctx.pos == 31);
   2.868 +        CX_TEST_ASSERT(ctx.next_pos == 40);
   2.869 +        CX_TEST_ASSERT(ctx.delim_pos == 37);
   2.870 +        CX_TEST_ASSERT(ctx.found == 4);
   2.871 +
   2.872 +        ret = cx_strtok_next(&ctx, &tok);
   2.873 +        CX_TEST_ASSERT(ret);
   2.874 +        CX_TEST_ASSERT(0 == cx_strcmp(tok, CX_STR("")));
   2.875 +        CX_TEST_ASSERT(ctx.pos == 40);
   2.876 +        CX_TEST_ASSERT(ctx.next_pos == 40);
   2.877 +        CX_TEST_ASSERT(ctx.delim_pos == 40);
   2.878 +        CX_TEST_ASSERT(ctx.found == 5);
   2.879 +
   2.880 +        ret = cx_strtok_next(&ctx, &tok);
   2.881 +        CX_TEST_ASSERT(!ret);
   2.882 +        CX_TEST_ASSERT(ctx.pos == 40);
   2.883 +        CX_TEST_ASSERT(ctx.delim_pos == 40);
   2.884 +        CX_TEST_ASSERT(ctx.found == 5);
   2.885 +    }
   2.886 +}
   2.887 +
   2.888 +CX_TEST(test_strtok_next_advanced) {
   2.889 +    cxmutstr str = cx_strdup(CX_STR("an,arbitrarily;||separated;string"));
   2.890 +    cxstring delim = CX_STR(",");
   2.891 +    cxstring delim_more[2] = {CX_STR("||"), CX_STR(";")};
   2.892 +    CX_TEST_DO {
   2.893 +        CxStrtokCtx ctx = cx_strtok_m(str, delim, 10);
   2.894 +        cx_strtok_delim(&ctx, delim_more, 2);
   2.895 +        bool ret;
   2.896 +        cxmutstr tok;
   2.897 +
   2.898 +        ret = cx_strtok_next_m(&ctx, &tok);
   2.899 +        CX_TEST_ASSERT(ret);
   2.900 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(tok), CX_STR("an")));
   2.901 +        CX_TEST_ASSERT(ctx.pos == 0);
   2.902 +        CX_TEST_ASSERT(ctx.next_pos == 3);
   2.903 +        CX_TEST_ASSERT(ctx.delim_pos == 2);
   2.904 +        CX_TEST_ASSERT(ctx.found == 1);
   2.905 +        cx_strupper(tok);
   2.906 +
   2.907 +        ret = cx_strtok_next_m(&ctx, &tok);
   2.908 +        CX_TEST_ASSERT(ret);
   2.909 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(tok), CX_STR("arbitrarily")));
   2.910 +        CX_TEST_ASSERT(ctx.pos == 3);
   2.911 +        CX_TEST_ASSERT(ctx.next_pos == 15);
   2.912 +        CX_TEST_ASSERT(ctx.delim_pos == 14);
   2.913 +        CX_TEST_ASSERT(ctx.found == 2);
   2.914 +        cx_strupper(tok);
   2.915 +
   2.916 +        ret = cx_strtok_next_m(&ctx, &tok);
   2.917 +        CX_TEST_ASSERT(ret);
   2.918 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(tok), CX_STR("")));
   2.919 +        CX_TEST_ASSERT(ctx.pos == 15);
   2.920 +        CX_TEST_ASSERT(ctx.next_pos == 17);
   2.921 +        CX_TEST_ASSERT(ctx.delim_pos == 15);
   2.922 +        CX_TEST_ASSERT(ctx.found == 3);
   2.923 +        cx_strupper(tok);
   2.924 +
   2.925 +        ret = cx_strtok_next_m(&ctx, &tok);
   2.926 +        CX_TEST_ASSERT(ret);
   2.927 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(tok), CX_STR("separated")));
   2.928 +        CX_TEST_ASSERT(ctx.pos == 17);
   2.929 +        CX_TEST_ASSERT(ctx.next_pos == 27);
   2.930 +        CX_TEST_ASSERT(ctx.delim_pos == 26);
   2.931 +        CX_TEST_ASSERT(ctx.found == 4);
   2.932 +        cx_strupper(tok);
   2.933 +
   2.934 +        ret = cx_strtok_next_m(&ctx, &tok);
   2.935 +        CX_TEST_ASSERT(ret);
   2.936 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(tok), CX_STR("string")));
   2.937 +        CX_TEST_ASSERT(ctx.pos == 27);
   2.938 +        CX_TEST_ASSERT(ctx.next_pos == 33);
   2.939 +        CX_TEST_ASSERT(ctx.delim_pos == 33);
   2.940 +        CX_TEST_ASSERT(ctx.found == 5);
   2.941 +        cx_strupper(tok);
   2.942 +
   2.943 +        ret = cx_strtok_next_m(&ctx, &tok);
   2.944 +        CX_TEST_ASSERT(!ret);
   2.945 +        CX_TEST_ASSERT(ctx.pos == 27);
   2.946 +        CX_TEST_ASSERT(ctx.next_pos == 33);
   2.947 +        CX_TEST_ASSERT(ctx.delim_pos == 33);
   2.948 +        CX_TEST_ASSERT(ctx.found == 5);
   2.949 +
   2.950 +        CX_TEST_ASSERT(0 == cx_strcmp(cx_strcast(str), CX_STR("AN,ARBITRARILY;||SEPARATED;STRING")));
   2.951 +    }
   2.952 +    cx_strfree(&str);
   2.953 +}
   2.954 +
   2.955 +
   2.956 +CxTestSuite *cx_test_suite_string(void) {
   2.957 +    CxTestSuite *suite = cx_test_suite_new("string");
   2.958 +
   2.959 +    cx_test_register(suite, test_string_construct);
   2.960 +    cx_test_register(suite, test_strfree);
   2.961 +    cx_test_register(suite, test_strdup);
   2.962 +    cx_test_register(suite, test_strdup_shortened);
   2.963 +    cx_test_register(suite, test_strlen);
   2.964 +    cx_test_register(suite, test_strsubs);
   2.965 +    cx_test_register(suite, test_strchr);
   2.966 +    cx_test_register(suite, test_strrchr);
   2.967 +    cx_test_register(suite, test_strstr);
   2.968 +    cx_test_register(suite, test_strcmp);
   2.969 +    cx_test_register(suite, test_strcasecmp);
   2.970 +    cx_test_register(suite, test_strcat);
   2.971 +    cx_test_register(suite, test_strsplit);
   2.972 +    cx_test_register(suite, test_strsplit_a);
   2.973 +    cx_test_register(suite, test_strtrim);
   2.974 +    cx_test_register(suite, test_strprefix);
   2.975 +    cx_test_register(suite, test_strsuffix);
   2.976 +    cx_test_register(suite, test_strcaseprefix);
   2.977 +    cx_test_register(suite, test_strcasesuffix);
   2.978 +    cx_test_register(suite, test_strreplace);
   2.979 +    cx_test_register(suite, test_strupper);
   2.980 +    cx_test_register(suite, test_strlower);
   2.981 +    cx_test_register(suite, test_strtok);
   2.982 +    cx_test_register(suite, test_strtok_m);
   2.983 +    cx_test_register(suite, test_strtok_delim);
   2.984 +    cx_test_register(suite, test_strtok_next_easy);
   2.985 +    cx_test_register(suite, test_strtok_next_unlimited);
   2.986 +    cx_test_register(suite, test_strtok_next_advanced);
   2.987 +
   2.988 +    return suite;
   2.989 +}
     3.1 --- a/tests/test_string.cpp	Thu Dec 28 19:17:45 2023 +0100
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,882 +0,0 @@
     3.4 -/*
     3.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 - *
     3.7 - * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
     3.8 - *
     3.9 - * Redistribution and use in source and binary forms, with or without
    3.10 - * modification, are permitted provided that the following conditions are met:
    3.11 - *
    3.12 - *   1. Redistributions of source code must retain the above copyright
    3.13 - *      notice, this list of conditions and the following disclaimer.
    3.14 - *
    3.15 - *   2. Redistributions in binary form must reproduce the above copyright
    3.16 - *      notice, this list of conditions and the following disclaimer in the
    3.17 - *      documentation and/or other materials provided with the distribution.
    3.18 - *
    3.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    3.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    3.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    3.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    3.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    3.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    3.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    3.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    3.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    3.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    3.29 - * POSSIBILITY OF SUCH DAMAGE.
    3.30 - */
    3.31 -
    3.32 -#include "cx/string.h"
    3.33 -#include "util_allocator.h"
    3.34 -
    3.35 -#include <gtest/gtest.h>
    3.36 -
    3.37 -#define EXPECT_ZERO_TERMINATED(str) EXPECT_EQ((str).ptr[(str).length], '\0')
    3.38 -
    3.39 -TEST(String, construct) {
    3.40 -    cxstring s1 = CX_STR("1234");
    3.41 -    cxstring s2 = cx_strn("abcd", 2);
    3.42 -    cxmutstr s3 = cx_mutstr((char *) "1234");
    3.43 -    cxmutstr s4 = cx_mutstrn((char *) "abcd", 2);
    3.44 -
    3.45 -    EXPECT_EQ(s1.length, 4);
    3.46 -    EXPECT_EQ(s2.length, 2);
    3.47 -    EXPECT_EQ(s3.length, 4);
    3.48 -    EXPECT_EQ(s4.length, 2);
    3.49 -}
    3.50 -
    3.51 -TEST(String, strfree) {
    3.52 -    CxTestingAllocator alloc;
    3.53 -    auto test = (char *) cxMalloc(&alloc, 16);
    3.54 -    cxmutstr str = cx_mutstrn(test, 16);
    3.55 -    ASSERT_EQ(str.ptr, test);
    3.56 -    EXPECT_EQ(str.length, 16);
    3.57 -    cx_strfree_a(&alloc, &str);
    3.58 -    EXPECT_EQ(str.ptr, nullptr);
    3.59 -    EXPECT_EQ(str.length, 0);
    3.60 -    EXPECT_TRUE(alloc.verify());
    3.61 -}
    3.62 -
    3.63 -TEST(String, strdup) {
    3.64 -    cxstring str = CX_STR("test");
    3.65 -    cxmutstr dup = cx_strdup(str);
    3.66 -    ASSERT_EQ(dup.length, str.length);
    3.67 -    EXPECT_STREQ(dup.ptr, str.ptr);
    3.68 -    EXPECT_ZERO_TERMINATED(dup);
    3.69 -    cx_strfree(&dup);
    3.70 -
    3.71 -    str.length = 2;
    3.72 -    dup = cx_strdup(str);
    3.73 -    ASSERT_EQ(dup.length, str.length);
    3.74 -    EXPECT_STREQ(dup.ptr, "te");
    3.75 -    EXPECT_ZERO_TERMINATED(dup);
    3.76 -    cx_strfree(&dup);
    3.77 -}
    3.78 -
    3.79 -TEST(String, strlen) {
    3.80 -    cxstring s1 = CX_STR("1234");
    3.81 -    cxstring s2 = CX_STR(".:.:.");
    3.82 -    cxstring s3 = CX_STR("X");
    3.83 -
    3.84 -    size_t len0 = cx_strlen(0);
    3.85 -    size_t len1 = cx_strlen(1, s1);
    3.86 -    size_t len2 = cx_strlen(2, s1, s2);
    3.87 -    size_t len3 = cx_strlen(3, s1, s2, s3);
    3.88 -
    3.89 -    EXPECT_EQ(len0, 0);
    3.90 -    EXPECT_EQ(len1, 4);
    3.91 -    EXPECT_EQ(len2, 9);
    3.92 -    EXPECT_EQ(len3, 10);
    3.93 -}
    3.94 -
    3.95 -TEST(String, strsubs) {
    3.96 -    cxstring str = CX_STR("A test string");
    3.97 -
    3.98 -    cxstring sub = cx_strsubs(str, 0);
    3.99 -    EXPECT_EQ(cx_strcmp(sub, str), 0);
   3.100 -
   3.101 -    sub = cx_strsubs(str, 2);
   3.102 -    EXPECT_EQ(cx_strcmp(sub, CX_STR("test string")), 0);
   3.103 -
   3.104 -    sub = cx_strsubs(str, 7);
   3.105 -    EXPECT_EQ(cx_strcmp(sub, CX_STR("string")), 0);
   3.106 -
   3.107 -    sub = cx_strsubs(str, 15);
   3.108 -    EXPECT_EQ(cx_strcmp(sub, CX_STR("")), 0);
   3.109 -
   3.110 -    sub = cx_strsubsl(str, 2, 4);
   3.111 -    EXPECT_EQ(cx_strcmp(sub, CX_STR("test")), 0);
   3.112 -
   3.113 -    sub = cx_strsubsl(str, 7, 3);
   3.114 -    EXPECT_EQ(cx_strcmp(sub, CX_STR("str")), 0);
   3.115 -
   3.116 -    sub = cx_strsubsl(str, 7, 20);
   3.117 -    EXPECT_EQ(cx_strcmp(sub, CX_STR("string")), 0);
   3.118 -
   3.119 -    // just for coverage, call the _m variant
   3.120 -    auto m = cx_strsubs_m(cx_mutstrn(nullptr, 0), 0);
   3.121 -    EXPECT_EQ(cx_strcmp(cx_strcast(m), CX_STR("")), 0);
   3.122 -}
   3.123 -
   3.124 -TEST(String, strchr) {
   3.125 -    cxstring str = CX_STR("I will find you - and I will kill you");
   3.126 -
   3.127 -    cxstring notfound = cx_strchr(str, 'x');
   3.128 -    EXPECT_EQ(notfound.length, 0);
   3.129 -
   3.130 -    cxstring result = cx_strchr(str, 'w');
   3.131 -    EXPECT_EQ(result.length, 35);
   3.132 -    EXPECT_STREQ(result.ptr, "will find you - and I will kill you");
   3.133 -
   3.134 -    // just for coverage, call the _m variant
   3.135 -    auto m = cx_strchr_m(cx_mutstrn(nullptr, 0), 'a');
   3.136 -    EXPECT_EQ(cx_strcmp(cx_strcast(m), CX_STR("")), 0);
   3.137 -}
   3.138 -
   3.139 -TEST(String, strrchr) {
   3.140 -    cxstring str = CX_STR("I will find you - and I will kill you");
   3.141 -
   3.142 -    cxstring notfound = cx_strrchr(str, 'x');
   3.143 -    EXPECT_EQ(notfound.length, 0);
   3.144 -
   3.145 -    cxstring result = cx_strrchr(str, 'w');
   3.146 -    EXPECT_EQ(result.length, 13);
   3.147 -    EXPECT_STREQ(result.ptr, "will kill you");
   3.148 -
   3.149 -    // just for coverage, call the _m variant
   3.150 -    auto m = cx_strrchr_m(cx_mutstrn(nullptr, 0), 'a');
   3.151 -    EXPECT_EQ(cx_strcmp(cx_strcast(m), CX_STR("")), 0);
   3.152 -}
   3.153 -
   3.154 -TEST(String, strstr) {
   3.155 -    cxstring str = CX_STR("find the match in this string");
   3.156 -    cxstring longstr = CX_STR(
   3.157 -            "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl"
   3.158 -            "mnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx"
   3.159 -            "yzabcdeababababnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij"
   3.160 -            "klmnopqrstuvwxyzaababababababababrstuvwxyzabcdefghijklmnopqrstuv"
   3.161 -            "abababababababababababababababababababababababababababababababab"
   3.162 -            "abababababababababababababababababababababababababababababababab"
   3.163 -            "abababababababababababababababababababababababababababababababab"
   3.164 -            "abababababababababababababababababababababababababababababababab"
   3.165 -            "abababababababababababababababababababababababababababababababab"
   3.166 -            "abababababababababababababababababababababababababababababababab"
   3.167 -            "wxyz1234567890");
   3.168 -    cxstring longstrpattern = CX_STR(
   3.169 -            "abababababababababababababababababababababababababababababababab"
   3.170 -            "abababababababababababababababababababababababababababababababab"
   3.171 -            "abababababababababababababababababababababababababababababababab"
   3.172 -            "abababababababababababababababababababababababababababababababab"
   3.173 -            "abababababababababababababababababababababababababababababababab"
   3.174 -    );
   3.175 -    cxstring longstrresult = CX_STR(
   3.176 -            "abababababababababababababababababababababababababababababababab"
   3.177 -            "abababababababababababababababababababababababababababababababab"
   3.178 -            "abababababababababababababababababababababababababababababababab"
   3.179 -            "abababababababababababababababababababababababababababababababab"
   3.180 -            "abababababababababababababababababababababababababababababababab"
   3.181 -            "abababababababababababababababababababababababababababababababab"
   3.182 -            "wxyz1234567890"
   3.183 -    );
   3.184 -
   3.185 -    cxstring notfound = cx_strstr(str, CX_STR("no match"));
   3.186 -    EXPECT_EQ(notfound.length, 0);
   3.187 -
   3.188 -    cxstring result = cx_strstr(str, CX_STR("match"));
   3.189 -    EXPECT_EQ(result.length, 20);
   3.190 -    EXPECT_STREQ(result.ptr, "match in this string");
   3.191 -
   3.192 -    result = cx_strstr(str, CX_STR(""));
   3.193 -    EXPECT_EQ(result.length, str.length);
   3.194 -    EXPECT_STREQ(result.ptr, str.ptr);
   3.195 -
   3.196 -    result = cx_strstr(longstr, longstrpattern);
   3.197 -    EXPECT_EQ(result.length, longstrresult.length);
   3.198 -    EXPECT_STREQ(result.ptr, longstrresult.ptr);
   3.199 -
   3.200 -    // just for coverage, call the _m variant
   3.201 -    auto mstr = cx_strdup(longstr);
   3.202 -    auto m = cx_strstr_m(mstr, longstrpattern);
   3.203 -    EXPECT_EQ(m.length, longstrresult.length);
   3.204 -    EXPECT_STREQ(m.ptr, longstrresult.ptr);
   3.205 -    cx_strfree(&mstr);
   3.206 -}
   3.207 -
   3.208 -TEST(String, strcmp) {
   3.209 -    cxstring str = CX_STR("compare this");
   3.210 -
   3.211 -    EXPECT_EQ(cx_strcmp(CX_STR(""), CX_STR("")), 0);
   3.212 -    EXPECT_GT(cx_strcmp(str, CX_STR("")), 0);
   3.213 -    EXPECT_EQ(cx_strcmp(str, CX_STR("compare this")), 0);
   3.214 -    EXPECT_NE(cx_strcmp(str, CX_STR("Compare This")), 0);
   3.215 -    EXPECT_LT(cx_strcmp(str, CX_STR("compare tool")), 0);
   3.216 -    EXPECT_GT(cx_strcmp(str, CX_STR("compare shit")), 0);
   3.217 -    EXPECT_LT(cx_strcmp(str, CX_STR("compare this not")), 0);
   3.218 -    EXPECT_GT(cx_strcmp(str, CX_STR("compare")), 0);
   3.219 -
   3.220 -    cxstring str2 = CX_STR("Compare This");
   3.221 -    EXPECT_NE(cx_strcmp_p(&str, &str2), 0);
   3.222 -    str2 = CX_STR("compare this");
   3.223 -    EXPECT_EQ(cx_strcmp_p(&str, &str2), 0);
   3.224 -}
   3.225 -
   3.226 -TEST(String, strcasecmp) {
   3.227 -    cxstring str = CX_STR("compare this");
   3.228 -
   3.229 -    EXPECT_EQ(cx_strcasecmp(CX_STR(""), CX_STR("")), 0);
   3.230 -    EXPECT_GT(cx_strcasecmp(str, CX_STR("")), 0);
   3.231 -    EXPECT_EQ(cx_strcasecmp(str, CX_STR("compare this")), 0);
   3.232 -    EXPECT_EQ(cx_strcasecmp(str, CX_STR("Compare This")), 0);
   3.233 -    EXPECT_LT(cx_strcasecmp(str, CX_STR("compare tool")), 0);
   3.234 -    EXPECT_GT(cx_strcasecmp(str, CX_STR("compare shit")), 0);
   3.235 -    EXPECT_LT(cx_strcasecmp(str, CX_STR("compare this not")), 0);
   3.236 -    EXPECT_GT(cx_strcasecmp(str, CX_STR("compare")), 0);
   3.237 -
   3.238 -    cxstring str2 = CX_STR("Compare This");
   3.239 -    EXPECT_EQ(cx_strcasecmp_p(&str, &str2), 0);
   3.240 -    str2 = CX_STR("Compare Tool");
   3.241 -    EXPECT_LT(cx_strcasecmp_p(&str, &str2), 0);
   3.242 -}
   3.243 -
   3.244 -TEST(String, strcat) {
   3.245 -    cxstring s1 = CX_STR("12");
   3.246 -    cxstring s2 = CX_STR("34");
   3.247 -    cxstring s3 = CX_STR("56");
   3.248 -    cxstring sn = {nullptr, 0};
   3.249 -
   3.250 -    CxTestingAllocator alloc;
   3.251 -
   3.252 -    cxmutstr t1 = cx_strcat_a(&alloc, 2, s1, s2);
   3.253 -    EXPECT_EQ(cx_strcmp(cx_strcast(t1), CX_STR("1234")), 0);
   3.254 -    EXPECT_ZERO_TERMINATED(t1);
   3.255 -    cx_strfree_a(&alloc, &t1);
   3.256 -
   3.257 -    cxmutstr t2 = cx_strcat_a(&alloc, 3, s1, s2, s3);
   3.258 -    EXPECT_EQ(cx_strcmp(cx_strcast(t2), CX_STR("123456")), 0);
   3.259 -    EXPECT_ZERO_TERMINATED(t2);
   3.260 -    cx_strfree_a(&alloc, &t2);
   3.261 -
   3.262 -    cxmutstr t3 = cx_strcat_a(&alloc, 6, s1, sn, s2, sn, s3, sn);
   3.263 -    EXPECT_EQ(cx_strcmp(cx_strcast(t3), CX_STR("123456")), 0);
   3.264 -    EXPECT_ZERO_TERMINATED(t3);
   3.265 -    cx_strfree_a(&alloc, &t3);
   3.266 -
   3.267 -    cxmutstr t4 = cx_strcat_a(&alloc, 2, sn, sn);
   3.268 -    EXPECT_EQ(cx_strcmp(cx_strcast(t4), CX_STR("")), 0);
   3.269 -    EXPECT_ZERO_TERMINATED(t4);
   3.270 -    cx_strfree_a(&alloc, &t4);
   3.271 -
   3.272 -    EXPECT_TRUE(alloc.verify());
   3.273 -
   3.274 -    // use the macro
   3.275 -    cxmutstr t5 = cx_strcat(3, s3, s1, s2);
   3.276 -    EXPECT_EQ(cx_strcmp(cx_strcast(t5), CX_STR("561234")), 0);
   3.277 -    EXPECT_ZERO_TERMINATED(t5);
   3.278 -    cx_strfree(&t5);
   3.279 -
   3.280 -    // use an initial string
   3.281 -    cxmutstr t6 = cx_strdup(CX_STR("Hello"));
   3.282 -    t6 = cx_strcat_m(t6, 2, CX_STR(", "), CX_STR("World!"));
   3.283 -    EXPECT_EQ(cx_strcmp(cx_strcast(t6), CX_STR("Hello, World!")), 0);
   3.284 -    EXPECT_ZERO_TERMINATED(t6);
   3.285 -    cx_strfree(&t6);
   3.286 -}
   3.287 -
   3.288 -TEST(String, strsplit) {
   3.289 -
   3.290 -    cxstring test = CX_STR("this,is,a,csv,string");
   3.291 -    size_t capa = 8;
   3.292 -    cxstring list[8];
   3.293 -    size_t n;
   3.294 -
   3.295 -    // special case: empty string
   3.296 -    n = cx_strsplit(test, CX_STR(""), capa, list);
   3.297 -    ASSERT_EQ(n, 1);
   3.298 -    EXPECT_EQ(cx_strcmp(list[0], test), 0);
   3.299 -
   3.300 -    // no delimiter occurrence
   3.301 -    n = cx_strsplit(test, CX_STR("z"), capa, list);
   3.302 -    ASSERT_EQ(n, 1);
   3.303 -    EXPECT_EQ(cx_strcmp(list[0], test), 0);
   3.304 -
   3.305 -    // partially matching delimiter
   3.306 -    n = cx_strsplit(test, CX_STR("is,not"), capa, list);
   3.307 -    ASSERT_EQ(n, 1);
   3.308 -    EXPECT_EQ(cx_strcmp(list[0], test), 0);
   3.309 -
   3.310 -    // matching single-char delimiter
   3.311 -    n = cx_strsplit(test, CX_STR(","), capa, list);
   3.312 -    ASSERT_EQ(n, 5);
   3.313 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("this")), 0);
   3.314 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("is")), 0);
   3.315 -    EXPECT_EQ(cx_strcmp(list[2], CX_STR("a")), 0);
   3.316 -    EXPECT_EQ(cx_strcmp(list[3], CX_STR("csv")), 0);
   3.317 -    EXPECT_EQ(cx_strcmp(list[4], CX_STR("string")), 0);
   3.318 -
   3.319 -    // matching multi-char delimiter
   3.320 -    n = cx_strsplit(test, CX_STR("is"), capa, list);
   3.321 -    ASSERT_EQ(n, 3);
   3.322 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("th")), 0);
   3.323 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR(",")), 0);
   3.324 -    EXPECT_EQ(cx_strcmp(list[2], CX_STR(",a,csv,string")), 0);
   3.325 -
   3.326 -    // bounded list using single-char delimiter
   3.327 -    n = cx_strsplit(test, CX_STR(","), 3, list);
   3.328 -    ASSERT_EQ(n, 3);
   3.329 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("this")), 0);
   3.330 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("is")), 0);
   3.331 -    EXPECT_EQ(cx_strcmp(list[2], CX_STR("a,csv,string")), 0);
   3.332 -
   3.333 -    // bounded list using multi-char delimiter
   3.334 -    n = cx_strsplit(test, CX_STR("is"), 2, list);
   3.335 -    ASSERT_EQ(n, 2);
   3.336 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("th")), 0);
   3.337 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR(",is,a,csv,string")), 0);
   3.338 -
   3.339 -    // start with delimiter
   3.340 -    n = cx_strsplit(test, CX_STR("this"), capa, list);
   3.341 -    ASSERT_EQ(n, 2);
   3.342 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("")), 0);
   3.343 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR(",is,a,csv,string")), 0);
   3.344 -
   3.345 -    // end with delimiter
   3.346 -    n = cx_strsplit(test, CX_STR("string"), capa, list);
   3.347 -    ASSERT_EQ(n, 2);
   3.348 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("this,is,a,csv,")), 0);
   3.349 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("")), 0);
   3.350 -
   3.351 -
   3.352 -    // end with delimiter exceed bound
   3.353 -    n = cx_strsplit(CX_STR("a,b,c,"), CX_STR(","), 3, list);
   3.354 -    ASSERT_EQ(n, 3);
   3.355 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("a")), 0);
   3.356 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("b")), 0);
   3.357 -    EXPECT_EQ(cx_strcmp(list[2], CX_STR("c,")), 0);
   3.358 -
   3.359 -    // exact match
   3.360 -    n = cx_strsplit(test, CX_STR("this,is,a,csv,string"), capa, list);
   3.361 -    ASSERT_EQ(n, 2);
   3.362 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("")), 0);
   3.363 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("")), 0);
   3.364 -
   3.365 -    // string to be split is only substring
   3.366 -    n = cx_strsplit(test, CX_STR("this,is,a,csv,string,with,extension"), capa, list);
   3.367 -    ASSERT_EQ(n, 1);
   3.368 -    EXPECT_EQ(cx_strcmp(list[0], test), 0);
   3.369 -
   3.370 -    // subsequent encounter of delimiter (the string between is empty)
   3.371 -    n = cx_strsplit(test, CX_STR("is,"), capa, list);
   3.372 -    ASSERT_EQ(n, 3);
   3.373 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("th")), 0);
   3.374 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("")), 0);
   3.375 -    EXPECT_EQ(cx_strcmp(list[2], CX_STR("a,csv,string")), 0);
   3.376 -
   3.377 -    // call the _m variant just for coverage
   3.378 -    auto mtest = cx_strdup(test);
   3.379 -    cxmutstr mlist[4];
   3.380 -    n = cx_strsplit_m(mtest, CX_STR("is,"), 4, mlist);
   3.381 -    ASSERT_EQ(n, 3);
   3.382 -    EXPECT_EQ(cx_strcmp(cx_strcast(mlist[0]), CX_STR("th")), 0);
   3.383 -    EXPECT_EQ(cx_strcmp(cx_strcast(mlist[1]), CX_STR("")), 0);
   3.384 -    EXPECT_EQ(cx_strcmp(cx_strcast(mlist[2]), CX_STR("a,csv,string")), 0);
   3.385 -    cx_strfree(&mtest);
   3.386 -}
   3.387 -
   3.388 -TEST(String, strsplit_a) {
   3.389 -    CxTestingAllocator alloc;
   3.390 -
   3.391 -    cxstring test = CX_STR("this,is,a,csv,string");
   3.392 -    size_t capa = 8;
   3.393 -    cxstring *list;
   3.394 -    size_t n;
   3.395 -
   3.396 -    // special case: empty string
   3.397 -    n = cx_strsplit_a(&alloc, test, CX_STR(""), capa, &list);
   3.398 -    ASSERT_EQ(n, 1);
   3.399 -    EXPECT_EQ(cx_strcmp(list[0], test), 0);
   3.400 -    cxFree(&alloc, list);
   3.401 -
   3.402 -    // no delimiter occurrence
   3.403 -    n = cx_strsplit_a(&alloc, test, CX_STR("z"), capa, &list);
   3.404 -    ASSERT_EQ(n, 1);
   3.405 -    EXPECT_EQ(cx_strcmp(list[0], test), 0);
   3.406 -    cxFree(&alloc, list);
   3.407 -
   3.408 -    // partially matching delimiter
   3.409 -    n = cx_strsplit_a(&alloc, test, CX_STR("is,not"), capa, &list);
   3.410 -    ASSERT_EQ(n, 1);
   3.411 -    EXPECT_EQ(cx_strcmp(list[0], test), 0);
   3.412 -    cxFree(&alloc, list);
   3.413 -
   3.414 -    // matching single-char delimiter
   3.415 -    n = cx_strsplit_a(&alloc, test, CX_STR(","), capa, &list);
   3.416 -    ASSERT_EQ(n, 5);
   3.417 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("this")), 0);
   3.418 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("is")), 0);
   3.419 -    EXPECT_EQ(cx_strcmp(list[2], CX_STR("a")), 0);
   3.420 -    EXPECT_EQ(cx_strcmp(list[3], CX_STR("csv")), 0);
   3.421 -    EXPECT_EQ(cx_strcmp(list[4], CX_STR("string")), 0);
   3.422 -    cxFree(&alloc, list);
   3.423 -
   3.424 -    // matching multi-char delimiter
   3.425 -    n = cx_strsplit_a(&alloc, test, CX_STR("is"), capa, &list);
   3.426 -    ASSERT_EQ(n, 3);
   3.427 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("th")), 0);
   3.428 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR(",")), 0);
   3.429 -    EXPECT_EQ(cx_strcmp(list[2], CX_STR(",a,csv,string")), 0);
   3.430 -    cxFree(&alloc, list);
   3.431 -
   3.432 -    // bounded list using single-char delimiter
   3.433 -    n = cx_strsplit_a(&alloc, test, CX_STR(","), 3, &list);
   3.434 -    ASSERT_EQ(n, 3);
   3.435 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("this")), 0);
   3.436 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("is")), 0);
   3.437 -    EXPECT_EQ(cx_strcmp(list[2], CX_STR("a,csv,string")), 0);
   3.438 -    cxFree(&alloc, list);
   3.439 -
   3.440 -    // bounded list using multi-char delimiter
   3.441 -    n = cx_strsplit_a(&alloc, test, CX_STR("is"), 2, &list);
   3.442 -    ASSERT_EQ(n, 2);
   3.443 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("th")), 0);
   3.444 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR(",is,a,csv,string")), 0);
   3.445 -    cxFree(&alloc, list);
   3.446 -
   3.447 -    // start with delimiter
   3.448 -    n = cx_strsplit_a(&alloc, test, CX_STR("this"), capa, &list);
   3.449 -    ASSERT_EQ(n, 2);
   3.450 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("")), 0);
   3.451 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR(",is,a,csv,string")), 0);
   3.452 -    cxFree(&alloc, list);
   3.453 -
   3.454 -    // end with delimiter
   3.455 -    n = cx_strsplit_a(&alloc, test, CX_STR("string"), capa, &list);
   3.456 -    ASSERT_EQ(n, 2);
   3.457 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("this,is,a,csv,")), 0);
   3.458 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("")), 0);
   3.459 -    cxFree(&alloc, list);
   3.460 -
   3.461 -    // end with delimiter exceed bound
   3.462 -    n = cx_strsplit_a(&alloc, CX_STR("a,b,c,"), CX_STR(","), 3, &list);
   3.463 -    ASSERT_EQ(n, 3);
   3.464 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("a")), 0);
   3.465 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("b")), 0);
   3.466 -    EXPECT_EQ(cx_strcmp(list[2], CX_STR("c,")), 0);
   3.467 -    cxFree(&alloc, list);
   3.468 -
   3.469 -    // exact match
   3.470 -    n = cx_strsplit_a(&alloc, test, CX_STR("this,is,a,csv,string"), capa, &list);
   3.471 -    ASSERT_EQ(n, 2);
   3.472 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("")), 0);
   3.473 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("")), 0);
   3.474 -    cxFree(&alloc, list);
   3.475 -
   3.476 -    // string to be split is only substring
   3.477 -    n = cx_strsplit_a(&alloc, test, CX_STR("this,is,a,csv,string,with,extension"), capa, &list);
   3.478 -    ASSERT_EQ(n, 1);
   3.479 -    EXPECT_EQ(cx_strcmp(list[0], test), 0);
   3.480 -    cxFree(&alloc, list);
   3.481 -
   3.482 -    // subsequent encounter of delimiter (the string between is empty)
   3.483 -    n = cx_strsplit_a(&alloc, test, CX_STR("is,"), capa, &list);
   3.484 -    ASSERT_EQ(n, 3);
   3.485 -    EXPECT_EQ(cx_strcmp(list[0], CX_STR("th")), 0);
   3.486 -    EXPECT_EQ(cx_strcmp(list[1], CX_STR("")), 0);
   3.487 -    EXPECT_EQ(cx_strcmp(list[2], CX_STR("a,csv,string")), 0);
   3.488 -    cxFree(&alloc, list);
   3.489 -
   3.490 -    // call the _m variant just for coverage
   3.491 -    auto mtest = cx_strdup(test);
   3.492 -    cxmutstr *mlist;
   3.493 -    n = cx_strsplit_ma(&alloc, mtest, CX_STR("is,"), 4, &mlist);
   3.494 -    ASSERT_EQ(n, 3);
   3.495 -    EXPECT_EQ(cx_strcmp(cx_strcast(mlist[0]), CX_STR("th")), 0);
   3.496 -    EXPECT_EQ(cx_strcmp(cx_strcast(mlist[1]), CX_STR("")), 0);
   3.497 -    EXPECT_EQ(cx_strcmp(cx_strcast(mlist[2]), CX_STR("a,csv,string")), 0);
   3.498 -    cxFree(&alloc, mlist);
   3.499 -    cx_strfree(&mtest);
   3.500 -
   3.501 -    EXPECT_TRUE(alloc.verify());
   3.502 -}
   3.503 -
   3.504 -TEST(String, strtrim) {
   3.505 -    cxstring t1 = cx_strtrim(CX_STR("  ein test  \t "));
   3.506 -    cxstring t2 = cx_strtrim(CX_STR("abc"));
   3.507 -    cxstring t3 = cx_strtrim(CX_STR(" 123"));
   3.508 -    cxstring t4 = cx_strtrim(CX_STR("xyz "));
   3.509 -    cxstring t5 = cx_strtrim(CX_STR("   "));
   3.510 -    cxstring empty = cx_strtrim(CX_STR(""));
   3.511 -
   3.512 -    EXPECT_EQ(cx_strcmp(t1, CX_STR("ein test")), 0);
   3.513 -    EXPECT_EQ(cx_strcmp(t2, CX_STR("abc")), 0);
   3.514 -    EXPECT_EQ(cx_strcmp(t3, CX_STR("123")), 0);
   3.515 -    EXPECT_EQ(cx_strcmp(t4, CX_STR("xyz")), 0);
   3.516 -    EXPECT_EQ(cx_strcmp(t5, CX_STR("")), 0);
   3.517 -    EXPECT_EQ(cx_strcmp(empty, CX_STR("")), 0);
   3.518 -
   3.519 -    // call the _m variant just for coverage
   3.520 -    cxmutstr m1 = cx_strtrim_m(cx_mutstr((char *) "  ein test  \t "));
   3.521 -    EXPECT_EQ(cx_strcmp(cx_strcast(m1), CX_STR("ein test")), 0);
   3.522 -}
   3.523 -
   3.524 -TEST(String, strprefix) {
   3.525 -    cxstring str = CX_STR("test my prefix and my suffix");
   3.526 -    cxstring empty = CX_STR("");
   3.527 -    EXPECT_FALSE(cx_strprefix(empty, CX_STR("pref")));
   3.528 -    EXPECT_TRUE(cx_strprefix(str, empty));
   3.529 -    EXPECT_TRUE(cx_strprefix(empty, empty));
   3.530 -    EXPECT_TRUE(cx_strprefix(str, CX_STR("test ")));
   3.531 -    EXPECT_FALSE(cx_strprefix(str, CX_STR("8-) fsck ")));
   3.532 -}
   3.533 -
   3.534 -TEST(String, strsuffix) {
   3.535 -    cxstring str = CX_STR("test my prefix and my suffix");
   3.536 -    cxstring empty = CX_STR("");
   3.537 -    EXPECT_FALSE(cx_strsuffix(empty, CX_STR("suf")));
   3.538 -    EXPECT_TRUE(cx_strsuffix(str, empty));
   3.539 -    EXPECT_TRUE(cx_strsuffix(empty, empty));
   3.540 -    EXPECT_TRUE(cx_strsuffix(str, CX_STR("fix")));
   3.541 -    EXPECT_FALSE(cx_strsuffix(str, CX_STR("fox")));
   3.542 -}
   3.543 -
   3.544 -TEST(String, strcaseprefix) {
   3.545 -    cxstring str = CX_STR("test my prefix and my suffix");
   3.546 -    cxstring empty = CX_STR("");
   3.547 -    EXPECT_FALSE(cx_strcaseprefix(empty, CX_STR("pREf")));
   3.548 -    EXPECT_TRUE(cx_strcaseprefix(str, empty));
   3.549 -    EXPECT_TRUE(cx_strcaseprefix(empty, empty));
   3.550 -    EXPECT_TRUE(cx_strcaseprefix(str, CX_STR("TEST ")));
   3.551 -    EXPECT_FALSE(cx_strcaseprefix(str, CX_STR("8-) fsck ")));
   3.552 -}
   3.553 -
   3.554 -TEST(String, strcasesuffix) {
   3.555 -    cxstring str = CX_STR("test my prefix and my suffix");
   3.556 -    cxstring empty = CX_STR("");
   3.557 -    EXPECT_FALSE(cx_strcasesuffix(empty, CX_STR("sUf")));
   3.558 -    EXPECT_TRUE(cx_strcasesuffix(str, empty));
   3.559 -    EXPECT_TRUE(cx_strcasesuffix(empty, empty));
   3.560 -    EXPECT_TRUE(cx_strcasesuffix(str, CX_STR("FIX")));
   3.561 -    EXPECT_FALSE(cx_strcasesuffix(str, CX_STR("fox")));
   3.562 -}
   3.563 -
   3.564 -TEST(String, strreplace) {
   3.565 -    CxTestingAllocator alloc;
   3.566 -    cxstring str = CX_STR("test ababab string aba");
   3.567 -    cxstring longstr = CX_STR(
   3.568 -            "xyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacd");
   3.569 -    cxstring notrail = CX_STR("test abab");
   3.570 -    cxstring empty = CX_STR("");
   3.571 -    cxstring astr = CX_STR("aaaaaaaaaa");
   3.572 -    cxstring csstr = CX_STR("test AB ab TEST xyz");
   3.573 -
   3.574 -    cxmutstr repl = cx_strreplace(str, CX_STR("abab"), CX_STR("muchlonger"));
   3.575 -    auto expected = "test muchlongerab string aba";
   3.576 -
   3.577 -    cxmutstr repln = cx_strreplacen(str, CX_STR("ab"), CX_STR("c"), 2);
   3.578 -    auto expectedn = "test ccab string aba";
   3.579 -
   3.580 -    cxmutstr longrepl = cx_strreplace(longstr, CX_STR("a"), CX_STR("z"));
   3.581 -    auto longexpect = "xyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzcd";
   3.582 -
   3.583 -    cxmutstr replnotrail = cx_strreplace(notrail, CX_STR("ab"), CX_STR("z"));
   3.584 -    auto notrailexpect = "test zz";
   3.585 -
   3.586 -    cxmutstr repleq = cx_strreplace(str, str, CX_STR("hello"));
   3.587 -    auto eqexpect = "hello";
   3.588 -
   3.589 -    cxmutstr replempty1 = cx_strreplace(empty, CX_STR("ab"), CX_STR("c")); // expect: empty
   3.590 -    cxmutstr replempty2 = cx_strreplace(str, CX_STR("abab"), empty);
   3.591 -    auto emptyexpect2 = "test ab string aba";
   3.592 -
   3.593 -    cxmutstr replpre = cx_strreplace(str, CX_STR("test "), CX_STR("TEST "));
   3.594 -    auto preexpected = "TEST ababab string aba";
   3.595 -
   3.596 -    cxmutstr replan1 = cx_strreplacen(astr, CX_STR("a"), CX_STR("x"), 1);
   3.597 -    auto an1expected = "xaaaaaaaaa";
   3.598 -
   3.599 -    cxmutstr replan4 = cx_strreplacen(astr, CX_STR("a"), CX_STR("x"), 4);
   3.600 -    auto an4expected = "xxxxaaaaaa";
   3.601 -
   3.602 -    cxmutstr replan9 = cx_strreplacen(astr, CX_STR("a"), CX_STR("x"), 9);
   3.603 -    auto an9expected = "xxxxxxxxxa";
   3.604 -
   3.605 -    cxmutstr replan10 = cx_strreplacen(astr, CX_STR("a"), CX_STR("x"), 10);
   3.606 -    auto an10expected = "xxxxxxxxxx";
   3.607 -
   3.608 -    cxmutstr repl1_a = cx_strreplace_a(&alloc, csstr, CX_STR("AB"), CX_STR("*"));
   3.609 -    auto expeced1_a = "test * ab TEST xyz";
   3.610 -
   3.611 -    cxmutstr repl2_a = cx_strreplace_a(&alloc, csstr, CX_STR("test"), CX_STR("TEST"));
   3.612 -    auto expected2_a = "TEST AB ab TEST xyz";
   3.613 -
   3.614 -
   3.615 -    EXPECT_NE(repl.ptr, str.ptr);
   3.616 -    EXPECT_ZERO_TERMINATED(repl);
   3.617 -    EXPECT_STREQ(repl.ptr, expected);
   3.618 -    EXPECT_ZERO_TERMINATED(repln);
   3.619 -    EXPECT_STREQ(repln.ptr, expectedn);
   3.620 -    EXPECT_ZERO_TERMINATED(longrepl);
   3.621 -    EXPECT_STREQ(longrepl.ptr, longexpect);
   3.622 -    EXPECT_ZERO_TERMINATED(replnotrail);
   3.623 -    EXPECT_STREQ(replnotrail.ptr, notrailexpect);
   3.624 -    EXPECT_ZERO_TERMINATED(repleq);
   3.625 -    EXPECT_STREQ(repleq.ptr, eqexpect);
   3.626 -    EXPECT_ZERO_TERMINATED(replempty1);
   3.627 -    EXPECT_STREQ(replempty1.ptr, "");
   3.628 -    EXPECT_ZERO_TERMINATED(replempty2);
   3.629 -    EXPECT_STREQ(replempty2.ptr, emptyexpect2);
   3.630 -    EXPECT_ZERO_TERMINATED(replpre);
   3.631 -    EXPECT_STREQ(replpre.ptr, preexpected);
   3.632 -    EXPECT_ZERO_TERMINATED(replan1);
   3.633 -    EXPECT_STREQ(replan1.ptr, an1expected);
   3.634 -    EXPECT_ZERO_TERMINATED(replan4);
   3.635 -    EXPECT_STREQ(replan4.ptr, an4expected);
   3.636 -    EXPECT_ZERO_TERMINATED(replan9);
   3.637 -    EXPECT_STREQ(replan9.ptr, an9expected);
   3.638 -    EXPECT_ZERO_TERMINATED(replan10);
   3.639 -    EXPECT_STREQ(replan10.ptr, an10expected);
   3.640 -    EXPECT_ZERO_TERMINATED(repl1_a);
   3.641 -    EXPECT_STREQ(repl1_a.ptr, expeced1_a);
   3.642 -    EXPECT_ZERO_TERMINATED(repl2_a);
   3.643 -    EXPECT_STREQ(repl2_a.ptr, expected2_a);
   3.644 -
   3.645 -    cx_strfree(&repl);
   3.646 -    cx_strfree(&repln);
   3.647 -    cx_strfree(&longrepl);
   3.648 -    cx_strfree(&replnotrail);
   3.649 -    cx_strfree(&repleq);
   3.650 -    cx_strfree(&replempty1);
   3.651 -    cx_strfree(&replempty2);
   3.652 -    cx_strfree(&replpre);
   3.653 -    cx_strfree(&replan1);
   3.654 -    cx_strfree(&replan4);
   3.655 -    cx_strfree(&replan9);
   3.656 -    cx_strfree(&replan10);
   3.657 -
   3.658 -    cx_strfree_a(&alloc, &repl1_a);
   3.659 -    cx_strfree_a(&alloc, &repl2_a);
   3.660 -    EXPECT_TRUE(alloc.verify());
   3.661 -}
   3.662 -
   3.663 -TEST(String, strupper) {
   3.664 -    cxmutstr str = cx_strdup(CX_STR("thIs 1s @ Te$t"));
   3.665 -    cx_strupper(str);
   3.666 -    EXPECT_STREQ(str.ptr, "THIS 1S @ TE$T");
   3.667 -    cx_strfree(&str);
   3.668 -}
   3.669 -
   3.670 -TEST(String, strlower) {
   3.671 -    cxmutstr str = cx_strdup(CX_STR("thIs 1s @ Te$t"));
   3.672 -    cx_strlower(str);
   3.673 -    EXPECT_STREQ(str.ptr, "this 1s @ te$t");
   3.674 -    cx_strfree(&str);
   3.675 -}
   3.676 -
   3.677 -TEST(String, strtok) {
   3.678 -    cxstring str = CX_STR("a,comma,separated,string");
   3.679 -    cxstring delim = CX_STR(",");
   3.680 -    CxStrtokCtx ctx = cx_strtok(str, delim, 3);
   3.681 -    EXPECT_EQ(ctx.str.ptr, str.ptr);
   3.682 -    EXPECT_EQ(ctx.str.length, str.length);
   3.683 -    EXPECT_EQ(ctx.delim.ptr, delim.ptr);
   3.684 -    EXPECT_EQ(ctx.delim.length, delim.length);
   3.685 -    EXPECT_EQ(ctx.limit, 3);
   3.686 -    EXPECT_EQ(ctx.found, 0);
   3.687 -    EXPECT_EQ(ctx.pos, 0);
   3.688 -    EXPECT_EQ(ctx.next_pos, 0);
   3.689 -    EXPECT_EQ(ctx.delim_more, nullptr);
   3.690 -    EXPECT_EQ(ctx.delim_more_count, 0);
   3.691 -}
   3.692 -
   3.693 -TEST(String, strtok_m) {
   3.694 -    cxmutstr str = cx_strdup(CX_STR("a,comma,separated,string"));
   3.695 -    cxstring delim = CX_STR(",");
   3.696 -    CxStrtokCtx ctx = cx_strtok_m(str, delim, 3);
   3.697 -    EXPECT_EQ(ctx.str.ptr, str.ptr);
   3.698 -    EXPECT_EQ(ctx.str.length, str.length);
   3.699 -    EXPECT_EQ(ctx.delim.ptr, delim.ptr);
   3.700 -    EXPECT_EQ(ctx.delim.length, delim.length);
   3.701 -    EXPECT_EQ(ctx.limit, 3);
   3.702 -    EXPECT_EQ(ctx.found, 0);
   3.703 -    EXPECT_EQ(ctx.pos, 0);
   3.704 -    EXPECT_EQ(ctx.next_pos, 0);
   3.705 -    EXPECT_EQ(ctx.delim_more, nullptr);
   3.706 -    EXPECT_EQ(ctx.delim_more_count, 0);
   3.707 -    cx_strfree(&str);
   3.708 -}
   3.709 -
   3.710 -TEST(String, strtok_delim) {
   3.711 -    cxstring str = CX_STR("an,arbitrarily|separated;string");
   3.712 -    cxstring delim = CX_STR(",");
   3.713 -    cxstring delim_more[2] = {CX_STR("|"), CX_STR(";")};
   3.714 -    CxStrtokCtx ctx = cx_strtok(str, delim, 3);
   3.715 -    cx_strtok_delim(&ctx, delim_more, 2);
   3.716 -    EXPECT_EQ(ctx.str.ptr, str.ptr);
   3.717 -    EXPECT_EQ(ctx.str.length, str.length);
   3.718 -    EXPECT_EQ(ctx.delim.ptr, delim.ptr);
   3.719 -    EXPECT_EQ(ctx.delim.length, delim.length);
   3.720 -    EXPECT_EQ(ctx.limit, 3);
   3.721 -    EXPECT_EQ(ctx.found, 0);
   3.722 -    EXPECT_EQ(ctx.pos, 0);
   3.723 -    EXPECT_EQ(ctx.next_pos, 0);
   3.724 -    EXPECT_EQ(ctx.delim_more, delim_more);
   3.725 -    EXPECT_EQ(ctx.delim_more_count, 2);
   3.726 -}
   3.727 -
   3.728 -TEST(String, strtok_next_easy) {
   3.729 -    cxstring str = CX_STR("a,comma,separated,string");
   3.730 -    cxstring delim = CX_STR(",");
   3.731 -    CxStrtokCtx ctx = cx_strtok(str, delim, 3);
   3.732 -    bool ret;
   3.733 -    cxstring tok;
   3.734 -
   3.735 -    ret = cx_strtok_next(&ctx, &tok);
   3.736 -    ASSERT_TRUE(ret);
   3.737 -    EXPECT_EQ(cx_strcmp(tok, CX_STR("a")), 0);
   3.738 -    EXPECT_EQ(ctx.pos, 0);
   3.739 -    EXPECT_EQ(ctx.next_pos, 2);
   3.740 -    EXPECT_EQ(ctx.delim_pos, 1);
   3.741 -    EXPECT_EQ(ctx.found, 1);
   3.742 -
   3.743 -    ret = cx_strtok_next(&ctx, &tok);
   3.744 -    ASSERT_TRUE(ret);
   3.745 -    EXPECT_EQ(cx_strcmp(tok, CX_STR("comma")), 0);
   3.746 -    EXPECT_EQ(ctx.pos, 2);
   3.747 -    EXPECT_EQ(ctx.next_pos, 8);
   3.748 -    EXPECT_EQ(ctx.delim_pos, 7);
   3.749 -    EXPECT_EQ(ctx.found, 2);
   3.750 -
   3.751 -    ret = cx_strtok_next(&ctx, &tok);
   3.752 -    ASSERT_TRUE(ret);
   3.753 -    EXPECT_EQ(cx_strcmp(tok, CX_STR("separated")), 0);
   3.754 -    EXPECT_EQ(ctx.pos, 8);
   3.755 -    EXPECT_EQ(ctx.next_pos, 18);
   3.756 -    EXPECT_EQ(ctx.delim_pos, 17);
   3.757 -    EXPECT_EQ(ctx.found, 3);
   3.758 -
   3.759 -    ret = cx_strtok_next(&ctx, &tok);
   3.760 -    ASSERT_FALSE(ret);
   3.761 -    EXPECT_EQ(ctx.pos, 8);
   3.762 -    EXPECT_EQ(ctx.next_pos, 18);
   3.763 -    EXPECT_EQ(ctx.delim_pos, 17);
   3.764 -    EXPECT_EQ(ctx.found, 3);
   3.765 -}
   3.766 -
   3.767 -TEST(String, strtok_next_unlimited) {
   3.768 -    cxstring str = CX_STR("some;-;otherwise;-;separated;-;string;-;");
   3.769 -    cxstring delim = CX_STR(";-;");
   3.770 -    CxStrtokCtx ctx = cx_strtok(str, delim, SIZE_MAX);
   3.771 -    bool ret;
   3.772 -    cxstring tok;
   3.773 -
   3.774 -    ret = cx_strtok_next(&ctx, &tok);
   3.775 -    ASSERT_TRUE(ret);
   3.776 -    EXPECT_EQ(cx_strcmp(tok, CX_STR("some")), 0);
   3.777 -    EXPECT_EQ(ctx.pos, 0);
   3.778 -    EXPECT_EQ(ctx.next_pos, 7);
   3.779 -    EXPECT_EQ(ctx.delim_pos, 4);
   3.780 -    EXPECT_EQ(ctx.found, 1);
   3.781 -
   3.782 -    ret = cx_strtok_next(&ctx, &tok);
   3.783 -    ASSERT_TRUE(ret);
   3.784 -    EXPECT_EQ(cx_strcmp(tok, CX_STR("otherwise")), 0);
   3.785 -    EXPECT_EQ(ctx.pos, 7);
   3.786 -    EXPECT_EQ(ctx.next_pos, 19);
   3.787 -    EXPECT_EQ(ctx.delim_pos, 16);
   3.788 -    EXPECT_EQ(ctx.found, 2);
   3.789 -
   3.790 -    ret = cx_strtok_next(&ctx, &tok);
   3.791 -    ASSERT_TRUE(ret);
   3.792 -    EXPECT_EQ(cx_strcmp(tok, CX_STR("separated")), 0);
   3.793 -    EXPECT_EQ(ctx.pos, 19);
   3.794 -    EXPECT_EQ(ctx.next_pos, 31);
   3.795 -    EXPECT_EQ(ctx.delim_pos, 28);
   3.796 -    EXPECT_EQ(ctx.found, 3);
   3.797 -
   3.798 -    ret = cx_strtok_next(&ctx, &tok);
   3.799 -    ASSERT_TRUE(ret);
   3.800 -    EXPECT_EQ(cx_strcmp(tok, CX_STR("string")), 0);
   3.801 -    EXPECT_EQ(ctx.pos, 31);
   3.802 -    EXPECT_EQ(ctx.next_pos, 40);
   3.803 -    EXPECT_EQ(ctx.delim_pos, 37);
   3.804 -    EXPECT_EQ(ctx.found, 4);
   3.805 -
   3.806 -    ret = cx_strtok_next(&ctx, &tok);
   3.807 -    ASSERT_TRUE(ret);
   3.808 -    EXPECT_EQ(cx_strcmp(tok, CX_STR("")), 0);
   3.809 -    EXPECT_EQ(ctx.pos, 40);
   3.810 -    EXPECT_EQ(ctx.next_pos, 40);
   3.811 -    EXPECT_EQ(ctx.delim_pos, 40);
   3.812 -    EXPECT_EQ(ctx.found, 5);
   3.813 -
   3.814 -    ret = cx_strtok_next(&ctx, &tok);
   3.815 -    ASSERT_FALSE(ret);
   3.816 -    EXPECT_EQ(ctx.pos, 40);
   3.817 -    EXPECT_EQ(ctx.delim_pos, 40);
   3.818 -    EXPECT_EQ(ctx.found, 5);
   3.819 -}
   3.820 -
   3.821 -TEST(String, strtok_next_advanced) {
   3.822 -    cxmutstr str = cx_strdup(CX_STR("an,arbitrarily;||separated;string"));
   3.823 -    cxstring delim = CX_STR(",");
   3.824 -    cxstring delim_more[2] = {CX_STR("||"), CX_STR(";")};
   3.825 -    CxStrtokCtx ctx = cx_strtok_m(str, delim, 10);
   3.826 -    cx_strtok_delim(&ctx, delim_more, 2);
   3.827 -    bool ret;
   3.828 -    cxmutstr tok;
   3.829 -
   3.830 -    ret = cx_strtok_next_m(&ctx, &tok);
   3.831 -    ASSERT_TRUE(ret);
   3.832 -    EXPECT_EQ(cx_strcmp(cx_strcast(tok), CX_STR("an")), 0);
   3.833 -    EXPECT_EQ(ctx.pos, 0);
   3.834 -    EXPECT_EQ(ctx.next_pos, 3);
   3.835 -    EXPECT_EQ(ctx.delim_pos, 2);
   3.836 -    EXPECT_EQ(ctx.found, 1);
   3.837 -    cx_strupper(tok);
   3.838 -
   3.839 -    ret = cx_strtok_next_m(&ctx, &tok);
   3.840 -    ASSERT_TRUE(ret);
   3.841 -    EXPECT_EQ(cx_strcmp(cx_strcast(tok), CX_STR("arbitrarily")), 0);
   3.842 -    EXPECT_EQ(ctx.pos, 3);
   3.843 -    EXPECT_EQ(ctx.next_pos, 15);
   3.844 -    EXPECT_EQ(ctx.delim_pos, 14);
   3.845 -    EXPECT_EQ(ctx.found, 2);
   3.846 -    cx_strupper(tok);
   3.847 -
   3.848 -    ret = cx_strtok_next_m(&ctx, &tok);
   3.849 -    ASSERT_TRUE(ret);
   3.850 -    EXPECT_EQ(cx_strcmp(cx_strcast(tok), CX_STR("")), 0);
   3.851 -    EXPECT_EQ(ctx.pos, 15);
   3.852 -    EXPECT_EQ(ctx.next_pos, 17);
   3.853 -    EXPECT_EQ(ctx.delim_pos, 15);
   3.854 -    EXPECT_EQ(ctx.found, 3);
   3.855 -    cx_strupper(tok);
   3.856 -
   3.857 -    ret = cx_strtok_next_m(&ctx, &tok);
   3.858 -    ASSERT_TRUE(ret);
   3.859 -    EXPECT_EQ(cx_strcmp(cx_strcast(tok), CX_STR("separated")), 0);
   3.860 -    EXPECT_EQ(ctx.pos, 17);
   3.861 -    EXPECT_EQ(ctx.next_pos, 27);
   3.862 -    EXPECT_EQ(ctx.delim_pos, 26);
   3.863 -    EXPECT_EQ(ctx.found, 4);
   3.864 -    cx_strupper(tok);
   3.865 -
   3.866 -    ret = cx_strtok_next_m(&ctx, &tok);
   3.867 -    ASSERT_TRUE(ret);
   3.868 -    EXPECT_EQ(cx_strcmp(cx_strcast(tok), CX_STR("string")), 0);
   3.869 -    EXPECT_EQ(ctx.pos, 27);
   3.870 -    EXPECT_EQ(ctx.next_pos, 33);
   3.871 -    EXPECT_EQ(ctx.delim_pos, 33);
   3.872 -    EXPECT_EQ(ctx.found, 5);
   3.873 -    cx_strupper(tok);
   3.874 -
   3.875 -    ret = cx_strtok_next_m(&ctx, &tok);
   3.876 -    ASSERT_FALSE(ret);
   3.877 -    EXPECT_EQ(ctx.pos, 27);
   3.878 -    EXPECT_EQ(ctx.next_pos, 33);
   3.879 -    EXPECT_EQ(ctx.delim_pos, 33);
   3.880 -    EXPECT_EQ(ctx.found, 5);
   3.881 -
   3.882 -    EXPECT_EQ(cx_strcmp(cx_strcast(str), CX_STR("AN,ARBITRARILY;||SEPARATED;STRING")), 0);
   3.883 -
   3.884 -    cx_strfree(&str);
   3.885 -}
     4.1 --- a/tests/ucxtest.c	Thu Dec 28 19:17:45 2023 +0100
     4.2 +++ b/tests/ucxtest.c	Thu Dec 28 20:37:53 2023 +0100
     4.3 @@ -32,6 +32,8 @@
     4.4  CxTestSuite *cx_test_suite_utils(void);
     4.5  CxTestSuite *cx_test_suite_hash_key(void);
     4.6  
     4.7 +CxTestSuite *cx_test_suite_string(void);
     4.8 +
     4.9  #define run_tests(suite) cx_test_run_stdout(suite); success += (suite)->success; failure += (suite)->failure
    4.10  #define execute_test_suites(...) unsigned success = 0, failure = 0; CxTestSuite* test_suites[] = {__VA_ARGS__}; \
    4.11      for (size_t i = 0; i < sizeof(test_suites)/sizeof(void*) ; i++) {run_tests(test_suites[i]);} (void)0
    4.12 @@ -43,7 +45,8 @@
    4.13      execute_test_suites(
    4.14              cx_test_suite_testing_allocator(),
    4.15              cx_test_suite_utils(),
    4.16 -            cx_test_suite_hash_key()
    4.17 +            cx_test_suite_hash_key(),
    4.18 +            cx_test_suite_string()
    4.19      );
    4.20      printf("=== OVERALL RESULT ===\n");
    4.21      printf("  Total:   %u\n  Success: %u\n  Failure: %u\n",

mercurial