| |
1 /* |
| |
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
| |
3 * |
| |
4 * Copyright 2025 Olaf Wintermann. All rights reserved. |
| |
5 * |
| |
6 * Redistribution and use in source and binary forms, with or without |
| |
7 * modification, are permitted provided that the following conditions are met: |
| |
8 * |
| |
9 * 1. Redistributions of source code must retain the above copyright |
| |
10 * notice, this list of conditions and the following disclaimer. |
| |
11 * |
| |
12 * 2. Redistributions in binary form must reproduce the above copyright |
| |
13 * notice, this list of conditions and the following disclaimer in the |
| |
14 * documentation and/or other materials provided with the distribution. |
| |
15 * |
| |
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| |
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| |
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| |
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| |
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| |
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| |
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| |
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| |
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| |
26 * POSSIBILITY OF SUCH DAMAGE. |
| |
27 */ |
| |
28 |
| |
29 #include "strreplace.h" |
| |
30 #include "../util/strreplace.h" |
| |
31 #include <cx/mempool.h> |
| |
32 |
| |
33 CX_TEST(test_string_template_compile) { |
| |
34 CxMempool *mp = cxMempoolCreate(100, CX_MEMPOOL_TYPE_ADVANCED); |
| |
35 const CxAllocator *a = mp->allocator; |
| |
36 |
| |
37 StringTemplate *tpl = NULL; |
| |
38 StringTemplateSegment *s0 = NULL; |
| |
39 StringTemplateSegment *s1 = NULL; |
| |
40 StringTemplateSegment *s2 = NULL; |
| |
41 StringTemplateSegment *s3 = NULL; |
| |
42 CX_TEST_DO { |
| |
43 // single segment tests |
| |
44 |
| |
45 tpl = string_template_compile(a, cx_str("")); |
| |
46 CX_TEST_ASSERT(tpl); // empty str |
| |
47 CX_TEST_ASSERT(!tpl->segments); |
| |
48 string_template_free(tpl); |
| |
49 |
| |
50 tpl = string_template_compile(a, cx_str("static")); |
| |
51 CX_TEST_ASSERT(tpl); // static |
| |
52 CX_TEST_ASSERT(tpl->segments); |
| |
53 CX_TEST_ASSERT(!tpl->segments->next); |
| |
54 CX_TEST_ASSERT(!cx_strcmp(tpl->segments->str, "static")); |
| |
55 CX_TEST_ASSERT(tpl->segments->type == STRING_SEGMENT_STR); |
| |
56 string_template_free(tpl); |
| |
57 |
| |
58 tpl = string_template_compile(a, cx_str("$$")); |
| |
59 CX_TEST_ASSERT(tpl); // $ |
| |
60 CX_TEST_ASSERT(tpl->segments); |
| |
61 CX_TEST_ASSERT(!tpl->segments->next); |
| |
62 CX_TEST_ASSERT(!cx_strcmp(tpl->segments->str, "$")); |
| |
63 CX_TEST_ASSERT(tpl->segments->type == STRING_SEGMENT_STR); |
| |
64 string_template_free(tpl); |
| |
65 |
| |
66 tpl = string_template_compile(a, cx_str("$var")); |
| |
67 CX_TEST_ASSERT(tpl); // var |
| |
68 CX_TEST_ASSERT(tpl->segments); |
| |
69 CX_TEST_ASSERT(!tpl->segments->next); |
| |
70 CX_TEST_ASSERT(!cx_strcmp(tpl->segments->str, "var")); |
| |
71 CX_TEST_ASSERT(tpl->segments->type == STRING_SEGMENT_VAR_PLACEHOLDER); |
| |
72 string_template_free(tpl); |
| |
73 |
| |
74 tpl = string_template_compile(a, cx_str("$12")); |
| |
75 CX_TEST_ASSERT(tpl); // 12 |
| |
76 CX_TEST_ASSERT(tpl->segments); |
| |
77 CX_TEST_ASSERT(!tpl->segments->next); |
| |
78 CX_TEST_ASSERT(!cx_strcmp(tpl->segments->str, "12")); |
| |
79 CX_TEST_ASSERT(tpl->segments->type == STRING_SEGMENT_NUM_PLACEHOLDER); |
| |
80 CX_TEST_ASSERT(tpl->segments->num == 12); |
| |
81 string_template_free(tpl); |
| |
82 |
| |
83 // double segment tests |
| |
84 tpl = string_template_compile(a, cx_str("test $var")); |
| |
85 CX_TEST_ASSERT(tpl); |
| |
86 s0 = tpl->segments; |
| |
87 CX_TEST_ASSERT(s0); |
| |
88 s1 = s0->next; |
| |
89 CX_TEST_ASSERT(s1); |
| |
90 CX_TEST_ASSERT(!cx_strcmp(s0->str, "test ")); |
| |
91 CX_TEST_ASSERT(s0->type == STRING_SEGMENT_STR); |
| |
92 CX_TEST_ASSERT(!cx_strcmp(s1->str, "var")); |
| |
93 CX_TEST_ASSERT(s1->type == STRING_SEGMENT_VAR_PLACEHOLDER); |
| |
94 CX_TEST_ASSERT(s1->next == NULL); |
| |
95 string_template_free(tpl); |
| |
96 |
| |
97 tpl = string_template_compile(a, cx_str("test ${var}")); |
| |
98 CX_TEST_ASSERT(tpl); |
| |
99 s0 = tpl->segments; |
| |
100 CX_TEST_ASSERT(s0); |
| |
101 s1 = s0->next; |
| |
102 CX_TEST_ASSERT(s1); |
| |
103 CX_TEST_ASSERT(!cx_strcmp(s0->str, "test ")); |
| |
104 CX_TEST_ASSERT(s0->type == STRING_SEGMENT_STR); |
| |
105 CX_TEST_ASSERT(!cx_strcmp(s1->str, "var")); |
| |
106 CX_TEST_ASSERT(s1->type == STRING_SEGMENT_VAR_PLACEHOLDER); |
| |
107 CX_TEST_ASSERT(s1->next == NULL); |
| |
108 string_template_free(tpl); |
| |
109 |
| |
110 tpl = string_template_compile(a, cx_str("$var test")); |
| |
111 CX_TEST_ASSERT(tpl); |
| |
112 s0 = tpl->segments; |
| |
113 CX_TEST_ASSERT(s0); |
| |
114 s1 = s0->next; |
| |
115 CX_TEST_ASSERT(s1); |
| |
116 CX_TEST_ASSERT(!cx_strcmp(s0->str, "var")); |
| |
117 CX_TEST_ASSERT(s0->type == STRING_SEGMENT_VAR_PLACEHOLDER); |
| |
118 CX_TEST_ASSERT(!cx_strcmp(s1->str, " test")); |
| |
119 CX_TEST_ASSERT(s1->type == STRING_SEGMENT_STR); |
| |
120 CX_TEST_ASSERT(s1->next == NULL); |
| |
121 string_template_free(tpl); |
| |
122 |
| |
123 tpl = string_template_compile(a, cx_str("$13 test")); |
| |
124 CX_TEST_ASSERT(tpl); |
| |
125 s0 = tpl->segments; |
| |
126 CX_TEST_ASSERT(s0); |
| |
127 s1 = s0->next; |
| |
128 CX_TEST_ASSERT(s1); |
| |
129 CX_TEST_ASSERT(!cx_strcmp(s0->str, "13")); |
| |
130 CX_TEST_ASSERT(s0->type == STRING_SEGMENT_NUM_PLACEHOLDER); |
| |
131 CX_TEST_ASSERT(!cx_strcmp(s1->str, " test")); |
| |
132 CX_TEST_ASSERT(s1->type == STRING_SEGMENT_STR); |
| |
133 CX_TEST_ASSERT(s1->next == NULL); |
| |
134 string_template_free(tpl); |
| |
135 |
| |
136 // multi segment tests |
| |
137 tpl = string_template_compile(a, cx_str("test$var1$var2")); |
| |
138 CX_TEST_ASSERT(tpl); |
| |
139 s0 = tpl->segments; |
| |
140 CX_TEST_ASSERT(s0); |
| |
141 s1 = s0->next; |
| |
142 CX_TEST_ASSERT(s1); |
| |
143 s2 = s1->next; |
| |
144 CX_TEST_ASSERT(2); |
| |
145 CX_TEST_ASSERT(!cx_strcmp(s0->str, "test")); |
| |
146 CX_TEST_ASSERT(s0->type == STRING_SEGMENT_STR); |
| |
147 CX_TEST_ASSERT(!cx_strcmp(s1->str, "var1")); |
| |
148 CX_TEST_ASSERT(s1->type == STRING_SEGMENT_VAR_PLACEHOLDER); |
| |
149 CX_TEST_ASSERT(!cx_strcmp(s2->str, "var2")); |
| |
150 CX_TEST_ASSERT(s2->type == STRING_SEGMENT_VAR_PLACEHOLDER); |
| |
151 CX_TEST_ASSERT(s2->next == NULL); |
| |
152 string_template_free(tpl); |
| |
153 |
| |
154 tpl = string_template_compile(a, cx_str("test/$1/$2")); |
| |
155 CX_TEST_ASSERT(tpl); |
| |
156 s0 = tpl->segments; |
| |
157 CX_TEST_ASSERT(s0); |
| |
158 s1 = s0->next; |
| |
159 CX_TEST_ASSERT(s1); |
| |
160 s2 = s1->next; |
| |
161 CX_TEST_ASSERT(2); |
| |
162 s3 = s2->next; |
| |
163 CX_TEST_ASSERT(s3); |
| |
164 CX_TEST_ASSERT(!cx_strcmp(s0->str, "test/")); |
| |
165 CX_TEST_ASSERT(s0->type == STRING_SEGMENT_STR); |
| |
166 CX_TEST_ASSERT(!cx_strcmp(s1->str, "1")); |
| |
167 CX_TEST_ASSERT(s1->type == STRING_SEGMENT_NUM_PLACEHOLDER); |
| |
168 CX_TEST_ASSERT(!cx_strcmp(s2->str, "/")); |
| |
169 CX_TEST_ASSERT(s2->type == STRING_SEGMENT_STR); |
| |
170 CX_TEST_ASSERT(!cx_strcmp(s3->str, "2")); |
| |
171 CX_TEST_ASSERT(s3->type == STRING_SEGMENT_NUM_PLACEHOLDER); |
| |
172 CX_TEST_ASSERT(s3->next == NULL); |
| |
173 string_template_free(tpl); |
| |
174 |
| |
175 tpl = string_template_compile(a, cx_str("ab$$cd/${1}/${2}")); |
| |
176 CX_TEST_ASSERT(tpl); |
| |
177 s0 = tpl->segments; |
| |
178 CX_TEST_ASSERT(s0); |
| |
179 s1 = s0->next; |
| |
180 CX_TEST_ASSERT(s1); |
| |
181 s2 = s1->next; |
| |
182 CX_TEST_ASSERT(2); |
| |
183 s3 = s2->next; |
| |
184 CX_TEST_ASSERT(s3); |
| |
185 CX_TEST_ASSERT(!cx_strcmp(s0->str, "ab$cd/")); |
| |
186 CX_TEST_ASSERT(s0->type == STRING_SEGMENT_STR); |
| |
187 CX_TEST_ASSERT(!cx_strcmp(s1->str, "1")); |
| |
188 CX_TEST_ASSERT(s1->type == STRING_SEGMENT_NUM_PLACEHOLDER); |
| |
189 CX_TEST_ASSERT(!cx_strcmp(s2->str, "/")); |
| |
190 CX_TEST_ASSERT(s2->type == STRING_SEGMENT_STR); |
| |
191 CX_TEST_ASSERT(!cx_strcmp(s3->str, "2")); |
| |
192 CX_TEST_ASSERT(s3->type == STRING_SEGMENT_NUM_PLACEHOLDER); |
| |
193 CX_TEST_ASSERT(s3->next == NULL); |
| |
194 string_template_free(tpl); |
| |
195 } |
| |
196 |
| |
197 cxMempoolFree(mp); |
| |
198 } |
| |
199 |
| |
200 CX_TEST(test_string_template_compile_error) { |
| |
201 // TODO |
| |
202 } |
| |
203 |
| |
204 static cxmutstr get_var(const CxAllocator *a, StringTemplateSegment *seg, void *userdata, WSBool *free_str) { |
| |
205 cxmutstr var_value = cx_strcat_a(a, 3, cx_str("var("), seg->str, cx_str(")")); |
| |
206 *free_str = TRUE; |
| |
207 return var_value; |
| |
208 } |
| |
209 |
| |
210 CX_TEST(test_string_template_write_to) { |
| |
211 CxMempool *mp = cxMempoolCreate(100, CX_MEMPOOL_TYPE_ADVANCED); |
| |
212 const CxAllocator *a = mp->allocator; |
| |
213 |
| |
214 CxBuffer buf; |
| |
215 cxBufferInit(&buf, NULL, 1024, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); |
| |
216 StringTemplate *tpl = NULL; |
| |
217 CX_TEST_DO { |
| |
218 tpl = string_template_compile(a, cx_str("hello world")); |
| |
219 ssize_t r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); |
| |
220 CX_TEST_ASSERT(r > 0); |
| |
221 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("hello world"))); |
| |
222 buf.pos = 0; |
| |
223 string_template_free(tpl); |
| |
224 |
| |
225 tpl = string_template_compile(a, cx_str("insert $var here")); |
| |
226 r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); |
| |
227 CX_TEST_ASSERT(r > 0); |
| |
228 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("insert var(var) here"))); |
| |
229 buf.pos = 0; |
| |
230 string_template_free(tpl); |
| |
231 |
| |
232 tpl = string_template_compile(a, cx_str("$1$2$3$4$5")); |
| |
233 r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); |
| |
234 CX_TEST_ASSERT(r > 0); |
| |
235 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("var(1)var(2)var(3)var(4)var(5)"))); |
| |
236 buf.pos = 0; |
| |
237 string_template_free(tpl); |
| |
238 |
| |
239 tpl = string_template_compile(a, cx_str("$$escape$$$myvar$$end")); |
| |
240 r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); |
| |
241 CX_TEST_ASSERT(r > 0); |
| |
242 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("$escape$var(myvar)$end"))); |
| |
243 buf.pos = 0; |
| |
244 string_template_free(tpl); |
| |
245 |
| |
246 tpl = string_template_compile(a, cx_str("$$$$${test}")); |
| |
247 r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); |
| |
248 CX_TEST_ASSERT(r > 0); |
| |
249 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("$$var(test)"))); |
| |
250 buf.pos = 0; |
| |
251 string_template_free(tpl); |
| |
252 |
| |
253 tpl = string_template_compile(a, cx_str("${123}end")); |
| |
254 r = string_template_write_to(tpl, a, get_var, NULL, &buf, (cx_write_func)cxBufferWrite); |
| |
255 CX_TEST_ASSERT(r > 0); |
| |
256 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, buf.pos), cx_str("var(123)end"))); |
| |
257 buf.pos = 0; |
| |
258 string_template_free(tpl); |
| |
259 } |
| |
260 cxBufferDestroy(&buf); |
| |
261 cxMempoolFree(mp); |
| |
262 } |
| |
263 |
| |
264 CX_TEST(test_string_template_build_string) { |
| |
265 CxMempool *mp = cxMempoolCreate(100, CX_MEMPOOL_TYPE_ADVANCED); |
| |
266 const CxAllocator *a = mp->allocator; |
| |
267 |
| |
268 StringTemplate *tpl = NULL; |
| |
269 CX_TEST_DO { |
| |
270 tpl = string_template_compile(a, cx_str("insert $var here")); |
| |
271 cxmutstr str = string_template_build_string(tpl, a, get_var, NULL); |
| |
272 CX_TEST_ASSERT(str.ptr); |
| |
273 CX_TEST_ASSERT(!cx_strcmp(str, cx_str("insert var(var) here"))); |
| |
274 string_template_free(tpl); |
| |
275 } |
| |
276 |
| |
277 cxMempoolFree(mp); |
| |
278 } |