--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/test/strreplace.c Sat Nov 22 16:44:42 2025 +0100 @@ -0,0 +1,278 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "strreplace.h" +#include "../util/strreplace.h" +#include <cx/mempool.h> + +CX_TEST(test_string_template_compile) { + CxMempool *mp = cxMempoolCreate(100, CX_MEMPOOL_TYPE_ADVANCED); + const CxAllocator *a = mp->allocator; + + StringTemplate *tpl = NULL; + StringTemplateSegment *s0 = NULL; + StringTemplateSegment *s1 = NULL; + StringTemplateSegment *s2 = NULL; + StringTemplateSegment *s3 = NULL; + CX_TEST_DO { + // single segment tests + + tpl = string_template_compile(a, cx_str("")); + CX_TEST_ASSERT(tpl); // empty str + CX_TEST_ASSERT(!tpl->segments); + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("static")); + CX_TEST_ASSERT(tpl); // static + CX_TEST_ASSERT(tpl->segments); + CX_TEST_ASSERT(!tpl->segments->next); + CX_TEST_ASSERT(!cx_strcmp(tpl->segments->str, "static")); + CX_TEST_ASSERT(tpl->segments->type == STRING_SEGMENT_STR); + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("$$")); + CX_TEST_ASSERT(tpl); // $ + CX_TEST_ASSERT(tpl->segments); + CX_TEST_ASSERT(!tpl->segments->next); + CX_TEST_ASSERT(!cx_strcmp(tpl->segments->str, "$")); + CX_TEST_ASSERT(tpl->segments->type == STRING_SEGMENT_STR); + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("$var")); + CX_TEST_ASSERT(tpl); // var + CX_TEST_ASSERT(tpl->segments); + CX_TEST_ASSERT(!tpl->segments->next); + CX_TEST_ASSERT(!cx_strcmp(tpl->segments->str, "var")); + CX_TEST_ASSERT(tpl->segments->type == STRING_SEGMENT_VAR_PLACEHOLDER); + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("$12")); + CX_TEST_ASSERT(tpl); // 12 + CX_TEST_ASSERT(tpl->segments); + CX_TEST_ASSERT(!tpl->segments->next); + CX_TEST_ASSERT(!cx_strcmp(tpl->segments->str, "12")); + CX_TEST_ASSERT(tpl->segments->type == STRING_SEGMENT_NUM_PLACEHOLDER); + CX_TEST_ASSERT(tpl->segments->num == 12); + string_template_free(tpl); + + // double segment tests + tpl = string_template_compile(a, cx_str("test $var")); + CX_TEST_ASSERT(tpl); + s0 = tpl->segments; + CX_TEST_ASSERT(s0); + s1 = s0->next; + CX_TEST_ASSERT(s1); + CX_TEST_ASSERT(!cx_strcmp(s0->str, "test ")); + CX_TEST_ASSERT(s0->type == STRING_SEGMENT_STR); + CX_TEST_ASSERT(!cx_strcmp(s1->str, "var")); + CX_TEST_ASSERT(s1->type == STRING_SEGMENT_VAR_PLACEHOLDER); + CX_TEST_ASSERT(s1->next == NULL); + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("test ${var}")); + CX_TEST_ASSERT(tpl); + s0 = tpl->segments; + CX_TEST_ASSERT(s0); + s1 = s0->next; + CX_TEST_ASSERT(s1); + CX_TEST_ASSERT(!cx_strcmp(s0->str, "test ")); + CX_TEST_ASSERT(s0->type == STRING_SEGMENT_STR); + CX_TEST_ASSERT(!cx_strcmp(s1->str, "var")); + CX_TEST_ASSERT(s1->type == STRING_SEGMENT_VAR_PLACEHOLDER); + CX_TEST_ASSERT(s1->next == NULL); + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("$var test")); + CX_TEST_ASSERT(tpl); + s0 = tpl->segments; + CX_TEST_ASSERT(s0); + s1 = s0->next; + CX_TEST_ASSERT(s1); + CX_TEST_ASSERT(!cx_strcmp(s0->str, "var")); + CX_TEST_ASSERT(s0->type == STRING_SEGMENT_VAR_PLACEHOLDER); + CX_TEST_ASSERT(!cx_strcmp(s1->str, " test")); + CX_TEST_ASSERT(s1->type == STRING_SEGMENT_STR); + CX_TEST_ASSERT(s1->next == NULL); + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("$13 test")); + CX_TEST_ASSERT(tpl); + s0 = tpl->segments; + CX_TEST_ASSERT(s0); + s1 = s0->next; + CX_TEST_ASSERT(s1); + CX_TEST_ASSERT(!cx_strcmp(s0->str, "13")); + CX_TEST_ASSERT(s0->type == STRING_SEGMENT_NUM_PLACEHOLDER); + CX_TEST_ASSERT(!cx_strcmp(s1->str, " test")); + CX_TEST_ASSERT(s1->type == STRING_SEGMENT_STR); + CX_TEST_ASSERT(s1->next == NULL); + string_template_free(tpl); + + // multi segment tests + tpl = string_template_compile(a, cx_str("test$var1$var2")); + CX_TEST_ASSERT(tpl); + s0 = tpl->segments; + CX_TEST_ASSERT(s0); + s1 = s0->next; + CX_TEST_ASSERT(s1); + s2 = s1->next; + CX_TEST_ASSERT(2); + CX_TEST_ASSERT(!cx_strcmp(s0->str, "test")); + CX_TEST_ASSERT(s0->type == STRING_SEGMENT_STR); + CX_TEST_ASSERT(!cx_strcmp(s1->str, "var1")); + CX_TEST_ASSERT(s1->type == STRING_SEGMENT_VAR_PLACEHOLDER); + CX_TEST_ASSERT(!cx_strcmp(s2->str, "var2")); + CX_TEST_ASSERT(s2->type == STRING_SEGMENT_VAR_PLACEHOLDER); + CX_TEST_ASSERT(s2->next == NULL); + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("test/$1/$2")); + CX_TEST_ASSERT(tpl); + s0 = tpl->segments; + CX_TEST_ASSERT(s0); + s1 = s0->next; + CX_TEST_ASSERT(s1); + s2 = s1->next; + CX_TEST_ASSERT(2); + s3 = s2->next; + CX_TEST_ASSERT(s3); + CX_TEST_ASSERT(!cx_strcmp(s0->str, "test/")); + CX_TEST_ASSERT(s0->type == STRING_SEGMENT_STR); + CX_TEST_ASSERT(!cx_strcmp(s1->str, "1")); + CX_TEST_ASSERT(s1->type == STRING_SEGMENT_NUM_PLACEHOLDER); + CX_TEST_ASSERT(!cx_strcmp(s2->str, "/")); + CX_TEST_ASSERT(s2->type == STRING_SEGMENT_STR); + CX_TEST_ASSERT(!cx_strcmp(s3->str, "2")); + CX_TEST_ASSERT(s3->type == STRING_SEGMENT_NUM_PLACEHOLDER); + CX_TEST_ASSERT(s3->next == NULL); + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("ab$$cd/${1}/${2}")); + CX_TEST_ASSERT(tpl); + s0 = tpl->segments; + CX_TEST_ASSERT(s0); + s1 = s0->next; + CX_TEST_ASSERT(s1); + s2 = s1->next; + CX_TEST_ASSERT(2); + s3 = s2->next; + CX_TEST_ASSERT(s3); + CX_TEST_ASSERT(!cx_strcmp(s0->str, "ab$cd/")); + CX_TEST_ASSERT(s0->type == STRING_SEGMENT_STR); + CX_TEST_ASSERT(!cx_strcmp(s1->str, "1")); + CX_TEST_ASSERT(s1->type == STRING_SEGMENT_NUM_PLACEHOLDER); + CX_TEST_ASSERT(!cx_strcmp(s2->str, "/")); + CX_TEST_ASSERT(s2->type == STRING_SEGMENT_STR); + CX_TEST_ASSERT(!cx_strcmp(s3->str, "2")); + CX_TEST_ASSERT(s3->type == STRING_SEGMENT_NUM_PLACEHOLDER); + CX_TEST_ASSERT(s3->next == NULL); + string_template_free(tpl); + } + + cxMempoolFree(mp); +} + +CX_TEST(test_string_template_compile_error) { + // TODO +} + +static cxmutstr get_var(const CxAllocator *a, StringTemplateSegment *seg, void *userdata, WSBool *free_str) { + cxmutstr var_value = cx_strcat_a(a, 3, cx_str("var("), seg->str, cx_str(")")); + *free_str = TRUE; + return var_value; +} + +CX_TEST(test_string_template_write_to) { + CxMempool *mp = cxMempoolCreate(100, CX_MEMPOOL_TYPE_ADVANCED); + const CxAllocator *a = mp->allocator; + + CxBuffer buf; + cxBufferInit(&buf, NULL, 1024, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); + StringTemplate *tpl = NULL; + CX_TEST_DO { + tpl = string_template_compile(a, cx_str("hello world")); + ssize_t r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); + CX_TEST_ASSERT(r > 0); + CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("hello world"))); + buf.pos = 0; + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("insert $var here")); + r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); + CX_TEST_ASSERT(r > 0); + CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("insert var(var) here"))); + buf.pos = 0; + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("$1$2$3$4$5")); + r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); + CX_TEST_ASSERT(r > 0); + CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("var(1)var(2)var(3)var(4)var(5)"))); + buf.pos = 0; + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("$$escape$$$myvar$$end")); + r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); + CX_TEST_ASSERT(r > 0); + CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("$escape$var(myvar)$end"))); + buf.pos = 0; + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("$$$$${test}")); + r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); + CX_TEST_ASSERT(r > 0); + CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("$$var(test)"))); + buf.pos = 0; + string_template_free(tpl); + + tpl = string_template_compile(a, cx_str("${123}end")); + r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); + CX_TEST_ASSERT(r > 0); + CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("var(123)end"))); + buf.pos = 0; + string_template_free(tpl); + } + cxBufferDestroy(&buf); + cxMempoolFree(mp); +} + +CX_TEST(test_string_template_build_string) { + CxMempool *mp = cxMempoolCreate(100, CX_MEMPOOL_TYPE_ADVANCED); + const CxAllocator *a = mp->allocator; + + StringTemplate *tpl = NULL; + CX_TEST_DO { + tpl = string_template_compile(a, cx_str("insert $var here")); + cxmutstr str = string_template_build_string(tpl, a, get_var, NULL); + CX_TEST_ASSERT(str.ptr); + CX_TEST_ASSERT(!cx_strcmp(str, cx_str("insert var(var) here"))); + string_template_free(tpl); + } + + cxMempoolFree(mp); +}