blob: 6f0a99651722d721c366d3e09e5ff548f582bbc4 [file] [log] [blame]
/*
* 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 <direct.h>
#define getcwd _getcwd
#else
#include <unistd.h> /* for getcwd() */
#endif
#include <stdio.h>
#include <string.h>
#include <apr_general.h>
#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