/* * path-test.c -- test the path functions * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== */ #ifdef _MSC_VER #include #define getcwd _getcwd #else #include /* for getcwd() */ #endif #include #include #include #include "svn_pools.h" #include "../svn_test.h" /* Make sure SVN_DEPRECATED is defined as empty before including svn_path.h. We don't want to trigger deprecation warnings by the tests of those functions. */ #ifdef SVN_DEPRECATED #undef SVN_DEPRECATED #endif #define SVN_DEPRECATED #include "svn_path.h" /* Using a symbol, because I tried experimenting with different representations */ #define SVN_EMPTY_PATH "" /* This check must match the check on top of dirent_uri.c and dirent_uri-tests.c */ #if defined(WIN32) || defined(__CYGWIN__) || defined(__OS2__) #define SVN_USE_DOS_PATHS #endif static svn_error_t * test_path_is_child(apr_pool_t *pool) { int i, j; /* The path checking code is platform specific, so we shouldn't run the Windows path handling testcases on non-Windows platforms. */ #define NUM_TEST_PATHS 11 static const char * const paths[NUM_TEST_PATHS] = { "/foo/bar", "/foo/bars", "/foo/baz", "/foo/bar/baz", "/flu/blar/blaz", "/foo/bar/baz/bing/boom", SVN_EMPTY_PATH, "foo", ".foo", "/", "foo2", }; static const char * const remainders[NUM_TEST_PATHS][NUM_TEST_PATHS] = { { 0, 0, 0, "baz", 0, "baz/bing/boom", 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, "bing/boom", 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, "foo", ".foo", 0, "foo2" }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { "foo/bar", "foo/bars", "foo/baz", "foo/bar/baz", "flu/blar/blaz", "foo/bar/baz/bing/boom", 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; for (i = 0; i < NUM_TEST_PATHS; i++) { for (j = 0; j < NUM_TEST_PATHS; j++) { const char *remainder; remainder = svn_path_is_child(paths[i], paths[j], pool); if (((remainder) && (! remainders[i][j])) || ((! remainder) && (remainders[i][j])) || (remainder && strcmp(remainder, remainders[i][j]))) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_is_child (%s, %s) returned '%s' instead of '%s'", paths[i], paths[j], remainder ? remainder : "(null)", remainders[i][j] ? remainders[i][j] : "(null)" ); } } #undef NUM_TEST_PATHS return SVN_NO_ERROR; } static svn_error_t * test_path_split(apr_pool_t *pool) { apr_size_t i; static const char * const paths[][3] = { { "/foo/bar", "/foo", "bar" }, { "/foo/bar/ ", "/foo/bar", " " }, { "/foo", "/", "foo" }, { "foo", SVN_EMPTY_PATH, "foo" }, { ".bar", SVN_EMPTY_PATH, ".bar" }, { "/.bar", "/", ".bar" }, { "foo/bar", "foo", "bar" }, { "/foo/bar", "/foo", "bar" }, { "foo/bar", "foo", "bar" }, { "foo./.bar", "foo.", ".bar" }, { "../foo", "..", "foo" }, { SVN_EMPTY_PATH, SVN_EMPTY_PATH, SVN_EMPTY_PATH }, { "/flu\\b/\\blarg", "/flu\\b", "\\blarg" }, { "/", "/", "/" }, }; for (i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { const char *dir, *base_name; svn_path_split(paths[i][0], &dir, &base_name, pool); if (strcmp(dir, paths[i][1])) { return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_split (%s) returned dirname '%s' instead of '%s'", paths[i][0], dir, paths[i][1]); } if (strcmp(base_name, paths[i][2])) { return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_split (%s) returned basename '%s' instead of '%s'", paths[i][0], base_name, paths[i][2]); } } return SVN_NO_ERROR; } static svn_error_t * test_path_is_url(apr_pool_t *pool) { apr_size_t i; /* Paths to test and their expected results. */ struct { const char *path; svn_boolean_t result; } tests[] = { { "", FALSE }, { "/blah/blah", FALSE }, { "//blah/blah", FALSE }, { "://blah/blah", FALSE }, { "a:abb://boo/", FALSE }, { "http://svn.apache.org/repos/asf/subversion", TRUE }, { "scheme/with", FALSE }, { "scheme/with:", FALSE }, { "scheme/with:/", FALSE }, { "scheme/with://", FALSE }, { "scheme/with://slash/", FALSE }, { "file:///path/to/repository", TRUE }, { "file://", TRUE }, { "file:/", FALSE }, { "file:", FALSE }, { "file", FALSE }, #ifdef SVN_USE_DOS_PATHS { "X:/foo", FALSE }, { "X:foo", FALSE }, { "X:", FALSE }, #endif /* non-WIN32 */ { "X:/", FALSE }, { "//srv/shr", FALSE }, { "//srv/shr/fld", FALSE }, }; for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { svn_boolean_t retval; retval = svn_path_is_url(tests[i].path); if (tests[i].result != retval) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_is_url (%s) returned %s instead of %s", tests[i].path, retval ? "TRUE" : "FALSE", tests[i].result ? "TRUE" : "FALSE"); } return SVN_NO_ERROR; } static svn_error_t * test_path_is_uri_safe(apr_pool_t *pool) { apr_size_t i; /* Paths to test and their expected results. */ struct { const char *path; svn_boolean_t result; } tests[] = { { "http://svn.collab.net/repos", TRUE }, { "http://svn.collab.net/repos%", FALSE }, { "http://svn.collab.net/repos%/svn", FALSE }, { "http://svn.collab.net/repos%2g", FALSE }, { "http://svn.collab.net/repos%2g/svn", FALSE }, { "http://svn.collab.net/repos%%", FALSE }, { "http://svn.collab.net/repos%%/svn", FALSE }, { "http://svn.collab.net/repos%2a", TRUE }, { "http://svn.collab.net/repos%2a/svn", TRUE }, }; for (i = 0; i < (sizeof(tests) / sizeof(tests[0])); i++) { svn_boolean_t retval; retval = svn_path_is_uri_safe(tests[i].path); if (tests[i].result != retval) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_is_uri_safe (%s) returned %s instead of %s", tests[i].path, retval ? "TRUE" : "FALSE", tests[i].result ? "TRUE" : "FALSE"); } return SVN_NO_ERROR; } static svn_error_t * test_uri_encode(apr_pool_t *pool) { int i; struct { const char *path; const char *result; } tests[] = { { "http://subversion.tigris.org", "http://subversion.tigris.org"}, { " special_at_beginning", "%20special_at_beginning" }, { "special_at_end ", "special_at_end%20" }, { "special in middle", "special%20in%20middle" }, { "\"Ouch!\" \"Did that hurt?\"", "%22Ouch!%22%20%20%22Did%20that%20hurt%3F%22" } }; for (i = 0; i < 5; i++) { const char *en_path, *de_path; /* URI-encode the path, and verify the results. */ en_path = svn_path_uri_encode(tests[i].path, pool); if (strcmp(en_path, tests[i].result)) { return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_uri_encode ('%s') returned '%s' instead of '%s'", tests[i].path, en_path, tests[i].result); } /* URI-decode the path, and make sure we're back where we started. */ de_path = svn_path_uri_decode(en_path, pool); if (strcmp(de_path, tests[i].path)) { return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_uri_decode ('%s') returned '%s' instead of '%s'", tests[i].result, de_path, tests[i].path); } } return SVN_NO_ERROR; } static svn_error_t * test_uri_decode(apr_pool_t *pool) { int i; struct { const char *path; const char *result; } tests[] = { { "http://c.r.a/s%\0" "8me", "http://c.r.a/s%"}, { "http://c.r.a/s%6\0" "me", "http://c.r.a/s%6" }, { "http://c.r.a/s%68me", "http://c.r.a/shme" }, }; for (i = 0; i < 3; i++) { const char *de_path; /* URI-decode the path, and verify the results. */ de_path = svn_path_uri_decode(tests[i].path, pool); if (strcmp(de_path, tests[i].result)) { return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_uri_decode ('%s') returned '%s' instead of '%s'", tests[i].path, de_path, tests[i].result); } } return SVN_NO_ERROR; } static svn_error_t * test_uri_autoescape(apr_pool_t *pool) { struct { const char *path; const char *result; } tests[] = { { "http://svn.collab.net/", "http://svn.collab.net/" }, { "file:///<>\" {}|\\^`", "file:///%3C%3E%22%20%7B%7D%7C%5C%5E%60" }, { "http://[::1]", "http://[::1]" } }; int i; for (i = 0; i < 3; ++i) { const char* uri = svn_path_uri_autoescape(tests[i].path, pool); if (strcmp(uri, tests[i].result) != 0) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_uri_autoescape on '%s' returned '%s' instead of '%s'", tests[i].path, uri, tests[i].result); if (strcmp(tests[i].path, tests[i].result) == 0 && tests[i].path != uri) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_uri_autoescape on '%s' returned identical but not same" " string", tests[i].path); } return SVN_NO_ERROR; } static svn_error_t * test_uri_from_iri(apr_pool_t *pool) { /* We have to code the IRIs like this because the compiler might translate character and string literals outside of ASCII to some character set, but here we are hard-coding UTF-8. But we all read UTF-8 codes like poetry, don't we. */ static const char p1[] = { '\x66', '\x69', '\x6C', '\x65', '\x3A', '\x2F', '\x2F', '\x2F', '\x72', '\xC3', '\xA4', '\x6B', '\x73', '\x6D', '\xC3', '\xB6', '\x72', '\x67', '\xC3', '\xA5', '\x73', '\0' }; static const char p2[] = { '\x66', '\x69', '\x6C', '\x65', '\x3A', '\x2F', '\x2F', '\x2F', '\x61', '\x62', '\x25', '\x32', '\x30', '\x63', '\x64', '\0' }; static const char *paths[2][2] = { { p1, "file:///r%C3%A4ksm%C3%B6rg%C3%A5s" }, { p2, "file:///ab%20cd" } }; int i; for (i = 0; i < 2; ++i) { const char *uri = svn_path_uri_from_iri(paths[i][0], pool); if (strcmp(paths[i][1], uri) != 0) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_uri_from_iri on '%s' returned '%s' instead of '%s'", paths[i][0], uri, paths[i][1]); if (strcmp(paths[i][0], uri) == 0 && paths[i][0] != uri) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_uri_from_iri on '%s' returned identical but not same" " string", paths[i][0]); } return SVN_NO_ERROR; } static svn_error_t * test_path_join(apr_pool_t *pool) { int i; char *result; static const char * const joins[][3] = { { "abc", "def", "abc/def" }, { "a", "def", "a/def" }, { "a", "d", "a/d" }, { "/", "d", "/d" }, { "/abc", "d", "/abc/d" }, { "/abc", "def", "/abc/def" }, { "/abc", "/def", "/def" }, { "/abc", "/d", "/d" }, { "/abc", "/", "/" }, { SVN_EMPTY_PATH, "/", "/" }, { "/", SVN_EMPTY_PATH, "/" }, { SVN_EMPTY_PATH, "abc", "abc" }, { "abc", SVN_EMPTY_PATH, "abc" }, { SVN_EMPTY_PATH, "/abc", "/abc" }, { SVN_EMPTY_PATH, SVN_EMPTY_PATH, SVN_EMPTY_PATH }, { "X:/abc", "/d", "/d" }, { "X:/abc", "/", "/" }, { "X:",SVN_EMPTY_PATH, "X:" }, { "X:", "/def", "/def" }, { "X:abc", "/d", "/d" }, { "X:abc", "/", "/" }, { "file://", "foo", "file:///foo" }, { "file:///foo", "bar", "file:///foo/bar" }, { "file:///foo", SVN_EMPTY_PATH, "file:///foo" }, { SVN_EMPTY_PATH, "file:///foo", "file:///foo" }, { "file:///X:", "bar", "file:///X:/bar" }, { "file:///X:foo", "bar", "file:///X:foo/bar" }, { "http://svn.dm.net", "repos", "http://svn.dm.net/repos" }, #ifdef SVN_USE_DOS_PATHS /* These will fail, see issue #2028 { "//srv/shr", "fld", "//srv/shr/fld" }, { "//srv", "shr/fld", "//srv/shr/fld" }, { "//srv/shr/fld", "subfld", "//srv/shr/fld/subfld" }, { "//srv/shr/fld", "//srv/shr", "//srv/shr" }, { "//srv", "//srv/fld", "//srv/fld" }, { "X:abc", "X:/def", "X:/def" }, { "X:/",SVN_EMPTY_PATH, "X:/" }, { "X:/","abc", "X:/abc" }, { "X:/", "/def", "/def" }, { "X:/abc", "X:/", "X:/" }, { "X:abc", "X:/", "X:/" }, { "X:abc", "X:/def", "X:/def" }, { "X:","abc", "X:abc" }, { "X:/abc", "X:/def", "X:/def" }, */ #else /* WIN32 or Cygwin */ { "X:abc", "X:/def", "X:abc/X:/def" }, { "X:","abc", "X:/abc" }, { "X:/abc", "X:/def", "X:/abc/X:/def" }, #endif /* non-WIN32 */ }; for (i = sizeof(joins) / sizeof(joins[0]); i--; ) { const char *base = joins[i][0]; const char *comp = joins[i][1]; const char *expect = joins[i][2]; result = svn_path_join(base, comp, pool); if (strcmp(result, expect)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_join(\"%s\", \"%s\") returned " "\"%s\". expected \"%s\"", base, comp, result, expect); /* svn_path_join_many does not support URLs, so skip the URL tests. */ if (svn_path_is_url(base)) continue; result = svn_path_join_many(pool, base, comp, SVN_VA_NULL); if (strcmp(result, expect)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_join_many(\"%s\", \"%s\") returned " "\"%s\". expected \"%s\"", base, comp, result, expect); } #define TEST_MANY(args, expect) \ result = svn_path_join_many args ; \ if (strcmp(result, expect) != 0) \ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, \ "svn_path_join_many" #args " returns \"%s\". " \ "expected \"%s\"", \ result, expect); TEST_MANY((pool, "abc", SVN_VA_NULL), "abc"); TEST_MANY((pool, "/abc", SVN_VA_NULL), "/abc"); TEST_MANY((pool, "/", SVN_VA_NULL), "/"); TEST_MANY((pool, "abc", "def", "ghi", SVN_VA_NULL), "abc/def/ghi"); TEST_MANY((pool, "abc", "/def", "ghi", SVN_VA_NULL), "/def/ghi"); TEST_MANY((pool, "/abc", "def", "ghi", SVN_VA_NULL), "/abc/def/ghi"); TEST_MANY((pool, "abc", "def", "/ghi", SVN_VA_NULL), "/ghi"); TEST_MANY((pool, "/", "def", "/ghi", SVN_VA_NULL), "/ghi"); TEST_MANY((pool, "/", "/def", "/ghi", SVN_VA_NULL), "/ghi"); TEST_MANY((pool, SVN_EMPTY_PATH, "def", "ghi", SVN_VA_NULL), "def/ghi"); TEST_MANY((pool, "abc", SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "abc/ghi"); TEST_MANY((pool, "abc", "def", SVN_EMPTY_PATH, SVN_VA_NULL), "abc/def"); TEST_MANY((pool, SVN_EMPTY_PATH, "def", SVN_EMPTY_PATH, SVN_VA_NULL), "def"); TEST_MANY((pool, SVN_EMPTY_PATH, SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "ghi"); TEST_MANY((pool, "abc", SVN_EMPTY_PATH, SVN_EMPTY_PATH, SVN_VA_NULL), "abc"); TEST_MANY((pool, SVN_EMPTY_PATH, "def", "/ghi", SVN_VA_NULL), "/ghi"); TEST_MANY((pool, SVN_EMPTY_PATH, SVN_EMPTY_PATH, "/ghi", SVN_VA_NULL), "/ghi"); TEST_MANY((pool, "/", "def", "ghi", SVN_VA_NULL), "/def/ghi"); TEST_MANY((pool, "abc", "/", "ghi", SVN_VA_NULL), "/ghi"); TEST_MANY((pool, "abc", "def", "/", SVN_VA_NULL), "/"); TEST_MANY((pool, "/", "/", "ghi", SVN_VA_NULL), "/ghi"); TEST_MANY((pool, "/", "/", "/", SVN_VA_NULL), "/"); TEST_MANY((pool, "/", SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "/ghi"); TEST_MANY((pool, "/", "def", SVN_EMPTY_PATH, SVN_VA_NULL), "/def"); TEST_MANY((pool, SVN_EMPTY_PATH, "/", "ghi", SVN_VA_NULL), "/ghi"); TEST_MANY((pool, "/", SVN_EMPTY_PATH, SVN_EMPTY_PATH, SVN_VA_NULL), "/"); TEST_MANY((pool, SVN_EMPTY_PATH, "/", SVN_EMPTY_PATH, SVN_VA_NULL), "/"); TEST_MANY((pool, SVN_EMPTY_PATH, SVN_EMPTY_PATH, "/", SVN_VA_NULL), "/"); #ifdef SVN_USE_DOS_PATHS /* These will fail, see issue #2028 TEST_MANY((pool, "X:", "def", "ghi", SVN_VA_NULL), "X:def/ghi"); TEST_MANY((pool, "X:", SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "X:ghi"); TEST_MANY((pool, "X:", "def", SVN_EMPTY_PATH, SVN_VA_NULL), "X:def"); TEST_MANY((pool, SVN_EMPTY_PATH, "X:", "ghi", SVN_VA_NULL), "X:ghi"); TEST_MANY((pool, "X:/", "def", "ghi", SVN_VA_NULL), "X:/def/ghi"); TEST_MANY((pool, "abc", "X:/", "ghi", SVN_VA_NULL), "X:/ghi"); TEST_MANY((pool, "abc", "def", "X:/", SVN_VA_NULL), "X:/"); TEST_MANY((pool, "X:/", "X:/", "ghi", SVN_VA_NULL), "X:/ghi"); TEST_MANY((pool, "X:/", "X:/", "/", SVN_VA_NULL), "/"); TEST_MANY((pool, "X:/", SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "X:/ghi"); TEST_MANY((pool, "X:/", "def", SVN_EMPTY_PATH, SVN_VA_NULL), "X:/def"); TEST_MANY((pool, SVN_EMPTY_PATH, "X:/", "ghi", SVN_VA_NULL), "X:/ghi"); TEST_MANY((pool, "X:/", SVN_EMPTY_PATH, SVN_EMPTY_PATH, SVN_VA_NULL), "X:/"); TEST_MANY((pool, SVN_EMPTY_PATH, "X:/", SVN_EMPTY_PATH, SVN_VA_NULL), "X:/"); TEST_MANY((pool, SVN_EMPTY_PATH, SVN_EMPTY_PATH, "X:/", SVN_VA_NULL), "X:/"); TEST_MANY((pool, "X:", "X:/", "ghi", SVN_VA_NULL), "X:/ghi"); TEST_MANY((pool, "X:", "X:/", "/", SVN_VA_NULL), "/"); TEST_MANY((pool, "//srv/shr", "def", "ghi", SVN_VA_NULL), "//srv/shr/def/ghi"); TEST_MANY((pool, "//srv", "shr", "def", "ghi", SVN_VA_NULL), "//srv/shr/def/ghi"); TEST_MANY((pool, "//srv/shr/fld", "def", "ghi", SVN_VA_NULL), "//srv/shr/fld/def/ghi"); TEST_MANY((pool, "//srv/shr/fld", "def", "//srv/shr", SVN_VA_NULL), "//srv/shr"); TEST_MANY((pool, "//srv", "shr", "//srv/shr", SVN_VA_NULL), "//srv/shr"); TEST_MANY((pool, SVN_EMPTY_PATH, "//srv/shr/fld", "def", "ghi", SVN_VA_NULL), "//srv/shr/fld/def/ghi"); TEST_MANY((pool, SVN_EMPTY_PATH, "//srv/shr/fld", "def", "//srv/shr", SVN_VA_NULL), "//srv/shr"); */ #else /* WIN32 or Cygwin */ TEST_MANY((pool, "X:", "def", "ghi", SVN_VA_NULL), "X:/def/ghi"); TEST_MANY((pool, "X:", SVN_EMPTY_PATH, "ghi", SVN_VA_NULL), "X:/ghi"); TEST_MANY((pool, "X:", "def", SVN_EMPTY_PATH, SVN_VA_NULL), "X:/def"); TEST_MANY((pool, SVN_EMPTY_PATH, "X:", "ghi", SVN_VA_NULL), "X:/ghi"); #endif /* non-WIN32 */ /* ### probably need quite a few more tests... */ return SVN_NO_ERROR; } static svn_error_t * test_path_basename(apr_pool_t *pool) { int i; char *result; struct { const char *path; const char *result; } tests[] = { { "abc", "abc" }, { "/abc", "abc" }, { "/abc", "abc" }, { "/x/abc", "abc" }, { "/xx/abc", "abc" }, { "/xx/abc", "abc" }, { "/xx/abc", "abc" }, { "a", "a" }, { "/a", "a" }, { "/b/a", "a" }, { "/b/a", "a" }, { "/", "/" }, { SVN_EMPTY_PATH, SVN_EMPTY_PATH }, { "X:/abc", "abc" }, { "X:", "X:" }, #ifdef SVN_USE_DOS_PATHS /* These will fail, see issue #2028 { "X:/", "X:/" }, { "X:abc", "abc" }, { "//srv/shr", "//srv/shr" }, { "//srv", "//srv" }, { "//srv/shr/fld", "fld" }, { "//srv/shr/fld/subfld", "subfld" }, */ #else /* WIN32 or Cygwin */ { "X:abc", "X:abc" }, #endif /* non-WIN32 */ }; for (i = sizeof(tests) / sizeof(tests[0]); i--; ) { const char *path = tests[i].path; const char *expect = tests[i].result; result = svn_path_basename(path, pool); if (strcmp(result, expect)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_basename(\"%s\") returned " "\"%s\". expected \"%s\"", path, result, expect); } return SVN_NO_ERROR; } static svn_error_t * test_path_dirname(apr_pool_t *pool) { int i; char *result; struct { const char *path; const char *result; } tests[] = { { "abc", "" }, { "/abc", "/" }, { "/x/abc", "/x" }, { "/xx/abc", "/xx" }, { "a", "" }, { "/a", "/" }, { "/b/a", "/b" }, { "/", "/" }, { SVN_EMPTY_PATH, SVN_EMPTY_PATH }, { "X:abc/def", "X:abc" }, #ifdef SVN_USE_DOS_PATHS { "//srv/shr/fld", "//srv/shr" }, { "//srv/shr/fld/subfld", "//srv/shr/fld" }, /* These will fail, see issue #2028 { "X:/", "X:/" }, { "X:/abc", "X:/" }, { "X:", "X:" }, { "X:abc", "X:" }, { "//srv/shr", "//srv/shr" }, */ #else /* WIN32 or Cygwin */ /* on non-Windows platforms, ':' is allowed in pathnames */ { "X:", "" }, { "X:abc", "" }, #endif /* non-WIN32 */ }; for (i = sizeof(tests) / sizeof(tests[0]); i--; ) { const char *path = tests[i].path; const char *expect = tests[i].result; result = svn_path_dirname(path, pool); if (strcmp(result, expect)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_dirname(\"%s\") returned " "\"%s\". expected \"%s\"", path, result, expect); } return SVN_NO_ERROR; } static svn_error_t * test_path_decompose(apr_pool_t *pool) { static const char * const paths[] = { "/", "/", NULL, "foo", "foo", NULL, "/foo", "/", "foo", NULL, "/foo/bar", "/", "foo", "bar", NULL, "foo/bar", "foo", "bar", NULL, /* Are these canonical? Should the middle bits produce SVN_EMPTY_PATH? */ "foo/bar", "foo", "bar", NULL, NULL, }; int i = 0; for (;;) { if (! paths[i]) break; else { apr_array_header_t *components = svn_path_decompose(paths[i], pool); int j; for (j = 0; j < components->nelts; ++j) { const char *component = APR_ARRAY_IDX(components, j, const char*); if (! paths[i+j+1]) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_decompose(\"%s\") returned " "unexpected component \"%s\"", paths[i], component); if (strcmp(component, paths[i+j+1])) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_decompose(\"%s\") returned " "\"%s\" expected \"%s\"", paths[i], component, paths[i+j+1]); } if (paths[i+j+1]) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_decompose(\"%s\") failed " "to return \"%s\"", paths[i], paths[i+j+1]); i += components->nelts + 2; } } return SVN_NO_ERROR; } static svn_error_t * test_path_canonicalize(apr_pool_t *pool) { struct { const char *path; const char *result; } tests[] = { { "", "" }, { ".", "" }, { "/", "/" }, { "/.", "/" }, { "./", "" }, { "./.", "" }, { "//", "/" }, { "/////", "/" }, { "./././.", "" }, { "////././.", "/" }, { "foo", "foo" }, { ".foo", ".foo" }, { "foo.", "foo." }, { "/foo", "/foo" }, { "foo/", "foo" }, { "foo//", "foo" }, { "foo///", "foo" }, { "foo./", "foo." }, { "foo./.", "foo." }, { "foo././/.", "foo." }, { "/foo/bar", "/foo/bar" }, { "foo/..", "foo/.." }, { "foo/../", "foo/.." }, { "foo/../.", "foo/.." }, { "foo//.//bar", "foo/bar" }, { "///foo", "/foo" }, { "/.//./.foo", "/.foo" }, { ".///.foo", ".foo" }, { "../foo", "../foo" }, { "../../foo/", "../../foo" }, { "../../foo/..", "../../foo/.." }, { "/../../", "/../.." }, { "dirA", "dirA" }, { "foo/dirA", "foo/dirA" }, { "http://hst", "http://hst" }, { "http://hst/foo/../bar","http://hst/foo/../bar" }, { "http://hst/", "http://hst" }, { "http:///", "http://" }, { "https://", "https://" }, { "file:///", "file://" }, { "file://", "file://" }, { "svn:///", "svn://" }, { "svn+ssh:///", "svn+ssh://" }, { "http://HST/", "http://hst" }, { "http://HST/FOO/BaR", "http://hst/FOO/BaR" }, { "svn+ssh://j.raNDom@HST/BaR", "svn+ssh://j.raNDom@hst/BaR" }, { "svn+SSH://j.random:jRaY@HST/BaR", "svn+ssh://j.random:jRaY@hst/BaR" }, { "SVN+ssh://j.raNDom:jray@HST/BaR", "svn+ssh://j.raNDom:jray@hst/BaR" }, { "fILe:///Users/jrandom/wc", "file:///Users/jrandom/wc" }, { "fiLE:///", "file://" }, { "fiLE://", "file://" }, { "X:/foo", "X:/foo" }, { "X:", "X:" }, { "X:foo", "X:foo" }, #ifdef SVN_USE_DOS_PATHS { "file:///c:/temp/repos", "file:///C:/temp/repos" }, { "file:///c:/temp/REPOS", "file:///C:/temp/REPOS" }, { "file:///C:/temp/REPOS", "file:///C:/temp/REPOS" }, { "C:/folder/subfolder/file", "C:/folder/subfolder/file" }, /* We permit UNC paths on Windows. By definition UNC * paths must have two components so we should remove the * double slash if there is only one component. */ { "//hst", "/hst" }, { "//hst/./", "/hst" }, { "//server/share/", "//server/share" }, { "//server/SHare/", "//server/SHare" }, { "//SERVER/SHare/", "//server/SHare" }, { "X:/", "X:/" }, #else /* WIN32 or Cygwin */ { "file:///c:/temp/repos", "file:///c:/temp/repos" }, { "file:///c:/temp/REPOS", "file:///c:/temp/REPOS" }, { "file:///C:/temp/REPOS", "file:///C:/temp/REPOS" }, #endif /* non-WIN32 */ { NULL, NULL } }; int i; i = 0; while (tests[i].path) { const char *canonical = svn_path_canonicalize(tests[i].path, pool); if (strcmp(canonical, tests[i].result)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_canonicalize(\"%s\") returned " "\"%s\" expected \"%s\"", tests[i].path, canonical, tests[i].result); ++i; } return SVN_NO_ERROR; } static svn_error_t * test_path_remove_component(apr_pool_t *pool) { struct { const char *path; const char *result; } tests[] = { { "", "" }, { "/", "/" }, { "foo", "" }, { "foo/bar", "foo" }, { "/foo/bar", "/foo" }, { "/foo", "/" }, #ifdef SVN_USE_DOS_PATHS { "X:/foo/bar", "X:/foo" }, { "//srv/shr/fld", "//srv/shr" }, { "//srv/shr/fld/subfld", "//srv/shr/fld" }, /* These will fail, see issue #2028 { "X:/foo", "X:/" }, { "X:/", "X:/" }, { "X:foo", "X:" }, { "X:", "X:" }, { "//srv/shr", "//srv/shr" }, */ #else /* WIN32 or Cygwin */ { "X:foo", "" }, { "X:", "" }, #endif /* non-WIN32 */ { NULL, NULL } }; int i; svn_stringbuf_t *buf; buf = svn_stringbuf_create_empty(pool); i = 0; while (tests[i].path) { svn_stringbuf_set(buf, tests[i].path); svn_path_remove_component(buf); if (strcmp(buf->data, tests[i].result)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_remove_component(\"%s\") returned " "\"%s\" expected \"%s\"", tests[i].path, buf->data, tests[i].result); ++i; } return SVN_NO_ERROR; } static svn_error_t * test_path_check_valid(apr_pool_t *pool) { apr_size_t i; /* Paths to test and their expected results. */ struct { const char *path; svn_boolean_t result; } tests[] = { { "/foo/bar", TRUE }, { "/foo", TRUE }, { "/", TRUE }, { "foo/bar", TRUE }, { "foo bar", TRUE }, { "foo\7bar", FALSE }, { "foo\31bar", FALSE }, { "\7foo\31bar", FALSE }, { "\7", FALSE }, { "", TRUE }, }; for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { svn_error_t *err = svn_path_check_valid(tests[i].path, pool); svn_boolean_t retval = (err == SVN_NO_ERROR); svn_error_clear(err); if (tests[i].result != retval) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_check_valid (%s) returned %s instead of %s", tests[i].path, retval ? "TRUE" : "FALSE", tests[i].result ? "TRUE" : "FALSE"); } return SVN_NO_ERROR; } static svn_error_t * test_path_is_ancestor(apr_pool_t *pool) { apr_size_t i; /* Paths to test and their expected results. */ struct { const char *path1; const char *path2; svn_boolean_t result; } tests[] = { { "/foo", "/foo/bar", TRUE}, { "/foo/bar", "/foo/bar/", TRUE}, { "/", "/foo", TRUE}, { SVN_EMPTY_PATH, "foo", TRUE}, { SVN_EMPTY_PATH, ".bar", TRUE}, { "/.bar", "/", FALSE}, { "foo/bar", "foo", FALSE}, { "/foo/bar", "/foo", FALSE}, { "foo", "foo/bar", TRUE}, { "foo.", "foo./.bar", TRUE}, { "../foo", "..", FALSE}, { SVN_EMPTY_PATH, SVN_EMPTY_PATH, TRUE}, { "/", "/", TRUE}, { "http://test", "http://test", TRUE}, { "http://test", "http://taste", FALSE}, { "http://test", "http://test/foo", TRUE}, { "http://test", "file://test/foo", FALSE}, { "http://test", "http://testF", FALSE}, /* TODO: this testcase fails, showing that svn_path_is_ancestor shouldn't be used on urls. This is related to issue #1711. { "http://", "http://test", FALSE}, */ { "X:foo", "X:bar", FALSE}, #ifdef SVN_USE_DOS_PATHS { "//srv/shr", "//srv", FALSE}, { "//srv/shr", "//srv/shr/fld", TRUE }, { "//srv", "//srv/shr/fld", TRUE }, { "//srv/shr/fld", "//srv/shr", FALSE }, { "//srv/shr/fld", "//srv2/shr/fld", FALSE }, /* These will fail, see issue #2028 { "X:/", "X:/", TRUE}, { "X:/foo", "X:/", FALSE}, { "X:/", "X:/foo", TRUE}, { "X:", "X:foo", TRUE}, */ #else /* WIN32 or Cygwin */ { "X:", "X:foo", FALSE}, #endif /* non-WIN32 */ }; for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { svn_boolean_t retval; retval = svn_path_is_ancestor(tests[i].path1, tests[i].path2); if (tests[i].result != retval) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_is_ancestor (%s, %s) returned %s instead of %s", tests[i].path1, tests[i].path2, retval ? "TRUE" : "FALSE", tests[i].result ? "TRUE" : "FALSE"); } return SVN_NO_ERROR; } static svn_error_t * test_is_single_path_component(apr_pool_t *pool) { apr_size_t i; /* Paths to test and their expected results. * Note that these paths need to be canonical, * else we might trigger an abort(). */ struct { const char *path; svn_boolean_t result; } tests[] = { { "/foo/bar", FALSE }, { "/foo", FALSE }, { "/", FALSE }, { "foo/bar", FALSE }, { "foo", TRUE }, { "..", FALSE }, { "", FALSE }, }; for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { svn_boolean_t retval; retval = svn_path_is_single_path_component(tests[i].path); if (tests[i].result != retval) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_is_single_path_component (%s) returned %s instead of %s", tests[i].path, retval ? "TRUE" : "FALSE", tests[i].result ? "TRUE" : "FALSE"); } return SVN_NO_ERROR; } static svn_error_t * test_compare_paths(apr_pool_t *pool) { apr_size_t i; /* Paths to test and their expected results. */ struct { const char *path1; const char *path2; int result; } tests[] = { { "/foo", "/foo", 0}, { "/foo/bar", "/foo/bar", 0}, { "/", "/", 0}, { SVN_EMPTY_PATH, SVN_EMPTY_PATH, 0}, { "foo", "foo", 0}, { "foo", "foo/bar", -1}, { "foo/bar", "foo/boo", -1}, { "boo", "foo", -1}, { "foo", "boo", 1}, { "foo/bar", "foo", 1}, { "/", "/foo", -1}, { "/foo", "/foo/bar", -1}, { "/foo", "/foo/bar/boo", -1}, { "foo", "/foo", 1}, { "foo\xe0""bar", "foo", 1}, { "X:/foo", "X:/foo", 0}, { "X:foo", "X:foo", 0}, { "X:", "X:foo", -1}, { "X:foo", "X:", 1}, #ifdef SVN_USE_DOS_PATHS { "//srv/shr", "//srv", 1}, { "//srv/shr", "//srv/shr/fld", -1 }, { "//srv/shr/fld", "//srv/shr", 1 }, { "//srv/shr/fld", "//abc/def/ghi", 1 }, /* These will fail, see issue #2028 { "X:/", "X:/", 0}, { "X:/", "X:/foo", -1}, { "X:/foo", "X:/", 1}, */ #endif /* WIN32 or Cygwin */ }; for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { int retval; retval = svn_path_compare_paths(tests[i].path1, tests[i].path2); /* tests if expected and actual result are both < 0, equal to 0 or greater than 0. */ if (! (tests[i].result * retval > 0 || (tests[i].result == 0 && retval == 0)) ) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_compare_paths (%s, %s) returned %d instead of %d", tests[i].path1, tests[i].path2, retval, tests[i].result); } return SVN_NO_ERROR; } static svn_error_t * test_path_get_longest_ancestor(apr_pool_t *pool) { apr_size_t i; /* Paths to test and their expected results. */ struct { const char *path1; const char *path2; const char *result; } tests[] = { { "/foo", "/foo/bar", "/foo"}, { "/foo/bar", "foo/bar", ""}, { "/", "/foo", "/"}, { SVN_EMPTY_PATH, "foo", SVN_EMPTY_PATH}, { SVN_EMPTY_PATH, ".bar", SVN_EMPTY_PATH}, { "/.bar", "/", "/"}, { "foo/bar", "foo", "foo"}, { "/foo/bar", "/foo", "/foo"}, { "/rif", "/raf", "/"}, { "foo", "foo/bar", "foo"}, { "foo.", "foo./.bar", "foo."}, { SVN_EMPTY_PATH, SVN_EMPTY_PATH, SVN_EMPTY_PATH}, { "/", "/", "/"}, { "http://test", "http://test", "http://test"}, { "http://test", "http://taste", ""}, { "http://test", "http://test/foo", "http://test"}, { "http://test", "file://test/foo", ""}, { "http://test", "http://tests", ""}, { "http://", "http://test", ""}, { "file:///A/C", "file:///B/D", ""}, { "file:///A/C", "file:///A/D", "file:///A"}, #ifdef SVN_USE_DOS_PATHS { "X:/", "X:/", "X:/"}, { "X:/foo/bar/A/D/H/psi", "X:/foo/bar/A/B", "X:/foo/bar/A" }, { "X:/foo/bar/boo", "X:/foo/bar/baz/boz", "X:/foo/bar"}, { "X:foo/bar", "X:foo/bar/boo", "X:foo/bar"}, { "//srv/shr", "//srv/shr/fld", "//srv/shr" }, { "//srv/shr/fld", "//srv/shr", "//srv/shr" }, /* These will fail, see issue #2028 { "//srv/shr/fld", "//srv2/shr/fld", "" }, { "X:/foo", "X:/", "X:/"}, { "X:/folder1", "X:/folder2", "X:/"}, { "X:/", "X:/foo", "X:/"}, { "X:", "X:foo", "X:"}, { "X:", "X:/", ""}, { "X:foo", "X:bar", "X:"}, */ #else /* WIN32 or Cygwin */ { "X:/foo", "X:", "X:"}, { "X:/folder1", "X:/folder2", "X:"}, { "X:", "X:foo", ""}, { "X:foo", "X:bar", ""}, #endif /* non-WIN32 */ }; for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { const char *retval; retval = svn_path_get_longest_ancestor(tests[i].path1, tests[i].path2, pool); if (strcmp(tests[i].result, retval)) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_get_longest_ancestor (%s, %s) returned %s instead of %s", tests[i].path1, tests[i].path2, retval, tests[i].result); /* changing the order of the paths should return the same results */ retval = svn_path_get_longest_ancestor(tests[i].path2, tests[i].path1, pool); if (strcmp(tests[i].result, retval)) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_get_longest_ancestor (%s, %s) returned %s instead of %s", tests[i].path2, tests[i].path1, retval, tests[i].result); } return SVN_NO_ERROR; } static svn_error_t * test_path_splitext(apr_pool_t *pool) { apr_size_t i; apr_pool_t *subpool = svn_pool_create(pool); /* Paths to test and their expected results. */ struct { const char *path; const char *path_root; const char *path_ext; } tests[] = { { "no-ext", "no-ext", "" }, { "test-file.py", "test-file.", "py" }, { "period.file.ext", "period.file.", "ext" }, { "multi-component/file.txt", "multi-component/file.", "txt" }, { "yep.still/no-ext", "yep.still/no-ext", "" }, { "folder.with/period.log", "folder.with/period.", "log" }, { "period.", "period.", "" }, { "dir/period.", "dir/period.", "" }, { "file.ends-with/period.", "file.ends-with/period.", "" }, { "two-periods..txt", "two-periods..", "txt" }, { ".dot-file", ".dot-file", "" }, { "sub/.dot-file", "sub/.dot-file", "" }, { ".dot-file.withext", ".dot-file.", "withext" }, { "sub/.dot-file.withext", "sub/.dot-file.", "withext" }, { "sub/a.out", "sub/a.", "out" }, { "a.out", "a.", "out" }, { "", "", "" }, }; for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { const char *path = tests[i].path; const char *path_root; const char *path_ext; svn_pool_clear(subpool); /* First, we'll try splitting and fetching both root and extension to see if they match our expected results. */ svn_path_splitext(&path_root, &path_ext, path, subpool); if ((strcmp(tests[i].path_root, path_root)) || (strcmp(tests[i].path_ext, path_ext))) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_splitext (%s) returned ('%s', '%s') " "instead of ('%s', '%s')", tests[i].path, path_root, path_ext, tests[i].path_root, tests[i].path_ext); /* Now, let's only fetch the root. */ svn_path_splitext(&path_root, NULL, path, subpool); if (strcmp(tests[i].path_root, path_root)) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_splitext (%s) with a NULL path_ext returned '%s' " "for the path_root instead of '%s'", tests[i].path, path_root, tests[i].path_root); /* Next, let's only fetch the extension. */ svn_path_splitext(NULL, &path_ext, path, subpool); if ((strcmp(tests[i].path_root, path_root)) || (strcmp(tests[i].path_ext, path_ext))) return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "svn_path_splitext (%s) with a NULL path_root returned '%s' " "for the path_ext instead of '%s'", tests[i].path, path_ext, tests[i].path_ext); } svn_pool_destroy(subpool); return SVN_NO_ERROR; } static svn_error_t * test_path_compose(apr_pool_t *pool) { static const char * const paths[] = { "", "/", "/foo", "/foo/bar", "/foo/bar/baz", "foo", "foo/bar", "foo/bar/baz", NULL, }; const char * const *path_ptr = paths; const char *input_path; for (input_path = *path_ptr; *path_ptr; input_path = *++path_ptr) { apr_array_header_t *components = svn_path_decompose(input_path, pool); const char *output_path = svn_path_compose(components, pool); if (strcmp(input_path, output_path)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_compose(" "svn_path_decompose(\"%s\")) " "returned \"%s\" expected \"%s\"", input_path, output_path, input_path); } return SVN_NO_ERROR; } static svn_error_t * test_path_is_canonical(apr_pool_t *pool) { struct { const char *path; svn_boolean_t canonical; } tests[] = { { "", TRUE }, { ".", FALSE }, { "/", TRUE }, { "/.", FALSE }, { "./", FALSE }, { "./.", FALSE }, { "//", FALSE }, { "/////", FALSE }, { "./././.", FALSE }, { "////././.", FALSE }, { "foo", TRUE }, { ".foo", TRUE }, { "foo.", TRUE }, { "/foo", TRUE }, { "foo/", FALSE }, { "foo./", FALSE }, { "foo./.", FALSE }, { "foo././/.", FALSE }, { "/foo/bar", TRUE }, { "foo/..", TRUE }, { "foo/../", FALSE }, { "foo/../.", FALSE }, { "foo//.//bar", FALSE }, { "///foo", FALSE }, { "/.//./.foo", FALSE }, { ".///.foo", FALSE }, { "../foo", TRUE }, { "../../foo/", FALSE }, { "../../foo/..", TRUE }, { "/../../", FALSE }, { "dirA", TRUE }, { "foo/dirA", TRUE }, { "http://hst", TRUE }, { "http://hst/foo/../bar", TRUE }, { "http://hst/", FALSE }, { "foo/./bar", FALSE }, { "http://HST/", FALSE }, { "http://HST/FOO/BaR", FALSE }, { "svn+ssh://j.raNDom@HST/BaR", FALSE }, { "svn+SSH://j.random:jRaY@HST/BaR", FALSE }, { "SVN+ssh://j.raNDom:jray@HST/BaR", FALSE }, { "svn+ssh://j.raNDom:jray@hst/BaR", TRUE }, { "fILe:///Users/jrandom/wc", FALSE }, { "fiLE:///", FALSE }, { "fiLE://", FALSE }, #ifdef SVN_USE_DOS_PATHS { "file:///c:/temp/repos", FALSE }, { "file:///c:/temp/REPOS", FALSE }, { "file:///C:/temp/REPOS", TRUE }, { "//server/share/", FALSE }, { "//server/share", TRUE }, { "//server/SHare", TRUE }, { "//SERVER/SHare", FALSE }, { "C:/folder/subfolder/file", TRUE }, #else /* WIN32 or Cygwin */ { "file:///c:/temp/repos", TRUE }, { "file:///c:/temp/REPOS", TRUE }, { "file:///C:/temp/REPOS", TRUE }, #endif /* non-WIN32 */ { NULL, FALSE }, }; int i; for (i = 0; tests[i].path; i++) { svn_boolean_t canonical; canonical = svn_path_is_canonical(tests[i].path, pool); if (tests[i].canonical != canonical) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_is_canonical(\"%s\") returned " "\"%s\" expected \"%s\"", tests[i].path, canonical ? "TRUE" : "FALSE", tests[i].canonical ? "TRUE" : "FALSE"); } return SVN_NO_ERROR; } static svn_error_t * test_path_local_style(apr_pool_t *pool) { struct { const char *path; const char *result; } tests[] = { { "", "." }, { ".", "." }, { "http://host/dir", "http://host/dir" }, /* Not with local separator */ #ifdef SVN_USE_DOS_PATHS { "A:/", "A:\\" }, { "a:/", "a:\\" }, { "A:/file", "A:\\file" }, { "dir/file", "dir\\file" }, { "/", "\\" }, { "//server/share/dir", "\\\\server\\share\\dir" }, #else { "a:/file", "a:/file" }, { "dir/file", "dir/file" }, { "/", "/" }, #endif { NULL, NULL } }; int i = 0; while (tests[i].path) { const char *local = svn_path_local_style(tests[i].path, pool); if (strcmp(local, tests[i].result)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_local_style(\"%s\") returned " "\"%s\" expected \"%s\"", tests[i].path, local, tests[i].result); ++i; } return SVN_NO_ERROR; } static svn_error_t * test_path_internal_style(apr_pool_t *pool) { struct { const char *path; const char *result; } tests[] = { { "", "" }, { ".", "" }, { "http://host/dir", "http://host/dir" }, { "/", "/" }, #ifdef SVN_USE_DOS_PATHS { "a:\\", "A:/" }, { "a:\\file", "A:/file" }, { "dir\\file", "dir/file" }, { "\\", "/" }, { "\\\\server/share/dir", "//server/share/dir" }, #else { "a:/", "a:" }, { "a:/file", "a:/file" }, { "dir/file", "dir/file" }, { "/", "/" }, { "//server/share/dir", "/server/share/dir" }, #endif { NULL, NULL } }; int i; i = 0; while (tests[i].path) { const char *local = svn_path_internal_style(tests[i].path, pool); if (strcmp(local, tests[i].result)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_internal_style(\"%s\") returned " "\"%s\" expected \"%s\"", tests[i].path, local, tests[i].result); ++i; } return SVN_NO_ERROR; } /* The type of a function to be tested by condense_targets_tests_helper(). * Matches svn_path_condense_targets(). */ typedef svn_error_t *(*condense_targets_func_t) (const char **pcommon, apr_array_header_t **pcondensed_targets, const apr_array_header_t *targets, svn_boolean_t remove_redundancies, apr_pool_t *pool); /** Executes function CONDENSE_TARGETS twice - with and without requesting the * condensed targets list - on TEST_TARGETS (comma sep. string) and compares * the results with EXP_COMMON and EXP_TARGETS (comma sep. string). * * @note: a '%' character at the beginning of EXP_COMMON or EXP_TARGETS will * be replaced by the current working directory. * * Returns an error if any of the comparisons fail. */ 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) { apr_array_header_t *targets; apr_array_header_t *condensed_targets; const char *common_path, *common_path2, *curdir; char *token, *iter; const char *exp_common_abs = exp_common; int i; char buf[8192]; if (! getcwd(buf, sizeof(buf))) return svn_error_create(SVN_ERR_BASE, NULL, "getcwd() failed"); curdir = svn_path_internal_style(buf, pool); /* Create the target array */ targets = apr_array_make(pool, sizeof(test_targets), sizeof(const char *)); token = apr_strtok(apr_pstrdup(pool, test_targets), ",", &iter); while (token) { APR_ARRAY_PUSH(targets, const char *) = svn_path_internal_style(token, pool); token = apr_strtok(NULL, ",", &iter); }; /* Call the function */ SVN_ERR(condense_targets(&common_path, &condensed_targets, targets, TRUE, pool)); /* Verify the common part with the expected (prefix with cwd). */ if (*exp_common == '%') exp_common_abs = apr_pstrcat(pool, curdir, exp_common + 1, SVN_VA_NULL); if (strcmp(common_path, exp_common_abs) != 0) { return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "%s (test %s) returned %s instead of %s", func_name, title, common_path, exp_common_abs); } /* Verify the condensed targets */ token = apr_strtok(apr_pstrdup(pool, exp_targets), ",", &iter); for (i = 0; i < condensed_targets->nelts; i++) { const char * target = APR_ARRAY_IDX(condensed_targets, i, const char*); if (token && (*token == '%')) token = apr_pstrcat(pool, curdir, token + 1, SVN_VA_NULL); if (! token || (target && (strcmp(target, token) != 0))) { return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "%s (test %s) couldn't find %s in expected targets list", func_name, title, target); } token = apr_strtok(NULL, ",", &iter); } /* Now ensure it works without the pbasename */ SVN_ERR(condense_targets(&common_path2, NULL, targets, TRUE, pool)); /* Verify the common part again */ if (strcmp(common_path, common_path2) != 0) { return svn_error_createf (SVN_ERR_TEST_FAILED, NULL, "%s (test %s): Common path without getting targets %s does not match" \ "common path with targets %s", func_name, title, common_path2, common_path); } return SVN_NO_ERROR; } static svn_error_t * test_path_condense_targets(apr_pool_t *pool) { int i; struct { const char* title; const char* targets; const char* exp_common; const char* exp_targets; } tests[] = { { "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", "%/z", "A,D,G" }, {"identical dirs", "z/A,z/A,z/A,z/A", "%/z/A", "" }, {"identical files", "z/A/file,z/A/file,z/A/file,z/A/file", "%/z/A/file", "" }, {"single dir", "z/A", "%/z/A", "" }, {"single file", "z/A/file", "%/z/A/file", "" }, {"URLs", "http://host/A/C,http://host/A/C/D,http://host/A/B/D", "http://host/A", "C,B/D" }, {"URLs with no common prefix", "http://host1/A/C,http://host2/A/C/D,http://host3/A/B/D", "", "http://host1/A/C,http://host2/A/C/D,http://host3/A/B/D" }, {"file URLs with no common prefix", "file:///A/C,file:///B/D", "", "file:///A/C,file:///B/D" }, {"URLs with mixed protocols", "http://host/A/C,file:///B/D,gopher://host/A", "", "http://host/A/C,file:///B/D,gopher://host/A" }, {"mixed paths and URLs", "z/A/B,z/A,http://host/A/C/D,http://host/A/C", "", "%/z/A,http://host/A/C" }, }; for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { SVN_ERR(condense_targets_tests_helper(tests[i].title, tests[i].targets, tests[i].exp_common, tests[i].exp_targets, "svn_path_condense_targets", svn_path_condense_targets, pool)); } return SVN_NO_ERROR; } static svn_error_t * test_path_is_repos_relative_url(apr_pool_t *pool) { int i; struct { const char* path; svn_boolean_t result; } tests[] = { { "^/A", TRUE }, { "http://host/A", FALSE }, { "/A/B", FALSE }, }; for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { svn_boolean_t result = svn_path_is_repos_relative_url(tests[i].path); if (tests[i].result != result) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_is_repos_relative_url(\"%s\")" " returned \"%s\" expected \"%s\"", tests[i].path, result ? "TRUE" : "FALSE", tests[i].result ? "TRUE" : "FALSE"); } return SVN_NO_ERROR; } static svn_error_t * test_path_resolve_repos_relative_url(apr_pool_t *pool) { int i; struct { const char *relative_url; const char *repos_root_url; const char *absolute_url; } tests[] = { { "^/A", "file:///Z/X", "file:///Z/X/A" }, { "^/A", "file:///Z/X/", "file:///Z/X//A" }, /* doesn't canonicalize */ { "^/A@2", "file:///Z/X", "file:///Z/X/A@2" }, /* peg rev */ { "^/A", "/Z/X", "/Z/X/A" }, /* doesn't verify repos_root is URL */ }; for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { const char *result; SVN_ERR(svn_path_resolve_repos_relative_url(&result, tests[i].relative_url, tests[i].repos_root_url, pool)); if (strcmp(tests[i].absolute_url,result)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "svn_path_resolve_repos_relative_url(\"%s\"," "\"%s\") returned \"%s\" expected \"%s\"", tests[i].relative_url, tests[i].repos_root_url, result, tests[i].absolute_url); } return SVN_NO_ERROR; } /* local define to support XFail-ing tests on Windows/Cygwin only */ #ifdef SVN_USE_DOS_PATHS #define WINDOWS_OR_CYGWIN TRUE #else #define WINDOWS_OR_CYGWIN FALSE #endif /* WIN32 or Cygwin */ /* The test table. */ static int max_threads = 1; static struct svn_test_descriptor_t test_funcs[] = { SVN_TEST_NULL, SVN_TEST_PASS2(test_path_is_child, "test svn_path_is_child"), SVN_TEST_PASS2(test_path_split, "test svn_path_split"), SVN_TEST_PASS2(test_path_is_url, "test svn_path_is_url"), SVN_TEST_PASS2(test_path_is_uri_safe, "test svn_path_is_uri_safe"), SVN_TEST_PASS2(test_uri_encode, "test svn_path_uri_[en/de]code"), SVN_TEST_PASS2(test_uri_decode, "test svn_path_uri_decode with invalid escape"), SVN_TEST_PASS2(test_uri_autoescape, "test svn_path_uri_autoescape"), SVN_TEST_PASS2(test_uri_from_iri, "test svn_path_uri_from_iri"), SVN_TEST_PASS2(test_path_join, "test svn_path_join(_many)"), SVN_TEST_PASS2(test_path_basename, "test svn_path_basename"), SVN_TEST_PASS2(test_path_dirname, "test svn_path_dirname"), SVN_TEST_PASS2(test_path_decompose, "test svn_path_decompose"), SVN_TEST_PASS2(test_path_canonicalize, "test svn_path_canonicalize"), SVN_TEST_PASS2(test_path_remove_component, "test svn_path_remove_component"), SVN_TEST_PASS2(test_path_is_ancestor, "test svn_path_is_ancestor"), SVN_TEST_PASS2(test_path_check_valid, "test svn_path_check_valid"), SVN_TEST_PASS2(test_is_single_path_component, "test svn_path_is_single_path_component"), SVN_TEST_PASS2(test_compare_paths, "test svn_path_compare_paths"), SVN_TEST_PASS2(test_path_get_longest_ancestor, "test svn_path_get_longest_ancestor"), SVN_TEST_PASS2(test_path_splitext, "test svn_path_splitext"), SVN_TEST_PASS2(test_path_compose, "test svn_path_decompose"), SVN_TEST_PASS2(test_path_is_canonical, "test svn_path_is_canonical"), SVN_TEST_PASS2(test_path_local_style, "test svn_path_local_style"), SVN_TEST_PASS2(test_path_internal_style, "test svn_path_internal_style"), SVN_TEST_PASS2(test_path_condense_targets, "test svn_path_condense_targets"), SVN_TEST_PASS2(test_path_is_repos_relative_url, "test svn_path_is_repos_relative_url"), SVN_TEST_PASS2(test_path_resolve_repos_relative_url, "test svn_path_resolve_repos_relative_url"), SVN_TEST_NULL }; SVN_TEST_MAIN