1 /*
2 * string-test.c: a collection of libsvn_string tests
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 /* ====================================================================
25 To add tests, look toward the bottom of this file.
26
27 */
28
29
30
31 #include <stdio.h>
32 #include <string.h>
33
34 #include <apr_pools.h>
35 #include <apr_file_io.h>
36
37 #include "../svn_test.h"
38
39 #include "svn_io.h"
40 #include "svn_error.h"
41 #include "svn_sorts.h" /* MIN / MAX */
42 #include "svn_string.h" /* This includes <apr_*.h> */
43 #include "private/svn_string_private.h"
44
45 /* A quick way to create error messages. */
46 static svn_error_t *
fail(apr_pool_t * pool,const char * fmt,...)47 fail(apr_pool_t *pool, const char *fmt, ...)
48 {
49 va_list ap;
50 char *msg;
51
52 va_start(ap, fmt);
53 msg = apr_pvsprintf(pool, fmt, ap);
54 va_end(ap);
55
56 return svn_error_create(SVN_ERR_TEST_FAILED, 0, msg);
57 }
58
59
60 /* Some of our own global variables, for simplicity. Yes,
61 simplicity. */
62 static const char *phrase_1 = "hello, ";
63 static const char *phrase_2 = "a longish phrase of sorts, longer than 16 anyway";
64
65
66
67
68 static svn_error_t *
test1(apr_pool_t * pool)69 test1(apr_pool_t *pool)
70 {
71 svn_stringbuf_t *a = svn_stringbuf_create(phrase_1, pool);
72
73 /* Test that length, data, and null-termination are correct. */
74 if ((a->len == strlen(phrase_1)) && ((strcmp(a->data, phrase_1)) == 0))
75 return SVN_NO_ERROR;
76 else
77 return fail(pool, "test failed");
78 }
79
80
81 static svn_error_t *
test2(apr_pool_t * pool)82 test2(apr_pool_t *pool)
83 {
84 svn_stringbuf_t *b = svn_stringbuf_ncreate(phrase_2, 16, pool);
85
86 /* Test that length, data, and null-termination are correct. */
87 if ((b->len == 16) && ((strncmp(b->data, phrase_2, 16)) == 0))
88 return SVN_NO_ERROR;
89 else
90 return fail(pool, "test failed");
91 }
92
93
94 static svn_error_t *
test3(apr_pool_t * pool)95 test3(apr_pool_t *pool)
96 {
97 char *tmp;
98 size_t old_len;
99
100 svn_stringbuf_t *a = svn_stringbuf_create(phrase_1, pool);
101 svn_stringbuf_t *b = svn_stringbuf_ncreate(phrase_2, 16, pool);
102
103 tmp = apr_palloc(pool, (a->len + b->len + 1));
104 strcpy(tmp, a->data);
105 strcat(tmp, b->data);
106 old_len = a->len;
107 svn_stringbuf_appendstr(a, b);
108
109 /* Test that length, data, and null-termination are correct. */
110 if ((a->len == (old_len + b->len)) && ((strcmp(a->data, tmp)) == 0))
111 return SVN_NO_ERROR;
112 else
113 return fail(pool, "test failed");
114 }
115
116
117 static svn_error_t *
test4(apr_pool_t * pool)118 test4(apr_pool_t *pool)
119 {
120 svn_stringbuf_t *a = svn_stringbuf_create(phrase_1, pool);
121 svn_stringbuf_appendcstr(a, "new bytes to append");
122
123 /* Test that length, data, and null-termination are correct. */
124 if (svn_stringbuf_compare
125 (a, svn_stringbuf_create("hello, new bytes to append", pool)))
126 return SVN_NO_ERROR;
127 else
128 return fail(pool, "test failed");
129 }
130
131
132 static svn_error_t *
test5(apr_pool_t * pool)133 test5(apr_pool_t *pool)
134 {
135 svn_stringbuf_t *a = svn_stringbuf_create(phrase_1, pool);
136 svn_stringbuf_appendbytes(a, "new bytes to append", 9);
137
138 /* Test that length, data, and null-termination are correct. */
139 if (svn_stringbuf_compare
140 (a, svn_stringbuf_create("hello, new bytes", pool)))
141 return SVN_NO_ERROR;
142 else
143 return fail(pool, "test failed");
144 }
145
146
147 static svn_error_t *
test6(apr_pool_t * pool)148 test6(apr_pool_t *pool)
149 {
150 svn_stringbuf_t *a = svn_stringbuf_create(phrase_1, pool);
151 svn_stringbuf_t *b = svn_stringbuf_create(phrase_2, pool);
152 svn_stringbuf_t *c = svn_stringbuf_dup(a, pool);
153
154 /* Test that length, data, and null-termination are correct. */
155 if ((svn_stringbuf_compare(a, c)) && (! svn_stringbuf_compare(b, c)))
156 return SVN_NO_ERROR;
157 else
158 return fail(pool, "test failed");
159 }
160
161
162 static svn_error_t *
test7(apr_pool_t * pool)163 test7(apr_pool_t *pool)
164 {
165 char *tmp;
166 size_t tmp_len;
167
168 svn_stringbuf_t *c = svn_stringbuf_create(phrase_2, pool);
169
170 tmp_len = c->len;
171 tmp = apr_palloc(pool, c->len + 1);
172 strcpy(tmp, c->data);
173
174 svn_stringbuf_chop(c, 11);
175
176 if ((c->len == (tmp_len - 11))
177 && (strncmp(tmp, c->data, c->len) == 0)
178 && (c->data[c->len] == '\0'))
179 return SVN_NO_ERROR;
180 else
181 return fail(pool, "test failed");
182 }
183
184
185 static svn_error_t *
test8(apr_pool_t * pool)186 test8(apr_pool_t *pool)
187 {
188 svn_stringbuf_t *c = svn_stringbuf_create(phrase_2, pool);
189
190 svn_stringbuf_setempty(c);
191
192 if ((c->len == 0) && (c->data[0] == '\0'))
193 return SVN_NO_ERROR;
194 else
195 return fail(pool, "test failed");
196 }
197
198
199 static svn_error_t *
test9(apr_pool_t * pool)200 test9(apr_pool_t *pool)
201 {
202 svn_stringbuf_t *a = svn_stringbuf_create(phrase_1, pool);
203
204 svn_stringbuf_fillchar(a, '#');
205
206 if ((strcmp(a->data, "#######") == 0)
207 && ((strncmp(a->data, "############", a->len - 1)) == 0)
208 && (a->data[(a->len - 1)] == '#')
209 && (a->data[(a->len)] == '\0'))
210 return SVN_NO_ERROR;
211 else
212 return fail(pool, "test failed");
213 }
214
215
216
217 static svn_error_t *
test10(apr_pool_t * pool)218 test10(apr_pool_t *pool)
219 {
220 svn_stringbuf_t *s, *t;
221 size_t len_1 = 0;
222 size_t block_len_1 = 0;
223 size_t block_len_2 = 0;
224
225 s = svn_stringbuf_create("a small string", pool);
226 len_1 = (s->len);
227 block_len_1 = (s->blocksize);
228
229 t = svn_stringbuf_create(", plus a string more than twice as long", pool);
230 svn_stringbuf_appendstr(s, t);
231 block_len_2 = (s->blocksize);
232
233 /* Test that:
234 * - The initial block was at least the right fit.
235 * - The initial block was not excessively large.
236 * - The block more than doubled (because second string so long).
237 */
238 if ((len_1 <= (block_len_1 - 1))
239 && ((block_len_1 - len_1) <= APR_ALIGN_DEFAULT(1))
240 && ((block_len_2 / block_len_1) > 2))
241 return SVN_NO_ERROR;
242 else
243 return fail(pool, "test failed");
244 }
245
246
247 static svn_error_t *
test11(apr_pool_t * pool)248 test11(apr_pool_t *pool)
249 {
250 svn_stringbuf_t *s;
251
252 s = svn_stringbuf_createf(pool,
253 "This %s is used in test %d.",
254 "string",
255 12);
256
257 if (strcmp(s->data, "This string is used in test 12.") == 0)
258 return SVN_NO_ERROR;
259 else
260 return fail(pool, "test failed");
261 }
262
263 static svn_error_t *
check_string_contents(svn_stringbuf_t * string,const char * ftext,apr_size_t ftext_len,int repeat,apr_pool_t * pool)264 check_string_contents(svn_stringbuf_t *string,
265 const char *ftext,
266 apr_size_t ftext_len,
267 int repeat,
268 apr_pool_t *pool)
269 {
270 const char *data;
271 apr_size_t len;
272 int i;
273
274 data = string->data;
275 len = string->len;
276 for (i = 0; i < repeat; ++i)
277 {
278 if (len < ftext_len || memcmp(ftext, data, ftext_len))
279 return fail(pool, "comparing failed");
280 data += ftext_len;
281 len -= ftext_len;
282 }
283 if (len < 1 || memcmp(data, "\0", 1))
284 return fail(pool, "comparing failed");
285 data += 1;
286 len -= 1;
287 for (i = 0; i < repeat; ++i)
288 {
289 if (len < ftext_len || memcmp(ftext, data, ftext_len))
290 return fail(pool, "comparing failed");
291 data += ftext_len;
292 len -= ftext_len;
293 }
294
295 if (len)
296 return fail(pool, "comparing failed");
297
298 return SVN_NO_ERROR;
299 }
300
301
302 static svn_error_t *
test12(apr_pool_t * pool)303 test12(apr_pool_t *pool)
304 {
305 svn_stringbuf_t *s;
306 const char fname[] = "string-test.tmp";
307 apr_file_t *file;
308 apr_status_t status;
309 apr_size_t len;
310 int i, repeat;
311 const char ftext[] =
312 "Just some boring text. Avoiding newlines 'cos I don't know"
313 "if any of the Subversion platfoms will mangle them! There's no"
314 "need to test newline handling here anyway, it's not relevant.";
315
316 status = apr_file_open(&file, fname, APR_WRITE | APR_TRUNCATE | APR_CREATE,
317 APR_OS_DEFAULT, pool);
318 if (status)
319 return fail(pool, "opening file");
320
321 repeat = 100;
322
323 /* Some text */
324 for (i = 0; i < repeat; ++i)
325 {
326 status = apr_file_write_full(file, ftext, sizeof(ftext) - 1, &len);
327 if (status)
328 return fail(pool, "writing file");
329 }
330
331 /* A null byte, I don't *think* any of our platforms mangle these */
332 status = apr_file_write_full(file, "\0", 1, &len);
333 if (status)
334 return fail(pool, "writing file");
335
336 /* Some more text */
337 for (i = 0; i < repeat; ++i)
338 {
339 status = apr_file_write_full(file, ftext, sizeof(ftext) - 1, &len);
340 if (status)
341 return fail(pool, "writing file");
342 }
343
344 status = apr_file_close(file);
345 if (status)
346 return fail(pool, "closing file");
347
348 SVN_ERR(svn_stringbuf_from_file(&s, fname, pool));
349 SVN_ERR(check_string_contents(s, ftext, sizeof(ftext) - 1, repeat, pool));
350
351 /* Reset to avoid false positives */
352 s = NULL;
353
354 status = apr_file_open(&file, fname, APR_READ, APR_OS_DEFAULT, pool);
355 if (status)
356 return fail(pool, "opening file");
357
358 SVN_ERR(svn_stringbuf_from_aprfile(&s, file, pool));
359 SVN_ERR(check_string_contents(s, ftext, sizeof(ftext) - 1, repeat, pool));
360
361 status = apr_file_close(file);
362 if (status)
363 return fail(pool, "closing file");
364
365 status = apr_file_remove(fname, pool);
366 if (status)
367 return fail(pool, "removing file");
368
369 return SVN_NO_ERROR;
370 }
371
372 /* Helper function for checking correctness of find_char_backward */
373 static svn_error_t *
test_find_char_backward(const char * data,apr_size_t len,char ch,apr_size_t pos,apr_pool_t * pool)374 test_find_char_backward(const char* data,
375 apr_size_t len,
376 char ch,
377 apr_size_t pos,
378 apr_pool_t *pool)
379 {
380 apr_size_t i;
381
382 svn_stringbuf_t *a = svn_stringbuf_create(data, pool);
383 i = svn_stringbuf_find_char_backward(a, ch);
384
385 if (i == pos)
386 return SVN_NO_ERROR;
387 else
388 return fail(pool, "test failed");
389 }
390
391 static svn_error_t *
test13(apr_pool_t * pool)392 test13(apr_pool_t *pool)
393 {
394 svn_stringbuf_t *a = svn_stringbuf_create("test, test", pool);
395
396 return test_find_char_backward(a->data, a->len, ',', 4, pool);
397 }
398
399 static svn_error_t *
test14(apr_pool_t * pool)400 test14(apr_pool_t *pool)
401 {
402 svn_stringbuf_t *a = svn_stringbuf_create(",test test", pool);
403
404 return test_find_char_backward(a->data, a->len, ',', 0, pool);
405 }
406
407 static svn_error_t *
test15(apr_pool_t * pool)408 test15(apr_pool_t *pool)
409 {
410 svn_stringbuf_t *a = svn_stringbuf_create("testing,", pool);
411
412 return test_find_char_backward(a->data,
413 a->len,
414 ',',
415 a->len - 1,
416 pool);
417 }
418
419 static svn_error_t *
test16(apr_pool_t * pool)420 test16(apr_pool_t *pool)
421 {
422 svn_stringbuf_t *a = svn_stringbuf_create_empty(pool);
423
424 return test_find_char_backward(a->data, a->len, ',', 0, pool);
425 }
426
427 static svn_error_t *
test17(apr_pool_t * pool)428 test17(apr_pool_t *pool)
429 {
430 svn_stringbuf_t *a = svn_stringbuf_create("test test test", pool);
431
432 return test_find_char_backward(a->data,
433 a->len,
434 ',',
435 a->len,
436 pool);
437 }
438
439 static svn_error_t *
test_first_non_whitespace(const char * str,const apr_size_t pos,apr_pool_t * pool)440 test_first_non_whitespace(const char *str,
441 const apr_size_t pos,
442 apr_pool_t *pool)
443 {
444 apr_size_t i;
445
446 svn_stringbuf_t *a = svn_stringbuf_create(str, pool);
447
448 i = svn_stringbuf_first_non_whitespace(a);
449
450 if (i == pos)
451 return SVN_NO_ERROR;
452 else
453 return fail(pool, "test failed");
454 }
455
456 static svn_error_t *
test18(apr_pool_t * pool)457 test18(apr_pool_t *pool)
458 {
459 return test_first_non_whitespace(" \ttest", 4, pool);
460 }
461
462 static svn_error_t *
test19(apr_pool_t * pool)463 test19(apr_pool_t *pool)
464 {
465 return test_first_non_whitespace("test", 0, pool);
466 }
467
468 static svn_error_t *
test20(apr_pool_t * pool)469 test20(apr_pool_t *pool)
470 {
471 return test_first_non_whitespace(" ", 3, pool);
472 }
473
474 static svn_error_t *
test21(apr_pool_t * pool)475 test21(apr_pool_t *pool)
476 {
477 svn_stringbuf_t *a = svn_stringbuf_create(" \ttest\t\t \t ", pool);
478 svn_stringbuf_t *b = svn_stringbuf_create("test", pool);
479
480 svn_stringbuf_strip_whitespace(a);
481
482 if (svn_stringbuf_compare(a, b))
483 return SVN_NO_ERROR;
484 else
485 return fail(pool, "test failed");
486 }
487
488 static svn_error_t *
test_stringbuf_unequal(const char * str1,const char * str2,apr_pool_t * pool)489 test_stringbuf_unequal(const char* str1,
490 const char* str2,
491 apr_pool_t *pool)
492 {
493 svn_stringbuf_t *a = svn_stringbuf_create(str1, pool);
494 svn_stringbuf_t *b = svn_stringbuf_create(str2, pool);
495
496 if (svn_stringbuf_compare(a, b))
497 return fail(pool, "test failed");
498 else
499 return SVN_NO_ERROR;
500 }
501
502 static svn_error_t *
test22(apr_pool_t * pool)503 test22(apr_pool_t *pool)
504 {
505 return test_stringbuf_unequal("abc", "abcd", pool);
506 }
507
508 static svn_error_t *
test23(apr_pool_t * pool)509 test23(apr_pool_t *pool)
510 {
511 return test_stringbuf_unequal("abc", "abb", pool);
512 }
513
514 static svn_error_t *
test24(apr_pool_t * pool)515 test24(apr_pool_t *pool)
516 {
517 char buffer[SVN_INT64_BUFFER_SIZE];
518 apr_size_t length;
519
520 length = svn__i64toa(buffer, 0);
521 SVN_TEST_ASSERT(length == 1);
522 SVN_TEST_STRING_ASSERT(buffer, "0");
523
524 length = svn__i64toa(buffer, APR_INT64_MIN);
525 SVN_TEST_ASSERT(length == 20);
526 SVN_TEST_STRING_ASSERT(buffer, "-9223372036854775808");
527
528 length = svn__i64toa(buffer, APR_INT64_MAX);
529 SVN_TEST_ASSERT(length == 19);
530 SVN_TEST_STRING_ASSERT(buffer, "9223372036854775807");
531
532 length = svn__ui64toa(buffer, 0u);
533 SVN_TEST_ASSERT(length == 1);
534 SVN_TEST_STRING_ASSERT(buffer, "0");
535
536 length = svn__ui64toa(buffer, APR_UINT64_MAX);
537 SVN_TEST_ASSERT(length == 20);
538 SVN_TEST_STRING_ASSERT(buffer, "18446744073709551615");
539
540 return SVN_NO_ERROR;
541 }
542
543 static svn_error_t *
sub_test_base36(apr_uint64_t value,const char * base36)544 sub_test_base36(apr_uint64_t value, const char *base36)
545 {
546 char buffer[SVN_INT64_BUFFER_SIZE];
547 apr_size_t length;
548 apr_size_t expected_length = strlen(base36);
549 const char *end = buffer;
550 apr_uint64_t result;
551
552 length = svn__ui64tobase36(buffer, value);
553 SVN_TEST_ASSERT(length == expected_length);
554 SVN_TEST_STRING_ASSERT(buffer, base36);
555
556 result = svn__base36toui64(&end, buffer);
557 SVN_TEST_ASSERT(end - buffer == length);
558 SVN_TEST_ASSERT(result == value);
559
560 result = svn__base36toui64(NULL, buffer);
561 SVN_TEST_ASSERT(result == value);
562
563 return SVN_NO_ERROR;
564 }
565
566 static svn_error_t *
test_base36(apr_pool_t * pool)567 test_base36(apr_pool_t *pool)
568 {
569 SVN_ERR(sub_test_base36(0, "0"));
570 SVN_ERR(sub_test_base36(APR_UINT64_C(1234567890), "kf12oi"));
571 SVN_ERR(sub_test_base36(APR_UINT64_C(0x7fffffffffffffff), "1y2p0ij32e8e7"));
572 SVN_ERR(sub_test_base36(APR_UINT64_C(0x8000000000000000), "1y2p0ij32e8e8"));
573 SVN_ERR(sub_test_base36(APR_UINT64_MAX, "3w5e11264sgsf"));
574
575 return SVN_NO_ERROR;
576 }
577
578 static svn_error_t *
expect_stringbuf_equal(const svn_stringbuf_t * str1,const char * str2,apr_pool_t * pool)579 expect_stringbuf_equal(const svn_stringbuf_t* str1,
580 const char* str2,
581 apr_pool_t *pool)
582 {
583 if (svn_stringbuf_compare(str1, svn_stringbuf_create(str2, pool)))
584 return SVN_NO_ERROR;
585 else
586 return fail(pool, "test failed");
587 }
588
589 static svn_error_t *
test_stringbuf_insert(apr_pool_t * pool)590 test_stringbuf_insert(apr_pool_t *pool)
591 {
592 svn_stringbuf_t *a = svn_stringbuf_create("st , ", pool);
593
594 svn_stringbuf_insert(a, 0, "teflon", 2);
595 SVN_TEST_STRING_ASSERT(a->data, "test , ");
596
597 svn_stringbuf_insert(a, 5, "hllo", 4);
598 SVN_TEST_STRING_ASSERT(a->data, "test hllo, ");
599
600 svn_stringbuf_insert(a, 6, a->data + 1, 1);
601 SVN_TEST_STRING_ASSERT(a->data, "test hello, ");
602
603 svn_stringbuf_insert(a, 12, "world class", 5);
604 SVN_TEST_STRING_ASSERT(a->data, "test hello, world");
605
606 svn_stringbuf_insert(a, 1200, "!", 1);
607 SVN_TEST_STRING_ASSERT(a->data, "test hello, world!");
608
609 svn_stringbuf_insert(a, 4, "\0-\0", 3);
610 SVN_TEST_ASSERT(svn_stringbuf_compare(a,
611 svn_stringbuf_ncreate("test\0-\0 hello, world!",
612 21, pool)));
613
614 svn_stringbuf_insert(a, 14, a->data + 4, 3);
615 SVN_TEST_ASSERT(svn_stringbuf_compare(a,
616 svn_stringbuf_ncreate("test\0-\0 hello,\0-\0 world!",
617 24, pool)));
618
619 return SVN_NO_ERROR;
620 }
621
622 static svn_error_t *
test_stringbuf_remove(apr_pool_t * pool)623 test_stringbuf_remove(apr_pool_t *pool)
624 {
625 svn_stringbuf_t *a = svn_stringbuf_create("test hello, world!", pool);
626
627 svn_stringbuf_remove(a, 0, 2);
628 SVN_TEST_STRING_ASSERT(a->data, "st hello, world!");
629
630 svn_stringbuf_remove(a, 2, 2);
631 SVN_TEST_STRING_ASSERT(a->data, "stello, world!");
632
633 svn_stringbuf_remove(a, 5, 200);
634 SVN_TEST_STRING_ASSERT(a->data, "stell");
635
636 svn_stringbuf_remove(a, 1200, 393);
637 SVN_ERR(expect_stringbuf_equal(a, "stell", pool));
638
639 svn_stringbuf_remove(a, APR_SIZE_MAX, 2);
640 SVN_ERR(expect_stringbuf_equal(a, "stell", pool));
641
642 svn_stringbuf_remove(a, 1, APR_SIZE_MAX);
643 SVN_ERR(expect_stringbuf_equal(a, "s", pool));
644
645 return SVN_NO_ERROR;
646 }
647
648 static svn_error_t *
test_stringbuf_replace(apr_pool_t * pool)649 test_stringbuf_replace(apr_pool_t *pool)
650 {
651 svn_stringbuf_t *a = svn_stringbuf_create("odd with some world?", pool);
652
653 svn_stringbuf_replace(a, 0, 3, "tester", 4);
654 SVN_TEST_STRING_ASSERT(a->data, "test with some world?");
655
656 svn_stringbuf_replace(a, 5, 10, "hllo, coder", 6);
657 SVN_TEST_STRING_ASSERT(a->data, "test hllo, world?");
658
659 svn_stringbuf_replace(a, 6, 0, a->data + 1, 1);
660 SVN_TEST_STRING_ASSERT(a->data, "test hello, world?");
661
662 svn_stringbuf_replace(a, 17, 10, "!", 1);
663 SVN_TEST_STRING_ASSERT(a->data, "test hello, world!");
664
665 svn_stringbuf_replace(a, 1200, 199, "!!", 2);
666 SVN_TEST_STRING_ASSERT(a->data, "test hello, world!!!");
667
668 svn_stringbuf_replace(a, 10, 2, "\0-\0", 3);
669 SVN_TEST_ASSERT(svn_stringbuf_compare(a,
670 svn_stringbuf_ncreate("test hello\0-\0world!!!",
671 21, pool)));
672
673 svn_stringbuf_replace(a, 10, 3, a->data + 10, 3);
674 SVN_TEST_ASSERT(svn_stringbuf_compare(a,
675 svn_stringbuf_ncreate("test hello\0-\0world!!!",
676 21, pool)));
677
678 svn_stringbuf_replace(a, 19, 1, a->data + 10, 3);
679 SVN_TEST_ASSERT(svn_stringbuf_compare(a,
680 svn_stringbuf_ncreate("test hello\0-\0world!\0-\0!",
681 23, pool)));
682
683 svn_stringbuf_replace(a, 1, APR_SIZE_MAX, "x", 1);
684 SVN_ERR(expect_stringbuf_equal(a, "tx", pool));
685
686 svn_stringbuf_replace(a, APR_SIZE_MAX, APR_SIZE_MAX, "y", 1);
687 SVN_ERR(expect_stringbuf_equal(a, "txy", pool));
688
689 return SVN_NO_ERROR;
690 }
691
692 static svn_error_t *
test_string_similarity(apr_pool_t * pool)693 test_string_similarity(apr_pool_t *pool)
694 {
695 const struct sim_score_test_t
696 {
697 const char *stra;
698 const char *strb;
699 apr_size_t lcs;
700 unsigned int score;
701 } tests[] =
702 {
703 #define SCORE(lcs, len) \
704 ((2 * SVN_STRING__SIM_RANGE_MAX * (lcs) + (len)/2) / (len))
705
706 /* Equality */
707 {"", "", 0, SVN_STRING__SIM_RANGE_MAX},
708 {"quoth", "quoth", 5, SCORE(5, 5+5)},
709
710 /* Deletion at start */
711 {"quoth", "uoth", 4, SCORE(4, 5+4)},
712 {"uoth", "quoth", 4, SCORE(4, 4+5)},
713
714 /* Deletion at end */
715 {"quoth", "quot", 4, SCORE(4, 5+4)},
716 {"quot", "quoth", 4, SCORE(4, 4+5)},
717
718 /* Insertion at start */
719 {"quoth", "Xquoth", 5, SCORE(5, 5+6)},
720 {"Xquoth", "quoth", 5, SCORE(5, 6+5)},
721
722 /* Insertion at end */
723 {"quoth", "quothX", 5, SCORE(5, 5+6)},
724 {"quothX", "quoth", 5, SCORE(5, 6+5)},
725
726 /* Insertion in middle */
727 {"quoth", "quoXth", 5, SCORE(5, 5+6)},
728 {"quoXth", "quoth", 5, SCORE(5, 6+5)},
729
730 /* Transposition at start */
731 {"quoth", "uqoth", 4, SCORE(4, 5+5)},
732 {"uqoth", "quoth", 4, SCORE(4, 5+5)},
733
734 /* Transposition at end */
735 {"quoth", "quoht", 4, SCORE(4, 5+5)},
736 {"quoht", "quoth", 4, SCORE(4, 5+5)},
737
738 /* Transposition in middle */
739 {"quoth", "qutoh", 4, SCORE(4, 5+5)},
740 {"qutoh", "quoth", 4, SCORE(4, 5+5)},
741
742 /* Difference */
743 {"quoth", "raven", 0, SCORE(0, 5+5)},
744 {"raven", "quoth", 0, SCORE(0, 5+5)},
745 {"x", "", 0, SCORE(0, 1+0)},
746 {"", "x", 0, SCORE(0, 0+1)},
747 {"", "quoth", 0, SCORE(0, 0+5)},
748 {"quoth", "", 0, SCORE(0, 5+0)},
749 {"quoth", "the raven", 2, SCORE(2, 5+9)},
750 {"the raven", "quoth", 2, SCORE(2, 5+9)},
751 {NULL, NULL}
752 };
753
754 const struct sim_score_test_t *t;
755 svn_membuf_t buffer;
756
757 svn_membuf__create(&buffer, 0, pool);
758 for (t = tests; t->stra; ++t)
759 {
760 apr_size_t lcs;
761 const apr_size_t score =
762 svn_cstring__similarity(t->stra, t->strb, &buffer, &lcs);
763 /*
764 fprintf(stderr,
765 "lcs %s ~ %s score %.6f (%"APR_SIZE_T_FMT
766 ") expected %.6f (%"APR_SIZE_T_FMT"))\n",
767 t->stra, t->strb, score/1.0/SVN_STRING__SIM_RANGE_MAX,
768 lcs, t->score/1.0/SVN_STRING__SIM_RANGE_MAX, t->lcs);
769 */
770 if (score != t->score)
771 return fail(pool, "%s ~ %s score %.6f <> expected %.6f",
772 t->stra, t->strb,
773 score/1.0/SVN_STRING__SIM_RANGE_MAX,
774 t->score/1.0/SVN_STRING__SIM_RANGE_MAX);
775
776 if (lcs != t->lcs)
777 return fail(pool,
778 "%s ~ %s lcs %"APR_SIZE_T_FMT
779 " <> expected %"APR_SIZE_T_FMT,
780 t->stra, t->strb, lcs, t->lcs);
781 }
782
783 /* Test partial similarity */
784 {
785 const svn_string_t foo = {"svn:foo", 4};
786 const svn_string_t bar = {"svn:bar", 4};
787 if (SVN_STRING__SIM_RANGE_MAX
788 != svn_string__similarity(&foo, &bar, &buffer, NULL))
789 return fail(pool, "'%s'[:4] ~ '%s'[:4] found different",
790 foo.data, bar.data);
791 }
792
793 return SVN_NO_ERROR;
794 }
795
796 static svn_error_t *
test_string_matching(apr_pool_t * pool)797 test_string_matching(apr_pool_t *pool)
798 {
799 const struct test_data_t
800 {
801 const char *a;
802 const char *b;
803 apr_size_t match_len;
804 apr_size_t rmatch_len;
805 }
806 tests[] =
807 {
808 /* edge cases */
809 {"", "", 0, 0},
810 {"", "x", 0, 0},
811 {"x", "", 0, 0},
812 {"x", "x", 1, 1},
813 {"", "1234567890abcdef", 0, 0},
814 {"1234567890abcdef", "", 0, 0},
815 {"1234567890abcdef", "1234567890abcdef", 16, 16},
816
817 /* left-side matches */
818 {"x", "y", 0, 0},
819 {"ax", "ay", 1, 0},
820 {"ax", "a", 1, 0},
821 {"a", "ay", 1, 0},
822 {"1234567890abcdef", "1234567890abcdeg", 15, 0},
823 {"1234567890abcdef_", "1234567890abcdefg", 16, 0},
824 {"12345678_0abcdef", "1234567890abcdeg", 8, 0},
825 {"1234567890abcdef", "12345678", 8, 0},
826 {"12345678", "1234567890abcdef", 8, 0},
827 {"12345678_0ab", "1234567890abcdef", 8, 0},
828
829 /* right-side matches */
830 {"xa", "ya", 0, 1},
831 {"xa", "a", 0, 1},
832 {"a", "ya", 0, 1},
833 {"_234567890abcdef", "1234567890abcdef", 0, 15},
834 {"_1234567890abcdef", "x1234567890abcdef", 0, 16},
835 {"1234567_90abcdef", "_1234567890abcdef", 0, 8},
836 {"1234567890abcdef", "90abcdef", 0, 8},
837 {"90abcdef", "1234567890abcdef", 0, 8},
838 {"8_0abcdef", "7890abcdef", 0, 7},
839
840 /* two-side matches */
841 {"bxa", "bya", 1, 1},
842 {"bxa", "ba", 1, 1},
843 {"ba", "bya", 1, 1},
844 {"1234567_90abcdef", "1234567890abcdef", 7, 8},
845 {"12345678_90abcdef", "1234567890abcdef", 8, 8},
846 {"12345678_0abcdef", "1234567890abcdef", 8, 7},
847 {"123456_abcdef", "1234sdffdssdf567890abcdef", 4, 6},
848 {"1234567890abcdef", "12345678ef", 8, 2},
849 {"x_234567890abcdef", "x1234567890abcdef", 1, 15},
850 {"1234567890abcdefx", "1234567890abcdex", 15, 1},
851
852 /* list terminator */
853 {NULL}
854 };
855
856 const struct test_data_t *test;
857 for (test = tests; test->a != NULL; ++test)
858 {
859 apr_size_t a_len = strlen(test->a);
860 apr_size_t b_len = strlen(test->b);
861 apr_size_t max_match = MIN(a_len, b_len);
862 apr_size_t match_len
863 = svn_cstring__match_length(test->a, test->b, max_match);
864 apr_size_t rmatch_len
865 = svn_cstring__reverse_match_length(test->a + a_len, test->b + b_len,
866 max_match);
867
868 SVN_TEST_ASSERT(match_len == test->match_len);
869 SVN_TEST_ASSERT(rmatch_len == test->rmatch_len);
870 }
871
872 return SVN_NO_ERROR;
873 }
874
875 static svn_error_t *
test_cstring_skip_prefix(apr_pool_t * pool)876 test_cstring_skip_prefix(apr_pool_t *pool)
877 {
878 SVN_TEST_STRING_ASSERT(svn_cstring_skip_prefix("12345", "12345"),
879 "");
880 SVN_TEST_STRING_ASSERT(svn_cstring_skip_prefix("12345", "123"),
881 "45");
882 SVN_TEST_STRING_ASSERT(svn_cstring_skip_prefix("12345", ""),
883 "12345");
884 SVN_TEST_STRING_ASSERT(svn_cstring_skip_prefix("12345", "23"),
885 NULL);
886 SVN_TEST_STRING_ASSERT(svn_cstring_skip_prefix("1", "12"),
887 NULL);
888 SVN_TEST_STRING_ASSERT(svn_cstring_skip_prefix("", ""),
889 "");
890 SVN_TEST_STRING_ASSERT(svn_cstring_skip_prefix("", "12"),
891 NULL);
892
893 return SVN_NO_ERROR;
894 }
895
896 static svn_error_t *
test_stringbuf_replace_all(apr_pool_t * pool)897 test_stringbuf_replace_all(apr_pool_t *pool)
898 {
899 svn_stringbuf_t *s = svn_stringbuf_create("abccabcdabc", pool);
900
901 /* no replacement */
902 SVN_TEST_ASSERT(0 == svn_stringbuf_replace_all(s, "xyz", "k"));
903 SVN_TEST_STRING_ASSERT(s->data, "abccabcdabc");
904 SVN_TEST_ASSERT(s->len == 11);
905
906 /* replace at string head: grow */
907 SVN_TEST_ASSERT(1 == svn_stringbuf_replace_all(s, "abcc", "xyabcz"));
908 SVN_TEST_STRING_ASSERT(s->data, "xyabczabcdabc");
909 SVN_TEST_ASSERT(s->len == 13);
910
911 /* replace at string head: shrink */
912 SVN_TEST_ASSERT(1 == svn_stringbuf_replace_all(s, "xyabcz", "abcc"));
913 SVN_TEST_STRING_ASSERT(s->data, "abccabcdabc");
914 SVN_TEST_ASSERT(s->len == 11);
915
916 /* replace at string tail: grow */
917 SVN_TEST_ASSERT(1 == svn_stringbuf_replace_all(s, "dabc", "xyabcz"));
918 SVN_TEST_STRING_ASSERT(s->data, "abccabcxyabcz");
919 SVN_TEST_ASSERT(s->len == 13);
920
921 /* replace at string tail: shrink */
922 SVN_TEST_ASSERT(1 == svn_stringbuf_replace_all(s, "xyabcz", "dabc"));
923 SVN_TEST_STRING_ASSERT(s->data, "abccabcdabc");
924 SVN_TEST_ASSERT(s->len == 11);
925
926 /* replace at multiple locations: grow */
927 SVN_TEST_ASSERT(3 == svn_stringbuf_replace_all(s, "ab", "xyabz"));
928 SVN_TEST_STRING_ASSERT(s->data, "xyabzccxyabzcdxyabzc");
929 SVN_TEST_ASSERT(s->len == 20);
930
931 /* replace at multiple locations: shrink */
932 SVN_TEST_ASSERT(3 == svn_stringbuf_replace_all(s, "xyabz", "ab"));
933 SVN_TEST_STRING_ASSERT(s->data, "abccabcdabc");
934 SVN_TEST_ASSERT(s->len == 11);
935
936 /* replace at multiple locations: same length */
937 SVN_TEST_ASSERT(3 == svn_stringbuf_replace_all(s, "abc", "xyz"));
938 SVN_TEST_STRING_ASSERT(s->data, "xyzcxyzdxyz");
939 SVN_TEST_ASSERT(s->len == 11);
940
941 /* replace at multiple locations: overlapping */
942 s = svn_stringbuf_create("aaaaaaaaaaa", pool);
943 SVN_TEST_ASSERT(5 == svn_stringbuf_replace_all(s, "aa", "aaa"));
944 SVN_TEST_STRING_ASSERT(s->data, "aaaaaaaaaaaaaaaa");
945 SVN_TEST_ASSERT(s->len == 16);
946
947 SVN_TEST_ASSERT(5 == svn_stringbuf_replace_all(s, "aaa", "aa"));
948 SVN_TEST_STRING_ASSERT(s->data, "aaaaaaaaaaa");
949 SVN_TEST_ASSERT(s->len == 11);
950
951 return SVN_NO_ERROR;
952 }
953
954 static svn_error_t *
test_stringbuf_leftchop(apr_pool_t * pool)955 test_stringbuf_leftchop(apr_pool_t *pool)
956 {
957 svn_stringbuf_t *s;
958
959 s = svn_stringbuf_create("abcd", pool);
960 svn_stringbuf_leftchop(s, 0);
961 SVN_TEST_ASSERT(s->len == 4);
962 SVN_TEST_STRING_ASSERT(s->data, "abcd");
963
964 svn_stringbuf_leftchop(s, 2);
965 SVN_TEST_ASSERT(s->len == 2);
966 SVN_TEST_STRING_ASSERT(s->data, "cd");
967
968 svn_stringbuf_leftchop(s, 4);
969 SVN_TEST_ASSERT(s->len == 0);
970 SVN_TEST_STRING_ASSERT(s->data, "");
971
972 s = svn_stringbuf_create("abcd", pool);
973 svn_stringbuf_leftchop(s, 4);
974 SVN_TEST_ASSERT(s->len == 0);
975 SVN_TEST_STRING_ASSERT(s->data, "");
976
977 s = svn_stringbuf_create_empty(pool);
978 svn_stringbuf_leftchop(s, 0);
979 SVN_TEST_ASSERT(s->len == 0);
980 SVN_TEST_STRING_ASSERT(s->data, "");
981
982 svn_stringbuf_leftchop(s, 2);
983 SVN_TEST_ASSERT(s->len == 0);
984 SVN_TEST_STRING_ASSERT(s->data, "");
985
986 return SVN_NO_ERROR;
987 }
988
989 static svn_error_t *
test_stringbuf_set(apr_pool_t * pool)990 test_stringbuf_set(apr_pool_t *pool)
991 {
992 svn_stringbuf_t *str = svn_stringbuf_create_empty(pool);
993
994 SVN_TEST_STRING_ASSERT(str->data, "");
995 SVN_TEST_INT_ASSERT(str->len, 0);
996
997 svn_stringbuf_set(str, "0123456789");
998 SVN_TEST_STRING_ASSERT(str->data, "0123456789");
999 SVN_TEST_INT_ASSERT(str->len, 10);
1000
1001 svn_stringbuf_set(str, "");
1002 SVN_TEST_STRING_ASSERT(str->data, "");
1003 SVN_TEST_INT_ASSERT(str->len, 0);
1004
1005 svn_stringbuf_set(str, "0123456789abcdef");
1006 SVN_TEST_STRING_ASSERT(str->data, "0123456789abcdef");
1007 SVN_TEST_INT_ASSERT(str->len, 16);
1008
1009 svn_stringbuf_set(str, "t");
1010 SVN_TEST_STRING_ASSERT(str->data, "t");
1011 SVN_TEST_INT_ASSERT(str->len, 1);
1012
1013 return SVN_NO_ERROR;
1014 }
1015
1016 static svn_error_t *
test_cstring_join(apr_pool_t * pool)1017 test_cstring_join(apr_pool_t *pool)
1018 {
1019 apr_array_header_t *arr;
1020
1021 {
1022 arr = apr_array_make(pool, 0, sizeof(const char *));
1023
1024 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "", FALSE, pool), "");
1025 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "", TRUE, pool), "");
1026 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, ";", FALSE, pool), "");
1027 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, ";", TRUE, pool), "");
1028 }
1029
1030 {
1031 arr = apr_array_make(pool, 0, sizeof(const char *));
1032 APR_ARRAY_PUSH(arr, const char *) = "";
1033
1034 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "", FALSE, pool), "");
1035 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "", TRUE, pool), "");
1036 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, ";", FALSE, pool), "");
1037 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, ";", TRUE, pool), ";");
1038 }
1039
1040 {
1041 arr = apr_array_make(pool, 0, sizeof(const char *));
1042 APR_ARRAY_PUSH(arr, const char *) = "ab";
1043 APR_ARRAY_PUSH(arr, const char *) = "cd";
1044
1045 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "", FALSE, pool), "abcd");
1046 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "", TRUE, pool), "abcd");
1047 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, ";", FALSE, pool), "ab;cd");
1048 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, ";", TRUE, pool), "ab;cd;");
1049 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "//", FALSE, pool), "ab//cd");
1050 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "//", TRUE, pool), "ab//cd//");
1051 }
1052
1053 {
1054 arr = apr_array_make(pool, 0, sizeof(const char *));
1055 APR_ARRAY_PUSH(arr, const char *) = "";
1056 APR_ARRAY_PUSH(arr, const char *) = "ab";
1057 APR_ARRAY_PUSH(arr, const char *) = "";
1058
1059 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "", FALSE, pool), "ab");
1060 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "", TRUE, pool), "ab");
1061 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, ";", FALSE, pool), ";ab;");
1062 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, ";", TRUE, pool), ";ab;;");
1063 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "//", FALSE, pool), "//ab//");
1064 SVN_TEST_STRING_ASSERT(svn_cstring_join2(arr, "//", TRUE, pool), "//ab////");
1065 }
1066
1067 return SVN_NO_ERROR;
1068 }
1069
1070 /*
1071 ====================================================================
1072 If you add a new test to this file, update this array.
1073
1074 (These globals are required by our included main())
1075 */
1076
1077 /* An array of all test functions */
1078
1079 static int max_threads = 1;
1080
1081 static struct svn_test_descriptor_t test_funcs[] =
1082 {
1083 SVN_TEST_NULL,
1084 SVN_TEST_PASS2(test1,
1085 "make svn_stringbuf_t from cstring"),
1086 SVN_TEST_PASS2(test2,
1087 "make svn_stringbuf_t from substring of cstring"),
1088 SVN_TEST_PASS2(test3,
1089 "append svn_stringbuf_t to svn_stringbuf_t"),
1090 SVN_TEST_PASS2(test4,
1091 "append C string to svn_stringbuf_t"),
1092 SVN_TEST_PASS2(test5,
1093 "append bytes, then compare two strings"),
1094 SVN_TEST_PASS2(test6,
1095 "dup two strings, then compare"),
1096 SVN_TEST_PASS2(test7,
1097 "chopping a string"),
1098 SVN_TEST_PASS2(test8,
1099 "emptying a string"),
1100 SVN_TEST_PASS2(test9,
1101 "fill string with hashmarks"),
1102 SVN_TEST_PASS2(test10,
1103 "block initialization and growth"),
1104 SVN_TEST_PASS2(test11,
1105 "formatting strings from varargs"),
1106 SVN_TEST_PASS2(test12,
1107 "create string from file"),
1108 SVN_TEST_PASS2(test13,
1109 "find_char_backward; middle case"),
1110 SVN_TEST_PASS2(test14,
1111 "find_char_backward; 0 case"),
1112 SVN_TEST_PASS2(test15,
1113 "find_char_backward; strlen - 1 case"),
1114 SVN_TEST_PASS2(test16,
1115 "find_char_backward; len = 0 case"),
1116 SVN_TEST_PASS2(test17,
1117 "find_char_backward; no occurrence case"),
1118 SVN_TEST_PASS2(test18,
1119 "check whitespace removal; common case"),
1120 SVN_TEST_PASS2(test19,
1121 "check whitespace removal; no whitespace case"),
1122 SVN_TEST_PASS2(test20,
1123 "check whitespace removal; all whitespace case"),
1124 SVN_TEST_PASS2(test21,
1125 "check that whitespace will be stripped correctly"),
1126 SVN_TEST_PASS2(test22,
1127 "compare stringbufs; different lengths"),
1128 SVN_TEST_PASS2(test23,
1129 "compare stringbufs; same length, different content"),
1130 SVN_TEST_PASS2(test24,
1131 "verify i64toa"),
1132 SVN_TEST_PASS2(test_base36,
1133 "verify base36 conversion"),
1134 SVN_TEST_PASS2(test_stringbuf_insert,
1135 "check inserting into svn_stringbuf_t"),
1136 SVN_TEST_PASS2(test_stringbuf_remove,
1137 "check deletion from svn_stringbuf_t"),
1138 SVN_TEST_PASS2(test_stringbuf_replace,
1139 "check replacement in svn_stringbuf_t"),
1140 SVN_TEST_PASS2(test_string_similarity,
1141 "test string similarity scores"),
1142 SVN_TEST_PASS2(test_string_matching,
1143 "test string matching"),
1144 SVN_TEST_PASS2(test_cstring_skip_prefix,
1145 "test svn_cstring_skip_prefix()"),
1146 SVN_TEST_PASS2(test_stringbuf_replace_all,
1147 "test svn_stringbuf_replace_all"),
1148 SVN_TEST_PASS2(test_stringbuf_leftchop,
1149 "test svn_stringbuf_leftchop"),
1150 SVN_TEST_PASS2(test_stringbuf_set,
1151 "test svn_stringbuf_set()"),
1152 SVN_TEST_PASS2(test_cstring_join,
1153 "test svn_cstring_join2()"),
1154 SVN_TEST_NULL
1155 };
1156
1157 SVN_TEST_MAIN
1158