This patch doesn't say anything about whether we wanted or now want the design of *_is_canonical() and *_canonicalize() to be such that is_canonical(canonicalize(path)) is always true. I present this for interested parties to play with and make progress.
Sample outputs: [[[ DBG: dirent_uri-test.c:1191: non-c. dirent: 'O:.' -> 'O:.' DBG: dirent_uri-test.c:1191: non-c. dirent: './v:' -> 'v:' DBG: dirent_uri-test.c:1198: non-c. URI: 'u9' -> 'u9' ... DBG: dirent_uri-test.c:1180: f='/hxDC7/' lt-dirent_uri-test: /home/julianfoad/src/subversion-fsrepli/subversion/libsvn_subr/dirent_uri.c:322: canonicalize: Assertion `*src != '/'' failed. ]]] To get the dirent failures I forcibly defined SVN_USE_DOS_PATHS in libsvn_subr/dirent_uri.c. For whatever invariants we do decide should hold, it would be beneficial to have such a test in place. The relpath_* functions do currently seem to obey this invariant, so I suggest we commit at least the relpath part of this, if we agree that's intended behaviour. -- - Julian
Proof of concept: add random testing of is_canonical(canonicalize(path)). Sample outputs: [[[ DBG: dirent_uri-test.c:1191: non-c. dirent: 'O:.' -> 'O:.' DBG: dirent_uri-test.c:1191: non-c. dirent: './v:' -> 'v:' DBG: dirent_uri-test.c:1198: non-c. URI: 'u9' -> 'u9' ... DBG: dirent_uri-test.c:1180: f='/hxDC7/' lt-dirent_uri-test: /home/julianfoad/src/subversion-fsrepli/subversion/libsvn_subr/dirent_uri.c:322: canonicalize: Assertion `*src != '/'' failed. ]]] * subversion/tests/libsvn_subr/dirent_uri-test.c (random_filename): New. (test_dirent_is_canonical_random): New test. (test_funcs): Run it. --This line, and those below, will be ignored-- Index: subversion/tests/libsvn_subr/dirent_uri-test.c =================================================================== --- subversion/tests/libsvn_subr/dirent_uri-test.c (revision 1848853) +++ subversion/tests/libsvn_subr/dirent_uri-test.c (working copy) @@ -1129,12 +1129,89 @@ test_dirent_is_canonical(apr_pool_t *poo canonicalized); } return SVN_NO_ERROR; } +/* */ +static char * +random_filename(void) +{ + static char s[10]; + int len = (long)rand() * 9 / RAND_MAX; + int i; + + for (i = 0; i < len; ++i) + { + float r1 = (float)rand() / RAND_MAX; + float r2 = (float)rand() / RAND_MAX; + char c; + + if (r1 < 0.2) /* about 1 in 5: special path chars */ + { + c = "/:"[(int)(r2 * 1.2)]; + } + else if (r1 < 0.3) + { + c = (char)(r2 * 32); /* about 1 in 10: control chars */ + if (c == 0) c = 127; + } + else + { + c = (char)(32 + r2 * 95); /* the rest: printable ASCII chars */ + } + s[i] = c; + } + s[i] = '\0'; + + return s; +} + +static svn_error_t * +test_dirent_is_canonical_random(apr_pool_t *pool) +{ + int tries = 1000000; + int i; + + for (i = 0; i < tries; ++i) + { + const char *f = random_filename(); + const char *ized; + + /*SVN_DBG(("f='%s'", f));*/ + ized = svn_relpath_canonicalize(f, pool); + if (!svn_relpath_is_canonical(ized)) + { + SVN_DBG(("non-c. relpath: '%s' -> '%s'", f, ized)); + /*SVN_TEST_ASSERT(svn_relpath_is_canonical( + svn_relpath_canonicalize(f, pool)));*/ + } + ized = svn_dirent_canonicalize(f, pool); + if (!svn_dirent_is_canonical(ized, pool)) + { + SVN_DBG(("non-c. dirent: '%s' -> '%s'", f, ized)); + /*SVN_TEST_ASSERT(svn_dirent_is_canonical( + svn_dirent_canonicalize(f, pool), pool));*/ + } +#if 0 /* URI fails in many cases, and svn_uri_canonicalize assertion-fails + when input begins with '/' */ + ized = svn_uri_canonicalize(f, pool); + if (!svn_uri_is_canonical(ized, pool)) + { + SVN_DBG(("non-c. URI: '%s' -> '%s'", f, ized)); + /*SVN_TEST_ASSERT(svn_uri_is_canonical( + svn_uri_canonicalize(f, pool), pool));*/ + } +#endif + } + + return SVN_NO_ERROR; +} + +#undef SVN_USE_DOS_PATHS + static svn_error_t * test_relpath_is_canonical(apr_pool_t *pool) { const testcase_is_canonical_t *t; static const testcase_is_canonical_t tests[] = { { "", TRUE }, @@ -3016,10 +3093,12 @@ static struct svn_test_descriptor_t test SVN_TEST_PASS2(test_fspath_get_longest_ancestor, "test svn_fspath__get_longest_ancestor"), SVN_TEST_PASS2(test_cert_match_dns_identity, "test svn_cert__match_dns_identity"), SVN_TEST_XFAIL2(test_rule3, "test match with RFC 6125 s. 6.4.3 Rule 3"), + SVN_TEST_PASS2(test_dirent_is_canonical_random, + "test dirent_is_canonical random"), SVN_TEST_NULL }; SVN_TEST_MAIN