1 /*
2  * path-test.c -- test the path functions
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 #ifdef _MSC_VER
25 #include <direct.h>
26 #define getcwd _getcwd
27 #else
28 #include <unistd.h> /* for getcwd() */
29 #endif
30 
31 #include <stdio.h>
32 #include <string.h>
33 #include <apr_general.h>
34 
35 #include "svn_pools.h"
36 
37 #include "../svn_test.h"
38 
39 /* Make sure SVN_DEPRECATED is defined as empty before including svn_path.h.
40    We don't want to trigger deprecation warnings by the tests of those
41    functions.  */
42 #ifdef SVN_DEPRECATED
43 #undef SVN_DEPRECATED
44 #endif
45 #define SVN_DEPRECATED
46 
47 #include "svn_path.h"
48 
49 
50 /* Using a symbol, because I tried experimenting with different
51    representations */
52 #define SVN_EMPTY_PATH ""
53 
54 /* This check must match the check on top of dirent_uri.c and
55    dirent_uri-tests.c */
56 #if defined(WIN32) || defined(__CYGWIN__) || defined(__OS2__)
57 #define SVN_USE_DOS_PATHS
58 #endif
59 
60 static svn_error_t *
test_path_is_child(apr_pool_t * pool)61 test_path_is_child(apr_pool_t *pool)
62 {
63   int i, j;
64 
65 /* The path checking code is platform specific, so we shouldn't run
66    the Windows path handling testcases on non-Windows platforms.
67    */
68 #define NUM_TEST_PATHS 11
69 
70   static const char * const paths[NUM_TEST_PATHS] = {
71     "/foo/bar",
72     "/foo/bars",
73     "/foo/baz",
74     "/foo/bar/baz",
75     "/flu/blar/blaz",
76     "/foo/bar/baz/bing/boom",
77     SVN_EMPTY_PATH,
78     "foo",
79     ".foo",
80     "/",
81     "foo2",
82     };
83 
84   static const char * const remainders[NUM_TEST_PATHS][NUM_TEST_PATHS] = {
85     { 0, 0, 0, "baz", 0, "baz/bing/boom", 0, 0, 0, 0, 0 },
86     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
87     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
88     { 0, 0, 0, 0, 0, "bing/boom", 0, 0, 0, 0, 0 },
89     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
90     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
91     { 0, 0, 0, 0, 0, 0, 0, "foo", ".foo", 0, "foo2" },
92     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
93     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
94     { "foo/bar", "foo/bars", "foo/baz", "foo/bar/baz", "flu/blar/blaz",
95       "foo/bar/baz/bing/boom", 0, 0, 0, 0, 0 },
96     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
97   };
98 
99   for (i = 0; i < NUM_TEST_PATHS; i++)
100     {
101       for (j = 0; j < NUM_TEST_PATHS; j++)
102         {
103           const char *remainder;
104 
105           remainder = svn_path_is_child(paths[i], paths[j], pool);
106 
107           if (((remainder) && (! remainders[i][j]))
108               || ((! remainder) && (remainders[i][j]))
109               || (remainder && strcmp(remainder, remainders[i][j])))
110             return svn_error_createf
111               (SVN_ERR_TEST_FAILED, NULL,
112                "svn_path_is_child (%s, %s) returned '%s' instead of '%s'",
113                paths[i], paths[j],
114                remainder ? remainder : "(null)",
115                remainders[i][j] ? remainders[i][j] : "(null)" );
116         }
117     }
118 #undef NUM_TEST_PATHS
119   return SVN_NO_ERROR;
120 }
121 
122 
123 static svn_error_t *
test_path_split(apr_pool_t * pool)124 test_path_split(apr_pool_t *pool)
125 {
126   apr_size_t i;
127 
128   static const char * const paths[][3] = {
129     { "/foo/bar",        "/foo",          "bar" },
130     { "/foo/bar/ ",       "/foo/bar",      " " },
131     { "/foo",            "/",             "foo" },
132     { "foo",             SVN_EMPTY_PATH,  "foo" },
133     { ".bar",            SVN_EMPTY_PATH,  ".bar" },
134     { "/.bar",           "/",             ".bar" },
135     { "foo/bar",         "foo",           "bar" },
136     { "/foo/bar",        "/foo",          "bar" },
137     { "foo/bar",         "foo",           "bar" },
138     { "foo./.bar",       "foo.",          ".bar" },
139     { "../foo",          "..",            "foo" },
140     { SVN_EMPTY_PATH,   SVN_EMPTY_PATH,   SVN_EMPTY_PATH },
141     { "/flu\\b/\\blarg", "/flu\\b",       "\\blarg" },
142     { "/",               "/",             "/" },
143   };
144 
145   for (i = 0; i < sizeof(paths) / sizeof(paths[0]); i++)
146     {
147       const char *dir, *base_name;
148 
149       svn_path_split(paths[i][0], &dir, &base_name, pool);
150       if (strcmp(dir, paths[i][1]))
151         {
152           return svn_error_createf
153             (SVN_ERR_TEST_FAILED, NULL,
154              "svn_path_split (%s) returned dirname '%s' instead of '%s'",
155              paths[i][0], dir, paths[i][1]);
156         }
157       if (strcmp(base_name, paths[i][2]))
158         {
159           return svn_error_createf
160             (SVN_ERR_TEST_FAILED, NULL,
161              "svn_path_split (%s) returned basename '%s' instead of '%s'",
162              paths[i][0], base_name, paths[i][2]);
163         }
164     }
165   return SVN_NO_ERROR;
166 }
167 
168 
169 static svn_error_t *
test_path_is_url(apr_pool_t * pool)170 test_path_is_url(apr_pool_t *pool)
171 {
172   apr_size_t i;
173 
174   /* Paths to test and their expected results. */
175   struct {
176     const char *path;
177     svn_boolean_t result;
178   } tests[] = {
179     { "",                                 FALSE },
180     { "/blah/blah",                       FALSE },
181     { "//blah/blah",                      FALSE },
182     { "://blah/blah",                     FALSE },
183     { "a:abb://boo/",                     FALSE },
184     { "http://svn.apache.org/repos/asf/subversion",  TRUE  },
185     { "scheme/with",                      FALSE },
186     { "scheme/with:",                     FALSE },
187     { "scheme/with:/",                    FALSE },
188     { "scheme/with://",                   FALSE },
189     { "scheme/with://slash/",             FALSE },
190     { "file:///path/to/repository",       TRUE  },
191     { "file://",                          TRUE  },
192     { "file:/",                           FALSE },
193     { "file:",                            FALSE },
194     { "file",                             FALSE },
195 #ifdef SVN_USE_DOS_PATHS
196     { "X:/foo",        FALSE },
197     { "X:foo",         FALSE },
198     { "X:",            FALSE },
199 #endif /* non-WIN32 */
200     { "X:/",           FALSE },
201     { "//srv/shr",     FALSE },
202     { "//srv/shr/fld", FALSE },
203   };
204 
205   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
206     {
207       svn_boolean_t retval;
208 
209       retval = svn_path_is_url(tests[i].path);
210       if (tests[i].result != retval)
211         return svn_error_createf
212           (SVN_ERR_TEST_FAILED, NULL,
213            "svn_path_is_url (%s) returned %s instead of %s",
214            tests[i].path, retval ? "TRUE" : "FALSE",
215            tests[i].result ? "TRUE" : "FALSE");
216     }
217 
218   return SVN_NO_ERROR;
219 }
220 
221 
222 static svn_error_t *
test_path_is_uri_safe(apr_pool_t * pool)223 test_path_is_uri_safe(apr_pool_t *pool)
224 {
225   apr_size_t i;
226 
227   /* Paths to test and their expected results. */
228   struct {
229     const char *path;
230     svn_boolean_t result;
231   } tests[] = {
232     { "http://svn.collab.net/repos",        TRUE  },
233     { "http://svn.collab.net/repos%",       FALSE },
234     { "http://svn.collab.net/repos%/svn",   FALSE },
235     { "http://svn.collab.net/repos%2g",     FALSE },
236     { "http://svn.collab.net/repos%2g/svn", FALSE },
237     { "http://svn.collab.net/repos%%",      FALSE },
238     { "http://svn.collab.net/repos%%/svn",  FALSE },
239     { "http://svn.collab.net/repos%2a",     TRUE  },
240     { "http://svn.collab.net/repos%2a/svn", TRUE  },
241   };
242 
243   for (i = 0; i < (sizeof(tests) / sizeof(tests[0])); i++)
244     {
245       svn_boolean_t retval;
246 
247       retval = svn_path_is_uri_safe(tests[i].path);
248       if (tests[i].result != retval)
249         return svn_error_createf
250           (SVN_ERR_TEST_FAILED, NULL,
251            "svn_path_is_uri_safe (%s) returned %s instead of %s",
252            tests[i].path, retval ? "TRUE" : "FALSE",
253            tests[i].result ? "TRUE" : "FALSE");
254     }
255 
256   return SVN_NO_ERROR;
257 }
258 
259 
260 static svn_error_t *
test_uri_encode(apr_pool_t * pool)261 test_uri_encode(apr_pool_t *pool)
262 {
263   int i;
264 
265   struct {
266     const char *path;
267     const char *result;
268   } tests[] = {
269     { "http://subversion.tigris.org",
270          "http://subversion.tigris.org"},
271     { " special_at_beginning",
272          "%20special_at_beginning" },
273     { "special_at_end ",
274          "special_at_end%20" },
275     { "special in middle",
276          "special%20in%20middle" },
277     { "\"Ouch!\"  \"Did that hurt?\"",
278          "%22Ouch!%22%20%20%22Did%20that%20hurt%3F%22" }
279   };
280 
281   for (i = 0; i < 5; i++)
282     {
283       const char *en_path, *de_path;
284 
285       /* URI-encode the path, and verify the results. */
286       en_path = svn_path_uri_encode(tests[i].path, pool);
287       if (strcmp(en_path, tests[i].result))
288         {
289           return svn_error_createf
290             (SVN_ERR_TEST_FAILED, NULL,
291              "svn_path_uri_encode ('%s') returned '%s' instead of '%s'",
292              tests[i].path, en_path, tests[i].result);
293         }
294 
295       /* URI-decode the path, and make sure we're back where we started. */
296       de_path = svn_path_uri_decode(en_path, pool);
297       if (strcmp(de_path, tests[i].path))
298         {
299           return svn_error_createf
300             (SVN_ERR_TEST_FAILED, NULL,
301              "svn_path_uri_decode ('%s') returned '%s' instead of '%s'",
302              tests[i].result, de_path, tests[i].path);
303         }
304     }
305   return SVN_NO_ERROR;
306 }
307 
308 
309 static svn_error_t *
test_uri_decode(apr_pool_t * pool)310 test_uri_decode(apr_pool_t *pool)
311 {
312   int i;
313 
314   struct {
315     const char *path;
316     const char *result;
317   } tests[] = {
318     { "http://c.r.a/s%\0" "8me",
319          "http://c.r.a/s%"},
320     { "http://c.r.a/s%6\0" "me",
321          "http://c.r.a/s%6" },
322     { "http://c.r.a/s%68me",
323          "http://c.r.a/shme" },
324   };
325 
326   for (i = 0; i < 3; i++)
327     {
328       const char *de_path;
329 
330       /* URI-decode the path, and verify the results. */
331       de_path = svn_path_uri_decode(tests[i].path, pool);
332       if (strcmp(de_path, tests[i].result))
333         {
334           return svn_error_createf
335             (SVN_ERR_TEST_FAILED, NULL,
336              "svn_path_uri_decode ('%s') returned '%s' instead of '%s'",
337              tests[i].path, de_path, tests[i].result);
338         }
339     }
340   return SVN_NO_ERROR;
341 }
342 
343 
344 static svn_error_t *
test_uri_autoescape(apr_pool_t * pool)345 test_uri_autoescape(apr_pool_t *pool)
346 {
347   struct {
348     const char *path;
349     const char *result;
350   } tests[] = {
351     { "http://svn.collab.net/", "http://svn.collab.net/" },
352     { "file:///<>\" {}|\\^`", "file:///%3C%3E%22%20%7B%7D%7C%5C%5E%60" },
353     { "http://[::1]", "http://[::1]" }
354   };
355   int i;
356 
357   for (i = 0; i < 3; ++i)
358     {
359       const char* uri = svn_path_uri_autoescape(tests[i].path, pool);
360       if (strcmp(uri, tests[i].result) != 0)
361         return svn_error_createf
362           (SVN_ERR_TEST_FAILED, NULL,
363            "svn_path_uri_autoescape on '%s' returned '%s' instead of '%s'",
364            tests[i].path, uri, tests[i].result);
365       if (strcmp(tests[i].path, tests[i].result) == 0
366           && tests[i].path != uri)
367         return svn_error_createf
368           (SVN_ERR_TEST_FAILED, NULL,
369            "svn_path_uri_autoescape on '%s' returned identical but not same"
370            " string", tests[i].path);
371     }
372 
373   return SVN_NO_ERROR;
374 }
375 
376 static svn_error_t *
test_uri_from_iri(apr_pool_t * pool)377 test_uri_from_iri(apr_pool_t *pool)
378 {
379   /* We have to code the IRIs like this because the compiler might translate
380      character and string literals outside of ASCII to some character set,
381      but here we are hard-coding UTF-8.  But we all read UTF-8 codes like
382      poetry, don't we. */
383   static const char p1[] = {
384     '\x66', '\x69', '\x6C', '\x65', '\x3A', '\x2F', '\x2F', '\x2F',
385     '\x72', '\xC3', '\xA4', '\x6B', '\x73', '\x6D', '\xC3', '\xB6', '\x72',
386     '\x67', '\xC3', '\xA5', '\x73', '\0' };
387   static const char p2[] = {
388     '\x66', '\x69', '\x6C', '\x65', '\x3A', '\x2F', '\x2F', '\x2F',
389     '\x61', '\x62', '\x25', '\x32', '\x30', '\x63', '\x64', '\0' };
390   static const char *paths[2][2] = {
391     { p1,
392       "file:///r%C3%A4ksm%C3%B6rg%C3%A5s" },
393     { p2,
394       "file:///ab%20cd" }
395   };
396   int i;
397 
398   for (i = 0; i < 2; ++i)
399     {
400       const char *uri = svn_path_uri_from_iri(paths[i][0], pool);
401       if (strcmp(paths[i][1], uri) != 0)
402         return svn_error_createf
403           (SVN_ERR_TEST_FAILED, NULL,
404            "svn_path_uri_from_iri on '%s' returned '%s' instead of '%s'",
405            paths[i][0], uri, paths[i][1]);
406       if (strcmp(paths[i][0], uri) == 0
407           && paths[i][0] != uri)
408         return svn_error_createf
409           (SVN_ERR_TEST_FAILED, NULL,
410            "svn_path_uri_from_iri on '%s' returned identical but not same"
411            " string", paths[i][0]);
412     }
413 
414   return SVN_NO_ERROR;
415 }
416 
417 static svn_error_t *
test_path_join(apr_pool_t * pool)418 test_path_join(apr_pool_t *pool)
419 {
420   int i;
421   char *result;
422 
423   static const char * const joins[][3] = {
424     { "abc", "def", "abc/def" },
425     { "a", "def", "a/def" },
426     { "a", "d", "a/d" },
427     { "/", "d", "/d" },
428     { "/abc", "d", "/abc/d" },
429     { "/abc", "def", "/abc/def" },
430     { "/abc", "/def", "/def" },
431     { "/abc", "/d", "/d" },
432     { "/abc", "/", "/" },
433     { SVN_EMPTY_PATH, "/", "/" },
434     { "/", SVN_EMPTY_PATH, "/" },
435     { SVN_EMPTY_PATH, "abc", "abc" },
436     { "abc", SVN_EMPTY_PATH, "abc" },
437     { SVN_EMPTY_PATH, "/abc", "/abc" },
438     { SVN_EMPTY_PATH, SVN_EMPTY_PATH, SVN_EMPTY_PATH },
439     { "X:/abc", "/d", "/d" },
440     { "X:/abc", "/", "/" },
441     { "X:",SVN_EMPTY_PATH, "X:" },
442     { "X:", "/def", "/def" },
443     { "X:abc", "/d", "/d" },
444     { "X:abc", "/", "/" },
445     { "file://", "foo", "file:///foo" },
446     { "file:///foo", "bar", "file:///foo/bar" },
447     { "file:///foo", SVN_EMPTY_PATH, "file:///foo" },
448     { SVN_EMPTY_PATH, "file:///foo", "file:///foo" },
449     { "file:///X:", "bar", "file:///X:/bar" },
450     { "file:///X:foo", "bar", "file:///X:foo/bar" },
451     { "http://svn.dm.net", "repos", "http://svn.dm.net/repos" },
452 #ifdef SVN_USE_DOS_PATHS
453 /* These will fail, see issue #2028
454     { "//srv/shr",     "fld",     "//srv/shr/fld" },
455     { "//srv",         "shr/fld", "//srv/shr/fld" },
456     { "//srv/shr/fld", "subfld",  "//srv/shr/fld/subfld" },
457     { "//srv/shr/fld", "//srv/shr", "//srv/shr" },
458     { "//srv",         "//srv/fld", "//srv/fld" },
459     { "X:abc", "X:/def", "X:/def" },    { "X:/",SVN_EMPTY_PATH, "X:/" },
460     { "X:/","abc", "X:/abc" },
461     { "X:/", "/def", "/def" },
462     { "X:/abc", "X:/", "X:/" },
463     { "X:abc", "X:/", "X:/" },
464     { "X:abc", "X:/def", "X:/def" },
465     { "X:","abc", "X:abc" },
466     { "X:/abc", "X:/def", "X:/def" },
467 */
468 #else /* WIN32 or Cygwin */
469     { "X:abc", "X:/def", "X:abc/X:/def" },
470     { "X:","abc", "X:/abc" },
471     { "X:/abc", "X:/def", "X:/abc/X:/def" },
472 #endif /* non-WIN32 */
473   };
474 
475   for (i = sizeof(joins) / sizeof(joins[0]); i--; )
476     {
477       const char *base = joins[i][0];
478       const char *comp = joins[i][1];
479       const char *expect = joins[i][2];
480 
481       result = svn_path_join(base, comp, pool);
482       if (strcmp(result, expect))
483         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
484                                  "svn_path_join(\"%s\", \"%s\") returned "
485                                  "\"%s\". expected \"%s\"",
486                                  base, comp, result, expect);
487 
488       /* svn_path_join_many does not support URLs, so skip the URL tests. */
489       if (svn_path_is_url(base))
490         continue;
491 
492       result = svn_path_join_many(pool, base, comp, SVN_VA_NULL);
493       if (strcmp(result, expect))
494         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
495                                  "svn_path_join_many(\"%s\", \"%s\") returned "
496                                  "\"%s\". expected \"%s\"",
497                                  base, comp, result, expect);
498     }
499 
500 #define TEST_MANY(args, expect) \
501   result = svn_path_join_many args ; \
502   if (strcmp(result, expect) != 0) \
503     return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, \
504                              "svn_path_join_many" #args " returns \"%s\". " \
505                              "expected \"%s\"", \
506                              result, expect);
507 
508   TEST_MANY((pool, "abc", SVN_VA_NULL), "abc");
509   TEST_MANY((pool, "/abc", SVN_VA_NULL), "/abc");
510   TEST_MANY((pool, "/", SVN_VA_NULL), "/");
511 
512   TEST_MANY((pool, "abc", "def", "ghi", SVN_VA_NULL), "abc/def/ghi");
513   TEST_MANY((pool, "abc", "/def", "ghi", SVN_VA_NULL), "/def/ghi");
514   TEST_MANY((pool, "/abc", "def", "ghi", SVN_VA_NULL), "/abc/def/ghi");
515   TEST_MANY((pool, "abc", "def", "/ghi", SVN_VA_NULL), "/ghi");
516   TEST_MANY((pool, "/", "def", "/ghi", SVN_VA_NULL), "/ghi");
517   TEST_MANY((pool, "/", "/def", "/ghi", SVN_VA_NULL), "/ghi");
518 
519   TEST_MANY((pool, SVN_EMPTY_PATH, "def", "ghi", SVN_VA_NULL), "def/ghi");
520   TEST_MANY((pool, "abc", SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "abc/ghi");
521   TEST_MANY((pool, "abc", "def", SVN_EMPTY_PATH, SVN_VA_NULL), "abc/def");
522   TEST_MANY((pool, SVN_EMPTY_PATH, "def", SVN_EMPTY_PATH, SVN_VA_NULL), "def");
523   TEST_MANY((pool, SVN_EMPTY_PATH, SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "ghi");
524   TEST_MANY((pool, "abc", SVN_EMPTY_PATH, SVN_EMPTY_PATH, SVN_VA_NULL), "abc");
525   TEST_MANY((pool, SVN_EMPTY_PATH, "def", "/ghi", SVN_VA_NULL), "/ghi");
526   TEST_MANY((pool, SVN_EMPTY_PATH, SVN_EMPTY_PATH, "/ghi", SVN_VA_NULL), "/ghi");
527 
528   TEST_MANY((pool, "/", "def", "ghi", SVN_VA_NULL), "/def/ghi");
529   TEST_MANY((pool, "abc", "/", "ghi", SVN_VA_NULL), "/ghi");
530   TEST_MANY((pool, "abc", "def", "/", SVN_VA_NULL), "/");
531   TEST_MANY((pool, "/", "/", "ghi", SVN_VA_NULL), "/ghi");
532   TEST_MANY((pool, "/", "/", "/", SVN_VA_NULL), "/");
533   TEST_MANY((pool, "/", SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "/ghi");
534   TEST_MANY((pool, "/", "def", SVN_EMPTY_PATH, SVN_VA_NULL), "/def");
535   TEST_MANY((pool, SVN_EMPTY_PATH, "/", "ghi", SVN_VA_NULL), "/ghi");
536   TEST_MANY((pool, "/", SVN_EMPTY_PATH, SVN_EMPTY_PATH, SVN_VA_NULL), "/");
537   TEST_MANY((pool, SVN_EMPTY_PATH, "/", SVN_EMPTY_PATH, SVN_VA_NULL), "/");
538   TEST_MANY((pool, SVN_EMPTY_PATH, SVN_EMPTY_PATH, "/", SVN_VA_NULL), "/");
539 
540 #ifdef SVN_USE_DOS_PATHS
541 /* These will fail, see issue #2028
542   TEST_MANY((pool, "X:", "def", "ghi", SVN_VA_NULL), "X:def/ghi");
543   TEST_MANY((pool, "X:", SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "X:ghi");
544   TEST_MANY((pool, "X:", "def", SVN_EMPTY_PATH, SVN_VA_NULL), "X:def");
545   TEST_MANY((pool, SVN_EMPTY_PATH, "X:", "ghi", SVN_VA_NULL), "X:ghi");
546   TEST_MANY((pool, "X:/", "def", "ghi", SVN_VA_NULL), "X:/def/ghi");
547   TEST_MANY((pool, "abc", "X:/", "ghi", SVN_VA_NULL), "X:/ghi");
548   TEST_MANY((pool, "abc", "def", "X:/", SVN_VA_NULL), "X:/");
549   TEST_MANY((pool, "X:/", "X:/", "ghi", SVN_VA_NULL), "X:/ghi");
550   TEST_MANY((pool, "X:/", "X:/", "/", SVN_VA_NULL), "/");
551   TEST_MANY((pool, "X:/", SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "X:/ghi");
552   TEST_MANY((pool, "X:/", "def", SVN_EMPTY_PATH, SVN_VA_NULL), "X:/def");
553   TEST_MANY((pool, SVN_EMPTY_PATH, "X:/", "ghi", SVN_VA_NULL), "X:/ghi");
554   TEST_MANY((pool, "X:/", SVN_EMPTY_PATH, SVN_EMPTY_PATH, SVN_VA_NULL), "X:/");
555   TEST_MANY((pool, SVN_EMPTY_PATH, "X:/", SVN_EMPTY_PATH, SVN_VA_NULL), "X:/");
556   TEST_MANY((pool, SVN_EMPTY_PATH, SVN_EMPTY_PATH, "X:/", SVN_VA_NULL), "X:/");
557   TEST_MANY((pool, "X:", "X:/", "ghi", SVN_VA_NULL), "X:/ghi");
558   TEST_MANY((pool, "X:", "X:/", "/", SVN_VA_NULL), "/");
559 
560   TEST_MANY((pool, "//srv/shr", "def", "ghi", SVN_VA_NULL), "//srv/shr/def/ghi");
561   TEST_MANY((pool, "//srv", "shr", "def", "ghi", SVN_VA_NULL), "//srv/shr/def/ghi");
562   TEST_MANY((pool, "//srv/shr/fld", "def", "ghi", SVN_VA_NULL),
563             "//srv/shr/fld/def/ghi");
564   TEST_MANY((pool, "//srv/shr/fld", "def", "//srv/shr", SVN_VA_NULL), "//srv/shr");
565   TEST_MANY((pool, "//srv", "shr", "//srv/shr", SVN_VA_NULL), "//srv/shr");
566   TEST_MANY((pool, SVN_EMPTY_PATH, "//srv/shr/fld", "def", "ghi", SVN_VA_NULL),
567             "//srv/shr/fld/def/ghi");
568   TEST_MANY((pool, SVN_EMPTY_PATH, "//srv/shr/fld", "def", "//srv/shr", SVN_VA_NULL),
569             "//srv/shr");
570 */
571 #else /* WIN32 or Cygwin */
572   TEST_MANY((pool, "X:", "def", "ghi", SVN_VA_NULL), "X:/def/ghi");
573   TEST_MANY((pool, "X:", SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "X:/ghi");
574   TEST_MANY((pool, "X:", "def", SVN_EMPTY_PATH, SVN_VA_NULL), "X:/def");
575   TEST_MANY((pool, SVN_EMPTY_PATH, "X:", "ghi", SVN_VA_NULL), "X:/ghi");
576 #endif /* non-WIN32 */
577 
578   /* ### probably need quite a few more tests... */
579 
580   return SVN_NO_ERROR;
581 }
582 
583 
584 static svn_error_t *
test_path_basename(apr_pool_t * pool)585 test_path_basename(apr_pool_t *pool)
586 {
587   int i;
588   char *result;
589 
590   struct {
591     const char *path;
592     const char *result;
593   } tests[] = {
594     { "abc", "abc" },
595     { "/abc", "abc" },
596     { "/abc", "abc" },
597     { "/x/abc", "abc" },
598     { "/xx/abc", "abc" },
599     { "/xx/abc", "abc" },
600     { "/xx/abc", "abc" },
601     { "a", "a" },
602     { "/a", "a" },
603     { "/b/a", "a" },
604     { "/b/a", "a" },
605     { "/", "/" },
606     { SVN_EMPTY_PATH, SVN_EMPTY_PATH },
607     { "X:/abc", "abc" },
608     { "X:", "X:" },
609 
610 #ifdef SVN_USE_DOS_PATHS
611 /* These will fail, see issue #2028
612     { "X:/", "X:/" },
613     { "X:abc", "abc" },
614     { "//srv/shr",      "//srv/shr" },
615     { "//srv",          "//srv" },
616     { "//srv/shr/fld",  "fld" },
617     { "//srv/shr/fld/subfld", "subfld" },
618 */
619 #else /* WIN32 or Cygwin */
620     { "X:abc", "X:abc" },
621 #endif /* non-WIN32 */
622   };
623 
624   for (i = sizeof(tests) / sizeof(tests[0]); i--; )
625     {
626       const char *path = tests[i].path;
627       const char *expect = tests[i].result;
628 
629       result = svn_path_basename(path, pool);
630       if (strcmp(result, expect))
631         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
632                                  "svn_path_basename(\"%s\") returned "
633                                  "\"%s\". expected \"%s\"",
634                                  path, result, expect);
635     }
636 
637   return SVN_NO_ERROR;
638 }
639 
640 
641 static svn_error_t *
test_path_dirname(apr_pool_t * pool)642 test_path_dirname(apr_pool_t *pool)
643 {
644   int i;
645   char *result;
646 
647   struct {
648     const char *path;
649     const char *result;
650   } tests[] = {
651     { "abc", "" },
652     { "/abc", "/" },
653     { "/x/abc", "/x" },
654     { "/xx/abc", "/xx" },
655     { "a", "" },
656     { "/a", "/" },
657     { "/b/a", "/b" },
658     { "/", "/" },
659     { SVN_EMPTY_PATH, SVN_EMPTY_PATH },
660     { "X:abc/def", "X:abc" },
661 #ifdef SVN_USE_DOS_PATHS
662     { "//srv/shr/fld",  "//srv/shr" },
663     { "//srv/shr/fld/subfld", "//srv/shr/fld" },
664 
665 /* These will fail, see issue #2028
666     { "X:/", "X:/" },
667     { "X:/abc", "X:/" },
668     { "X:", "X:" },
669     { "X:abc", "X:" },
670     { "//srv/shr",      "//srv/shr" },
671 */
672 #else  /* WIN32 or Cygwin */
673     /* on non-Windows platforms, ':' is allowed in pathnames */
674     { "X:", "" },
675     { "X:abc", "" },
676 #endif /* non-WIN32 */
677   };
678 
679   for (i = sizeof(tests) / sizeof(tests[0]); i--; )
680     {
681       const char *path = tests[i].path;
682       const char *expect = tests[i].result;
683 
684       result = svn_path_dirname(path, pool);
685       if (strcmp(result, expect))
686         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
687                                  "svn_path_dirname(\"%s\") returned "
688                                  "\"%s\". expected \"%s\"",
689                                  path, result, expect);
690     }
691 
692   return SVN_NO_ERROR;
693 }
694 
695 
696 static svn_error_t *
test_path_decompose(apr_pool_t * pool)697 test_path_decompose(apr_pool_t *pool)
698 {
699   static const char * const paths[] = {
700     "/", "/", NULL,
701     "foo", "foo", NULL,
702     "/foo", "/", "foo", NULL,
703     "/foo/bar", "/", "foo", "bar", NULL,
704     "foo/bar", "foo", "bar", NULL,
705 
706     /* Are these canonical? Should the middle bits produce SVN_EMPTY_PATH? */
707     "foo/bar", "foo", "bar", NULL,
708     NULL,
709   };
710   int i = 0;
711 
712   for (;;)
713     {
714       if (! paths[i])
715         break;
716       else
717         {
718           apr_array_header_t *components = svn_path_decompose(paths[i], pool);
719           int j;
720           for (j = 0; j < components->nelts; ++j)
721             {
722               const char *component = APR_ARRAY_IDX(components,
723                                                     j,
724                                                     const char*);
725               if (! paths[i+j+1])
726                 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
727                                          "svn_path_decompose(\"%s\") returned "
728                                          "unexpected component \"%s\"",
729                                          paths[i], component);
730               if (strcmp(component, paths[i+j+1]))
731                 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
732                                          "svn_path_decompose(\"%s\") returned "
733                                          "\"%s\" expected \"%s\"",
734                                          paths[i], component, paths[i+j+1]);
735             }
736           if (paths[i+j+1])
737             return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
738                                      "svn_path_decompose(\"%s\") failed "
739                                      "to return \"%s\"",
740                                      paths[i], paths[i+j+1]);
741           i += components->nelts + 2;
742         }
743     }
744 
745   return SVN_NO_ERROR;
746 }
747 
748 static svn_error_t *
test_path_canonicalize(apr_pool_t * pool)749 test_path_canonicalize(apr_pool_t *pool)
750 {
751   struct {
752     const char *path;
753     const char *result;
754   } tests[] = {
755     { "",                     "" },
756     { ".",                    "" },
757     { "/",                    "/" },
758     { "/.",                   "/" },
759     { "./",                   "" },
760     { "./.",                  "" },
761     { "//",                   "/" },
762     { "/////",                "/" },
763     { "./././.",              "" },
764     { "////././.",            "/" },
765     { "foo",                  "foo" },
766     { ".foo",                 ".foo" },
767     { "foo.",                 "foo." },
768     { "/foo",                 "/foo" },
769     { "foo/",                 "foo" },
770     { "foo//",                "foo" },
771     { "foo///",               "foo" },
772     { "foo./",                "foo." },
773     { "foo./.",               "foo." },
774     { "foo././/.",            "foo." },
775     { "/foo/bar",             "/foo/bar" },
776     { "foo/..",               "foo/.." },
777     { "foo/../",              "foo/.." },
778     { "foo/../.",             "foo/.." },
779     { "foo//.//bar",          "foo/bar" },
780     { "///foo",               "/foo" },
781     { "/.//./.foo",           "/.foo" },
782     { ".///.foo",             ".foo" },
783     { "../foo",               "../foo" },
784     { "../../foo/",           "../../foo" },
785     { "../../foo/..",         "../../foo/.." },
786     { "/../../",              "/../.." },
787     { "dirA",                 "dirA" },
788     { "foo/dirA",             "foo/dirA" },
789     { "http://hst",           "http://hst" },
790     { "http://hst/foo/../bar","http://hst/foo/../bar" },
791     { "http://hst/",          "http://hst" },
792     { "http:///",             "http://" },
793     { "https://",             "https://" },
794     { "file:///",             "file://" },
795     { "file://",              "file://" },
796     { "svn:///",              "svn://" },
797     { "svn+ssh:///",          "svn+ssh://" },
798     { "http://HST/",          "http://hst" },
799     { "http://HST/FOO/BaR",   "http://hst/FOO/BaR" },
800     { "svn+ssh://j.raNDom@HST/BaR", "svn+ssh://j.raNDom@hst/BaR" },
801     { "svn+SSH://j.random:jRaY@HST/BaR", "svn+ssh://j.random:jRaY@hst/BaR" },
802     { "SVN+ssh://j.raNDom:jray@HST/BaR", "svn+ssh://j.raNDom:jray@hst/BaR" },
803     { "fILe:///Users/jrandom/wc", "file:///Users/jrandom/wc" },
804     { "fiLE:///",             "file://" },
805     { "fiLE://",              "file://" },
806     { "X:/foo",               "X:/foo" },
807     { "X:",                   "X:" },
808     { "X:foo",                "X:foo" },
809 #ifdef SVN_USE_DOS_PATHS
810     { "file:///c:/temp/repos", "file:///C:/temp/repos" },
811     { "file:///c:/temp/REPOS", "file:///C:/temp/REPOS" },
812     { "file:///C:/temp/REPOS", "file:///C:/temp/REPOS" },
813     { "C:/folder/subfolder/file", "C:/folder/subfolder/file" },
814     /* We permit UNC paths on Windows.  By definition UNC
815      * paths must have two components so we should remove the
816      * double slash if there is only one component. */
817     { "//hst",                "/hst" },
818     { "//hst/./",             "/hst" },
819     { "//server/share/",      "//server/share" },
820     { "//server/SHare/",      "//server/SHare" },
821     { "//SERVER/SHare/",      "//server/SHare" },
822     { "X:/",                  "X:/" },
823 #else /* WIN32 or Cygwin */
824     { "file:///c:/temp/repos", "file:///c:/temp/repos" },
825     { "file:///c:/temp/REPOS", "file:///c:/temp/REPOS" },
826     { "file:///C:/temp/REPOS", "file:///C:/temp/REPOS" },
827 #endif /* non-WIN32 */
828     { NULL, NULL }
829   };
830   int i;
831 
832   i = 0;
833   while (tests[i].path)
834     {
835       const char *canonical = svn_path_canonicalize(tests[i].path, pool);
836 
837       if (strcmp(canonical, tests[i].result))
838         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
839                                  "svn_path_canonicalize(\"%s\") returned "
840                                  "\"%s\" expected \"%s\"",
841                                  tests[i].path, canonical, tests[i].result);
842       ++i;
843     }
844 
845   return SVN_NO_ERROR;
846 }
847 
848 static svn_error_t *
test_path_remove_component(apr_pool_t * pool)849 test_path_remove_component(apr_pool_t *pool)
850 {
851   struct {
852     const char *path;
853     const char *result;
854   } tests[] = {
855     { "",                     "" },
856     { "/",                    "/" },
857     { "foo",                  "" },
858     { "foo/bar",              "foo" },
859     { "/foo/bar",             "/foo" },
860     { "/foo",                 "/" },
861 #ifdef SVN_USE_DOS_PATHS
862     { "X:/foo/bar",           "X:/foo" },
863     { "//srv/shr/fld",        "//srv/shr" },
864     { "//srv/shr/fld/subfld", "//srv/shr/fld" },
865 /* These will fail, see issue #2028
866     { "X:/foo",               "X:/" },
867     { "X:/",                  "X:/" },
868     { "X:foo",                "X:" },
869     { "X:",                   "X:" },
870     { "//srv/shr",            "//srv/shr" },
871 */
872 #else /* WIN32 or Cygwin */
873     { "X:foo",                "" },
874     { "X:",                   "" },
875 #endif /* non-WIN32 */
876     { NULL, NULL }
877   };
878   int i;
879   svn_stringbuf_t *buf;
880 
881   buf = svn_stringbuf_create_empty(pool);
882 
883   i = 0;
884   while (tests[i].path)
885     {
886       svn_stringbuf_set(buf, tests[i].path);
887 
888       svn_path_remove_component(buf);
889 
890       if (strcmp(buf->data, tests[i].result))
891         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
892                                  "svn_path_remove_component(\"%s\") returned "
893                                  "\"%s\" expected \"%s\"",
894                                  tests[i].path, buf->data, tests[i].result);
895       ++i;
896     }
897 
898   return SVN_NO_ERROR;
899 }
900 
901 static svn_error_t *
test_path_check_valid(apr_pool_t * pool)902 test_path_check_valid(apr_pool_t *pool)
903 {
904   apr_size_t i;
905 
906   /* Paths to test and their expected results. */
907   struct {
908     const char *path;
909     svn_boolean_t result;
910   } tests[] = {
911     { "/foo/bar",      TRUE },
912     { "/foo",          TRUE },
913     { "/",             TRUE },
914     { "foo/bar",       TRUE },
915     { "foo bar",       TRUE },
916     { "foo\7bar",      FALSE },
917     { "foo\31bar",     FALSE },
918     { "\7foo\31bar",   FALSE },
919     { "\7",            FALSE },
920     { "",              TRUE },
921   };
922 
923   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
924     {
925       svn_error_t *err = svn_path_check_valid(tests[i].path, pool);
926       svn_boolean_t retval = (err == SVN_NO_ERROR);
927 
928       svn_error_clear(err);
929       if (tests[i].result != retval)
930         return svn_error_createf
931           (SVN_ERR_TEST_FAILED, NULL,
932            "svn_path_check_valid (%s) returned %s instead of %s",
933            tests[i].path, retval ? "TRUE" : "FALSE",
934            tests[i].result ? "TRUE" : "FALSE");
935     }
936 
937   return SVN_NO_ERROR;
938 }
939 
940 static svn_error_t *
test_path_is_ancestor(apr_pool_t * pool)941 test_path_is_ancestor(apr_pool_t *pool)
942 {
943   apr_size_t i;
944 
945   /* Paths to test and their expected results. */
946   struct {
947     const char *path1;
948     const char *path2;
949     svn_boolean_t result;
950   } tests[] = {
951     { "/foo",            "/foo/bar",      TRUE},
952     { "/foo/bar",        "/foo/bar/",     TRUE},
953     { "/",               "/foo",          TRUE},
954     { SVN_EMPTY_PATH,    "foo",           TRUE},
955     { SVN_EMPTY_PATH,    ".bar",          TRUE},
956 
957     { "/.bar",           "/",             FALSE},
958     { "foo/bar",         "foo",           FALSE},
959     { "/foo/bar",        "/foo",          FALSE},
960     { "foo",             "foo/bar",       TRUE},
961     { "foo.",            "foo./.bar",     TRUE},
962 
963     { "../foo",          "..",            FALSE},
964     { SVN_EMPTY_PATH,    SVN_EMPTY_PATH,  TRUE},
965     { "/",               "/",             TRUE},
966 
967     { "http://test",    "http://test",     TRUE},
968     { "http://test",    "http://taste",    FALSE},
969     { "http://test",    "http://test/foo", TRUE},
970     { "http://test",    "file://test/foo", FALSE},
971     { "http://test",    "http://testF",    FALSE},
972 /*
973     TODO: this testcase fails, showing that svn_path_is_ancestor
974     shouldn't be used on urls. This is related to issue #1711.
975 
976     { "http://",        "http://test",     FALSE},
977 */
978     { "X:foo",           "X:bar",         FALSE},
979 #ifdef SVN_USE_DOS_PATHS
980     { "//srv/shr",       "//srv",         FALSE},
981     { "//srv/shr",       "//srv/shr/fld", TRUE },
982     { "//srv",           "//srv/shr/fld", TRUE },
983     { "//srv/shr/fld",   "//srv/shr",     FALSE },
984     { "//srv/shr/fld",   "//srv2/shr/fld", FALSE },
985 /* These will fail, see issue #2028
986     { "X:/",             "X:/",           TRUE},
987     { "X:/foo",          "X:/",           FALSE},
988     { "X:/",             "X:/foo",        TRUE},
989     { "X:",              "X:foo",         TRUE},
990 */
991 #else /* WIN32 or Cygwin */
992     { "X:",              "X:foo",         FALSE},
993 
994 #endif /* non-WIN32 */
995   };
996 
997   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
998     {
999       svn_boolean_t retval;
1000 
1001       retval = svn_path_is_ancestor(tests[i].path1, tests[i].path2);
1002       if (tests[i].result != retval)
1003         return svn_error_createf
1004           (SVN_ERR_TEST_FAILED, NULL,
1005            "svn_path_is_ancestor (%s, %s) returned %s instead of %s",
1006            tests[i].path1, tests[i].path2, retval ? "TRUE" : "FALSE",
1007            tests[i].result ? "TRUE" : "FALSE");
1008     }
1009   return SVN_NO_ERROR;
1010 }
1011 
1012 static svn_error_t *
test_is_single_path_component(apr_pool_t * pool)1013 test_is_single_path_component(apr_pool_t *pool)
1014 {
1015   apr_size_t i;
1016 
1017   /* Paths to test and their expected results.
1018    * Note that these paths need to be canonical,
1019    * else we might trigger an abort(). */
1020   struct {
1021     const char *path;
1022     svn_boolean_t result;
1023   } tests[] = {
1024     { "/foo/bar",      FALSE },
1025     { "/foo",          FALSE },
1026     { "/",             FALSE },
1027     { "foo/bar",       FALSE },
1028     { "foo",           TRUE },
1029     { "..",            FALSE },
1030     { "",              FALSE },
1031   };
1032 
1033   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
1034     {
1035       svn_boolean_t retval;
1036 
1037       retval = svn_path_is_single_path_component(tests[i].path);
1038       if (tests[i].result != retval)
1039         return svn_error_createf
1040           (SVN_ERR_TEST_FAILED, NULL,
1041            "svn_path_is_single_path_component (%s) returned %s instead of %s",
1042            tests[i].path, retval ? "TRUE" : "FALSE",
1043            tests[i].result ? "TRUE" : "FALSE");
1044     }
1045 
1046   return SVN_NO_ERROR;
1047 }
1048 
1049 static svn_error_t *
test_compare_paths(apr_pool_t * pool)1050 test_compare_paths(apr_pool_t *pool)
1051 {
1052   apr_size_t i;
1053 
1054   /* Paths to test and their expected results. */
1055   struct {
1056     const char *path1;
1057     const char *path2;
1058     int result;
1059   } tests[] = {
1060     { "/foo",         "/foo",         0},
1061     { "/foo/bar",     "/foo/bar",     0},
1062     { "/",            "/",            0},
1063     { SVN_EMPTY_PATH, SVN_EMPTY_PATH, 0},
1064     { "foo",          "foo",          0},
1065     { "foo",          "foo/bar",      -1},
1066     { "foo/bar",      "foo/boo",      -1},
1067     { "boo",          "foo",          -1},
1068     { "foo",          "boo",          1},
1069     { "foo/bar",      "foo",          1},
1070     { "/",            "/foo",         -1},
1071     { "/foo",         "/foo/bar",     -1},
1072     { "/foo",         "/foo/bar/boo", -1},
1073     { "foo",          "/foo",         1},
1074     { "foo\xe0""bar", "foo",          1},
1075     { "X:/foo",       "X:/foo",        0},
1076     { "X:foo",        "X:foo",         0},
1077     { "X:",           "X:foo",         -1},
1078     { "X:foo",        "X:",            1},
1079 #ifdef SVN_USE_DOS_PATHS
1080     { "//srv/shr",    "//srv",         1},
1081     { "//srv/shr",    "//srv/shr/fld", -1 },
1082     { "//srv/shr/fld", "//srv/shr",    1 },
1083     { "//srv/shr/fld", "//abc/def/ghi", 1 },
1084 /* These will fail, see issue #2028
1085     { "X:/",          "X:/",           0},
1086     { "X:/",          "X:/foo",        -1},
1087     { "X:/foo",       "X:/",           1},
1088 */
1089 #endif /* WIN32 or Cygwin */
1090   };
1091 
1092   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
1093     {
1094       int retval;
1095 
1096       retval = svn_path_compare_paths(tests[i].path1, tests[i].path2);
1097       /* tests if expected and actual result are both < 0,
1098          equal to 0 or greater than 0. */
1099       if (! (tests[i].result * retval > 0 ||
1100             (tests[i].result == 0 && retval == 0)) )
1101         return svn_error_createf
1102           (SVN_ERR_TEST_FAILED, NULL,
1103            "svn_path_compare_paths (%s, %s) returned %d instead of %d",
1104            tests[i].path1, tests[i].path2, retval, tests[i].result);
1105     }
1106   return SVN_NO_ERROR;
1107 }
1108 
1109 static svn_error_t *
test_path_get_longest_ancestor(apr_pool_t * pool)1110 test_path_get_longest_ancestor(apr_pool_t *pool)
1111 {
1112   apr_size_t i;
1113 
1114   /* Paths to test and their expected results. */
1115   struct {
1116     const char *path1;
1117     const char *path2;
1118     const char *result;
1119   } tests[] = {
1120     { "/foo",           "/foo/bar",        "/foo"},
1121     { "/foo/bar",       "foo/bar",         ""},
1122     { "/",              "/foo",            "/"},
1123     { SVN_EMPTY_PATH,   "foo",             SVN_EMPTY_PATH},
1124     { SVN_EMPTY_PATH,   ".bar",            SVN_EMPTY_PATH},
1125     { "/.bar",          "/",               "/"},
1126     { "foo/bar",        "foo",             "foo"},
1127     { "/foo/bar",       "/foo",            "/foo"},
1128     { "/rif",           "/raf",            "/"},
1129     { "foo",            "foo/bar",         "foo"},
1130     { "foo.",           "foo./.bar",       "foo."},
1131     { SVN_EMPTY_PATH,   SVN_EMPTY_PATH,    SVN_EMPTY_PATH},
1132     { "/",              "/",               "/"},
1133     { "http://test",    "http://test",     "http://test"},
1134     { "http://test",    "http://taste",    ""},
1135     { "http://test",    "http://test/foo", "http://test"},
1136     { "http://test",    "file://test/foo", ""},
1137     { "http://test",    "http://tests",    ""},
1138     { "http://",        "http://test",     ""},
1139     { "file:///A/C",    "file:///B/D",     ""},
1140     { "file:///A/C",    "file:///A/D",     "file:///A"},
1141 
1142 #ifdef SVN_USE_DOS_PATHS
1143     { "X:/",            "X:/",             "X:/"},
1144     { "X:/foo/bar/A/D/H/psi", "X:/foo/bar/A/B", "X:/foo/bar/A" },
1145     { "X:/foo/bar/boo", "X:/foo/bar/baz/boz", "X:/foo/bar"},
1146     { "X:foo/bar",      "X:foo/bar/boo",   "X:foo/bar"},
1147     { "//srv/shr",      "//srv/shr/fld",   "//srv/shr" },
1148     { "//srv/shr/fld",  "//srv/shr",       "//srv/shr" },
1149 
1150 /* These will fail, see issue #2028
1151     { "//srv/shr/fld",  "//srv2/shr/fld",  "" },
1152     { "X:/foo",         "X:/",             "X:/"},
1153     { "X:/folder1",     "X:/folder2",      "X:/"},
1154     { "X:/",            "X:/foo",          "X:/"},
1155     { "X:",             "X:foo",           "X:"},
1156     { "X:",             "X:/",             ""},
1157     { "X:foo",          "X:bar",           "X:"},
1158 */
1159 #else /* WIN32 or Cygwin */
1160     { "X:/foo",         "X:",              "X:"},
1161     { "X:/folder1",     "X:/folder2",      "X:"},
1162     { "X:",             "X:foo",           ""},
1163     { "X:foo",          "X:bar",           ""},
1164 #endif /* non-WIN32 */
1165   };
1166 
1167   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
1168     {
1169       const char *retval;
1170 
1171       retval = svn_path_get_longest_ancestor(tests[i].path1, tests[i].path2,
1172                                              pool);
1173 
1174       if (strcmp(tests[i].result, retval))
1175         return svn_error_createf
1176           (SVN_ERR_TEST_FAILED, NULL,
1177            "svn_path_get_longest_ancestor (%s, %s) returned %s instead of %s",
1178            tests[i].path1, tests[i].path2, retval, tests[i].result);
1179 
1180       /* changing the order of the paths should return the same results */
1181       retval = svn_path_get_longest_ancestor(tests[i].path2, tests[i].path1,
1182                                              pool);
1183 
1184       if (strcmp(tests[i].result, retval))
1185         return svn_error_createf
1186           (SVN_ERR_TEST_FAILED, NULL,
1187            "svn_path_get_longest_ancestor (%s, %s) returned %s instead of %s",
1188            tests[i].path2, tests[i].path1, retval, tests[i].result);
1189     }
1190   return SVN_NO_ERROR;
1191 }
1192 
1193 
1194 static svn_error_t *
test_path_splitext(apr_pool_t * pool)1195 test_path_splitext(apr_pool_t *pool)
1196 {
1197   apr_size_t i;
1198   apr_pool_t *subpool = svn_pool_create(pool);
1199 
1200   /* Paths to test and their expected results. */
1201   struct {
1202     const char *path;
1203     const char *path_root;
1204     const char *path_ext;
1205   } tests[] = {
1206     { "no-ext",                    "no-ext",                 "" },
1207     { "test-file.py",              "test-file.",             "py" },
1208     { "period.file.ext",           "period.file.",           "ext" },
1209     { "multi-component/file.txt",  "multi-component/file.",  "txt" },
1210     { "yep.still/no-ext",          "yep.still/no-ext",       "" },
1211     { "folder.with/period.log",    "folder.with/period.",    "log" },
1212     { "period.",                   "period.",                "" },
1213     { "dir/period.",               "dir/period.",            "" },
1214     { "file.ends-with/period.",    "file.ends-with/period.", "" },
1215     { "two-periods..txt",          "two-periods..",          "txt" },
1216     { ".dot-file",                 ".dot-file",              "" },
1217     { "sub/.dot-file",             "sub/.dot-file",          "" },
1218     { ".dot-file.withext",         ".dot-file.",             "withext" },
1219     { "sub/.dot-file.withext",     "sub/.dot-file.",         "withext" },
1220     { "sub/a.out",                 "sub/a.",                 "out" },
1221     { "a.out",                     "a.",                     "out" },
1222     { "",                          "",                       "" },
1223   };
1224 
1225   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
1226     {
1227       const char *path = tests[i].path;
1228       const char *path_root;
1229       const char *path_ext;
1230 
1231       svn_pool_clear(subpool);
1232 
1233       /* First, we'll try splitting and fetching both root and
1234          extension to see if they match our expected results. */
1235       svn_path_splitext(&path_root, &path_ext, path, subpool);
1236       if ((strcmp(tests[i].path_root, path_root))
1237           || (strcmp(tests[i].path_ext, path_ext)))
1238         return svn_error_createf
1239           (SVN_ERR_TEST_FAILED, NULL,
1240            "svn_path_splitext (%s) returned ('%s', '%s') "
1241            "instead of ('%s', '%s')",
1242            tests[i].path, path_root, path_ext,
1243            tests[i].path_root, tests[i].path_ext);
1244 
1245       /* Now, let's only fetch the root. */
1246       svn_path_splitext(&path_root, NULL, path, subpool);
1247       if (strcmp(tests[i].path_root, path_root))
1248         return svn_error_createf
1249           (SVN_ERR_TEST_FAILED, NULL,
1250            "svn_path_splitext (%s) with a NULL path_ext returned '%s' "
1251            "for the path_root instead of '%s'",
1252            tests[i].path, path_root, tests[i].path_root);
1253 
1254       /* Next, let's only fetch the extension. */
1255       svn_path_splitext(NULL, &path_ext, path, subpool);
1256       if ((strcmp(tests[i].path_root, path_root))
1257           || (strcmp(tests[i].path_ext, path_ext)))
1258         return svn_error_createf
1259           (SVN_ERR_TEST_FAILED, NULL,
1260            "svn_path_splitext (%s) with a NULL path_root returned '%s' "
1261            "for the path_ext instead of '%s'",
1262            tests[i].path, path_ext, tests[i].path_ext);
1263     }
1264   svn_pool_destroy(subpool);
1265   return SVN_NO_ERROR;
1266 }
1267 
1268 
1269 static svn_error_t *
test_path_compose(apr_pool_t * pool)1270 test_path_compose(apr_pool_t *pool)
1271 {
1272   static const char * const paths[] = {
1273     "",
1274     "/",
1275     "/foo",
1276     "/foo/bar",
1277     "/foo/bar/baz",
1278     "foo",
1279     "foo/bar",
1280     "foo/bar/baz",
1281     NULL,
1282   };
1283   const char * const *path_ptr = paths;
1284   const char *input_path;
1285 
1286   for (input_path = *path_ptr; *path_ptr; input_path = *++path_ptr)
1287     {
1288       apr_array_header_t *components = svn_path_decompose(input_path, pool);
1289       const char *output_path = svn_path_compose(components, pool);
1290 
1291       if (strcmp(input_path, output_path))
1292         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
1293                                  "svn_path_compose("
1294                                  "svn_path_decompose(\"%s\")) "
1295                                  "returned \"%s\" expected \"%s\"",
1296                                  input_path, output_path, input_path);
1297     }
1298 
1299   return SVN_NO_ERROR;
1300 }
1301 
1302 static svn_error_t *
test_path_is_canonical(apr_pool_t * pool)1303 test_path_is_canonical(apr_pool_t *pool)
1304 {
1305   struct {
1306     const char *path;
1307     svn_boolean_t canonical;
1308   } tests[] = {
1309     { "",                      TRUE },
1310     { ".",                     FALSE },
1311     { "/",                     TRUE },
1312     { "/.",                    FALSE },
1313     { "./",                    FALSE },
1314     { "./.",                   FALSE },
1315     { "//",                    FALSE },
1316     { "/////",                 FALSE },
1317     { "./././.",               FALSE },
1318     { "////././.",             FALSE },
1319     { "foo",                   TRUE },
1320     { ".foo",                  TRUE },
1321     { "foo.",                  TRUE },
1322     { "/foo",                  TRUE },
1323     { "foo/",                  FALSE },
1324     { "foo./",                 FALSE },
1325     { "foo./.",                FALSE },
1326     { "foo././/.",             FALSE },
1327     { "/foo/bar",              TRUE },
1328     { "foo/..",                TRUE },
1329     { "foo/../",               FALSE },
1330     { "foo/../.",              FALSE },
1331     { "foo//.//bar",           FALSE },
1332     { "///foo",                FALSE },
1333     { "/.//./.foo",            FALSE },
1334     { ".///.foo",              FALSE },
1335     { "../foo",                TRUE },
1336     { "../../foo/",            FALSE },
1337     { "../../foo/..",          TRUE },
1338     { "/../../",               FALSE },
1339     { "dirA",                  TRUE },
1340     { "foo/dirA",              TRUE },
1341     { "http://hst",            TRUE },
1342     { "http://hst/foo/../bar", TRUE },
1343     { "http://hst/",           FALSE },
1344     { "foo/./bar",             FALSE },
1345     { "http://HST/",           FALSE },
1346     { "http://HST/FOO/BaR",    FALSE },
1347     { "svn+ssh://j.raNDom@HST/BaR", FALSE },
1348     { "svn+SSH://j.random:jRaY@HST/BaR", FALSE },
1349     { "SVN+ssh://j.raNDom:jray@HST/BaR", FALSE },
1350     { "svn+ssh://j.raNDom:jray@hst/BaR", TRUE },
1351     { "fILe:///Users/jrandom/wc", FALSE },
1352     { "fiLE:///",              FALSE },
1353     { "fiLE://",               FALSE },
1354 #ifdef SVN_USE_DOS_PATHS
1355     { "file:///c:/temp/repos", FALSE },
1356     { "file:///c:/temp/REPOS", FALSE },
1357     { "file:///C:/temp/REPOS", TRUE },
1358     { "//server/share/",       FALSE },
1359     { "//server/share",        TRUE },
1360     { "//server/SHare",        TRUE },
1361     { "//SERVER/SHare",        FALSE },
1362     { "C:/folder/subfolder/file", TRUE },
1363 #else /* WIN32 or Cygwin */
1364     { "file:///c:/temp/repos", TRUE },
1365     { "file:///c:/temp/REPOS", TRUE },
1366     { "file:///C:/temp/REPOS", TRUE },
1367 #endif /* non-WIN32 */
1368     { NULL, FALSE },
1369   };
1370   int i;
1371 
1372   for (i = 0; tests[i].path; i++)
1373     {
1374       svn_boolean_t canonical;
1375 
1376       canonical = svn_path_is_canonical(tests[i].path, pool);
1377       if (tests[i].canonical != canonical)
1378         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
1379                                  "svn_path_is_canonical(\"%s\") returned "
1380                                  "\"%s\" expected \"%s\"",
1381                                  tests[i].path,
1382                                  canonical ? "TRUE" : "FALSE",
1383                                  tests[i].canonical ? "TRUE" : "FALSE");
1384     }
1385 
1386   return SVN_NO_ERROR;
1387 }
1388 
1389 static svn_error_t *
test_path_local_style(apr_pool_t * pool)1390 test_path_local_style(apr_pool_t *pool)
1391 {
1392   struct {
1393     const char *path;
1394     const char *result;
1395   } tests[] = {
1396     { "",                     "." },
1397     { ".",                    "." },
1398     { "http://host/dir",      "http://host/dir" }, /* Not with local separator */
1399 #ifdef SVN_USE_DOS_PATHS
1400     { "A:/",                 "A:\\" },
1401     { "a:/",                 "a:\\" },
1402     { "A:/file",             "A:\\file" },
1403     { "dir/file",            "dir\\file" },
1404     { "/",                   "\\" },
1405     { "//server/share/dir",  "\\\\server\\share\\dir" },
1406 #else
1407     { "a:/file",             "a:/file" },
1408     { "dir/file",            "dir/file" },
1409     { "/",                   "/" },
1410 #endif
1411     { NULL, NULL }
1412   };
1413   int i = 0;
1414 
1415   while (tests[i].path)
1416     {
1417       const char *local = svn_path_local_style(tests[i].path, pool);
1418 
1419       if (strcmp(local, tests[i].result))
1420         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
1421                                  "svn_path_local_style(\"%s\") returned "
1422                                  "\"%s\" expected \"%s\"",
1423                                  tests[i].path, local, tests[i].result);
1424       ++i;
1425     }
1426 
1427   return SVN_NO_ERROR;
1428 }
1429 
1430 static svn_error_t *
test_path_internal_style(apr_pool_t * pool)1431 test_path_internal_style(apr_pool_t *pool)
1432 {
1433   struct {
1434     const char *path;
1435     const char *result;
1436   } tests[] = {
1437     { "",                     "" },
1438     { ".",                    "" },
1439     { "http://host/dir",      "http://host/dir" },
1440     { "/",                    "/" },
1441 #ifdef SVN_USE_DOS_PATHS
1442     { "a:\\",                 "A:/" },
1443     { "a:\\file",             "A:/file" },
1444     { "dir\\file",            "dir/file" },
1445     { "\\",                   "/" },
1446     { "\\\\server/share/dir",  "//server/share/dir" },
1447 #else
1448     { "a:/",                 "a:" },
1449     { "a:/file",             "a:/file" },
1450     { "dir/file",            "dir/file" },
1451     { "/",                   "/" },
1452     { "//server/share/dir",  "/server/share/dir" },
1453 #endif
1454     { NULL, NULL }
1455   };
1456   int i;
1457 
1458   i = 0;
1459   while (tests[i].path)
1460     {
1461       const char *local = svn_path_internal_style(tests[i].path, pool);
1462 
1463       if (strcmp(local, tests[i].result))
1464         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
1465                                  "svn_path_internal_style(\"%s\") returned "
1466                                  "\"%s\" expected \"%s\"",
1467                                  tests[i].path, local, tests[i].result);
1468       ++i;
1469     }
1470 
1471   return SVN_NO_ERROR;
1472 }
1473 
1474 
1475 /* The type of a function to be tested by condense_targets_tests_helper().
1476  * Matches svn_path_condense_targets().
1477  */
1478 typedef svn_error_t *(*condense_targets_func_t)
1479                            (const char **pcommon,
1480                             apr_array_header_t **pcondensed_targets,
1481                             const apr_array_header_t *targets,
1482                             svn_boolean_t remove_redundancies,
1483                             apr_pool_t *pool);
1484 
1485 /** Executes function CONDENSE_TARGETS twice - with and without requesting the
1486  * condensed targets list -  on TEST_TARGETS (comma sep. string) and compares
1487  * the results with EXP_COMMON and EXP_TARGETS (comma sep. string).
1488  *
1489  * @note: a '%' character at the beginning of EXP_COMMON or EXP_TARGETS will
1490  * be replaced by the current working directory.
1491  *
1492  * Returns an error if any of the comparisons fail.
1493  */
1494 static svn_error_t *
condense_targets_tests_helper(const char * title,const char * test_targets,const char * exp_common,const char * exp_targets,const char * func_name,condense_targets_func_t condense_targets,apr_pool_t * pool)1495 condense_targets_tests_helper(const char* title,
1496                               const char* test_targets,
1497                               const char* exp_common,
1498                               const char* exp_targets,
1499                               const char* func_name,
1500                               condense_targets_func_t condense_targets,
1501                               apr_pool_t *pool)
1502 {
1503   apr_array_header_t *targets;
1504   apr_array_header_t *condensed_targets;
1505   const char *common_path, *common_path2, *curdir;
1506   char *token, *iter;
1507   const char *exp_common_abs = exp_common;
1508   int i;
1509   char buf[8192];
1510 
1511   if (! getcwd(buf, sizeof(buf)))
1512     return svn_error_create(SVN_ERR_BASE, NULL, "getcwd() failed");
1513   curdir = svn_path_internal_style(buf, pool);
1514 
1515   /* Create the target array */
1516   targets = apr_array_make(pool, sizeof(test_targets), sizeof(const char *));
1517   token = apr_strtok(apr_pstrdup(pool, test_targets), ",", &iter);
1518   while (token)
1519     {
1520       APR_ARRAY_PUSH(targets, const char *) =
1521         svn_path_internal_style(token, pool);
1522       token = apr_strtok(NULL, ",", &iter);
1523     };
1524 
1525   /* Call the function */
1526   SVN_ERR(condense_targets(&common_path, &condensed_targets, targets,
1527                            TRUE, pool));
1528 
1529   /* Verify the common part with the expected (prefix with cwd). */
1530   if (*exp_common == '%')
1531     exp_common_abs = apr_pstrcat(pool, curdir, exp_common + 1, SVN_VA_NULL);
1532 
1533   if (strcmp(common_path, exp_common_abs) != 0)
1534     {
1535       return svn_error_createf
1536         (SVN_ERR_TEST_FAILED, NULL,
1537          "%s (test %s) returned %s instead of %s",
1538            func_name, title,
1539            common_path, exp_common_abs);
1540     }
1541 
1542   /* Verify the condensed targets */
1543   token = apr_strtok(apr_pstrdup(pool, exp_targets), ",", &iter);
1544   for (i = 0; i < condensed_targets->nelts; i++)
1545     {
1546       const char * target = APR_ARRAY_IDX(condensed_targets, i, const char*);
1547       if (token && (*token == '%'))
1548         token = apr_pstrcat(pool, curdir, token + 1, SVN_VA_NULL);
1549       if (! token ||
1550           (target && (strcmp(target, token) != 0)))
1551         {
1552           return svn_error_createf
1553             (SVN_ERR_TEST_FAILED, NULL,
1554              "%s (test %s) couldn't find %s in expected targets list",
1555                func_name, title,
1556                target);
1557         }
1558       token = apr_strtok(NULL, ",", &iter);
1559     }
1560 
1561   /* Now ensure it works without the pbasename */
1562   SVN_ERR(condense_targets(&common_path2, NULL, targets, TRUE, pool));
1563 
1564   /* Verify the common part again */
1565   if (strcmp(common_path, common_path2) != 0)
1566     {
1567       return svn_error_createf
1568         (SVN_ERR_TEST_FAILED, NULL,
1569          "%s (test %s): Common path without getting targets %s does not match" \
1570          "common path with targets %s",
1571           func_name, title,
1572           common_path2, common_path);
1573     }
1574 
1575   return SVN_NO_ERROR;
1576 }
1577 
1578 
1579 static svn_error_t *
test_path_condense_targets(apr_pool_t * pool)1580 test_path_condense_targets(apr_pool_t *pool)
1581 {
1582   int i;
1583   struct {
1584     const char* title;
1585     const char* targets;
1586     const char* exp_common;
1587     const char* exp_targets;
1588   } tests[] = {
1589     { "normal use", "z/A/B,z/A,z/A/C,z/D/E,z/D/F,z/D,z/G,z/G/H,z/G/I",
1590       "%/z", "A,D,G" },
1591     {"identical dirs", "z/A,z/A,z/A,z/A",
1592      "%/z/A", "" },
1593     {"identical files", "z/A/file,z/A/file,z/A/file,z/A/file",
1594      "%/z/A/file", "" },
1595     {"single dir", "z/A",
1596      "%/z/A", "" },
1597     {"single file", "z/A/file",
1598      "%/z/A/file", "" },
1599     {"URLs", "http://host/A/C,http://host/A/C/D,http://host/A/B/D",
1600      "http://host/A", "C,B/D" },
1601     {"URLs with no common prefix",
1602      "http://host1/A/C,http://host2/A/C/D,http://host3/A/B/D",
1603      "", "http://host1/A/C,http://host2/A/C/D,http://host3/A/B/D" },
1604     {"file URLs with no common prefix", "file:///A/C,file:///B/D",
1605      "", "file:///A/C,file:///B/D" },
1606     {"URLs with mixed protocols",
1607      "http://host/A/C,file:///B/D,gopher://host/A",
1608      "", "http://host/A/C,file:///B/D,gopher://host/A" },
1609     {"mixed paths and URLs",
1610      "z/A/B,z/A,http://host/A/C/D,http://host/A/C",
1611      "", "%/z/A,http://host/A/C" },
1612   };
1613 
1614   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
1615     {
1616       SVN_ERR(condense_targets_tests_helper(tests[i].title,
1617                                             tests[i].targets,
1618                                             tests[i].exp_common,
1619                                             tests[i].exp_targets,
1620                                             "svn_path_condense_targets",
1621                                             svn_path_condense_targets,
1622                                             pool));
1623     }
1624 
1625   return SVN_NO_ERROR;
1626 }
1627 
1628 static svn_error_t *
test_path_is_repos_relative_url(apr_pool_t * pool)1629 test_path_is_repos_relative_url(apr_pool_t *pool)
1630 {
1631   int i;
1632   struct {
1633     const char* path;
1634     svn_boolean_t result;
1635   } tests[] = {
1636     { "^/A",           TRUE },
1637     { "http://host/A", FALSE },
1638     { "/A/B",          FALSE },
1639   };
1640 
1641   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
1642     {
1643       svn_boolean_t result = svn_path_is_repos_relative_url(tests[i].path);
1644 
1645       if (tests[i].result != result)
1646         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
1647                                  "svn_path_is_repos_relative_url(\"%s\")"
1648                                  " returned \"%s\" expected \"%s\"",
1649                                  tests[i].path,
1650                                  result ? "TRUE" : "FALSE",
1651                                  tests[i].result ? "TRUE" : "FALSE");
1652     }
1653 
1654   return SVN_NO_ERROR;
1655 }
1656 
1657 static svn_error_t *
test_path_resolve_repos_relative_url(apr_pool_t * pool)1658 test_path_resolve_repos_relative_url(apr_pool_t *pool)
1659 {
1660   int i;
1661   struct {
1662     const char *relative_url;
1663     const char *repos_root_url;
1664     const char *absolute_url;
1665   } tests[] = {
1666     { "^/A", "file:///Z/X", "file:///Z/X/A" },
1667     { "^/A", "file:///Z/X/", "file:///Z/X//A" }, /* doesn't canonicalize */
1668     { "^/A@2", "file:///Z/X", "file:///Z/X/A@2" }, /* peg rev */
1669     { "^/A", "/Z/X", "/Z/X/A" }, /* doesn't verify repos_root is URL */
1670   };
1671 
1672   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
1673     {
1674       const char *result;
1675 
1676       SVN_ERR(svn_path_resolve_repos_relative_url(&result,
1677                                                   tests[i].relative_url,
1678                                                   tests[i].repos_root_url,
1679                                                   pool));
1680 
1681       if (strcmp(tests[i].absolute_url,result))
1682         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
1683                                  "svn_path_resolve_repos_relative_url(\"%s\","
1684                                  "\"%s\") returned \"%s\" expected \"%s\"",
1685                                  tests[i].relative_url,
1686                                  tests[i].repos_root_url,
1687                                  result, tests[i].absolute_url);
1688     }
1689 
1690   return SVN_NO_ERROR;
1691 }
1692 
1693 
1694 /* local define to support XFail-ing tests on Windows/Cygwin only */
1695 #ifdef SVN_USE_DOS_PATHS
1696 #define WINDOWS_OR_CYGWIN TRUE
1697 #else
1698 #define WINDOWS_OR_CYGWIN FALSE
1699 #endif /* WIN32 or Cygwin */
1700 
1701 
1702 /* The test table.  */
1703 
1704 static int max_threads = 1;
1705 
1706 static struct svn_test_descriptor_t test_funcs[] =
1707   {
1708     SVN_TEST_NULL,
1709     SVN_TEST_PASS2(test_path_is_child,
1710                    "test svn_path_is_child"),
1711     SVN_TEST_PASS2(test_path_split,
1712                    "test svn_path_split"),
1713     SVN_TEST_PASS2(test_path_is_url,
1714                    "test svn_path_is_url"),
1715     SVN_TEST_PASS2(test_path_is_uri_safe,
1716                    "test svn_path_is_uri_safe"),
1717     SVN_TEST_PASS2(test_uri_encode,
1718                    "test svn_path_uri_[en/de]code"),
1719     SVN_TEST_PASS2(test_uri_decode,
1720                    "test svn_path_uri_decode with invalid escape"),
1721     SVN_TEST_PASS2(test_uri_autoescape,
1722                    "test svn_path_uri_autoescape"),
1723     SVN_TEST_PASS2(test_uri_from_iri,
1724                    "test svn_path_uri_from_iri"),
1725     SVN_TEST_PASS2(test_path_join,
1726                    "test svn_path_join(_many)"),
1727     SVN_TEST_PASS2(test_path_basename,
1728                    "test svn_path_basename"),
1729     SVN_TEST_PASS2(test_path_dirname,
1730                    "test svn_path_dirname"),
1731     SVN_TEST_PASS2(test_path_decompose,
1732                    "test svn_path_decompose"),
1733     SVN_TEST_PASS2(test_path_canonicalize,
1734                    "test svn_path_canonicalize"),
1735     SVN_TEST_PASS2(test_path_remove_component,
1736                    "test svn_path_remove_component"),
1737     SVN_TEST_PASS2(test_path_is_ancestor,
1738                    "test svn_path_is_ancestor"),
1739     SVN_TEST_PASS2(test_path_check_valid,
1740                    "test svn_path_check_valid"),
1741     SVN_TEST_PASS2(test_is_single_path_component,
1742                    "test svn_path_is_single_path_component"),
1743     SVN_TEST_PASS2(test_compare_paths,
1744                    "test svn_path_compare_paths"),
1745     SVN_TEST_PASS2(test_path_get_longest_ancestor,
1746                    "test svn_path_get_longest_ancestor"),
1747     SVN_TEST_PASS2(test_path_splitext,
1748                    "test svn_path_splitext"),
1749     SVN_TEST_PASS2(test_path_compose,
1750                    "test svn_path_decompose"),
1751     SVN_TEST_PASS2(test_path_is_canonical,
1752                    "test svn_path_is_canonical"),
1753     SVN_TEST_PASS2(test_path_local_style,
1754                    "test svn_path_local_style"),
1755     SVN_TEST_PASS2(test_path_internal_style,
1756                    "test svn_path_internal_style"),
1757     SVN_TEST_PASS2(test_path_condense_targets,
1758                    "test svn_path_condense_targets"),
1759     SVN_TEST_PASS2(test_path_is_repos_relative_url,
1760                    "test svn_path_is_repos_relative_url"),
1761     SVN_TEST_PASS2(test_path_resolve_repos_relative_url,
1762                    "test svn_path_resolve_repos_relative_url"),
1763     SVN_TEST_NULL
1764   };
1765 
1766 SVN_TEST_MAIN
1767