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