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