Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/authz-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/authz-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/authz-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/authz-test.c Fri Jan 14 14:01:45 2022 @@ -212,7 +212,7 @@ test_authz_parse(const svn_test_opts_t * APR_READ, APR_OS_DEFAULT, pool)); groups = svn_stream_from_aprfile2(groups_file, FALSE, pool); - SVN_ERR(svn_authz__parse(&authz, rules, groups, pool, pool)); + SVN_ERR(svn_authz__parse(&authz, rules, groups, NULL, NULL, pool, pool)); printf("Access check for ('%s', '%s')\n", check_user, check_repo); @@ -304,7 +304,7 @@ run_global_rights_tests(const char *cont svn_stringbuf_t *buffer = svn_stringbuf_create(contents, pool); svn_stream_t *stream = svn_stream_from_stringbuf(buffer, pool); - SVN_ERR(svn_repos_authz_parse(&authz, stream, NULL, pool)); + SVN_ERR(svn_repos_authz_parse2(&authz, stream, NULL, NULL, NULL, pool, pool)); for (; test_cases->repos; ++test_cases) { @@ -463,7 +463,7 @@ issue_4741_groups(apr_pool_t *pool) svn_authz_t *authz; svn_boolean_t access_granted; - SVN_ERR(svn_repos_authz_parse(&authz, stream, NULL, pool)); + SVN_ERR(svn_repos_authz_parse2(&authz, stream, NULL, NULL, NULL, pool, pool)); SVN_ERR(svn_repos_authz_check_access(authz, "repo", "/", "userA", svn_authz_write, &access_granted, @@ -481,7 +481,7 @@ issue_4741_groups(apr_pool_t *pool) static svn_error_t * reposful_reposless_stanzas_inherit(apr_pool_t *pool) { - const char rules[] = + const char rules[] = "[groups]" NL "company = user1, user2, user3" NL "customer = customer1, customer2" NL @@ -500,7 +500,7 @@ reposful_reposless_stanzas_inherit(apr_p svn_authz_t *authz; svn_boolean_t access_granted; - SVN_ERR(svn_repos_authz_parse(&authz, stream, NULL, pool)); + SVN_ERR(svn_repos_authz_parse2(&authz, stream, NULL, NULL, NULL, pool, pool)); SVN_ERR(svn_repos_authz_check_access(authz, "project1", "/foo", "user1", svn_authz_write | svn_authz_recursive, @@ -522,7 +522,7 @@ static struct svn_test_descriptor_t test "test svn_authz__get_global_rights"), SVN_TEST_PASS2(issue_4741_groups, "issue 4741 groups"), - SVN_TEST_XFAIL2(reposful_reposless_stanzas_inherit, + SVN_TEST_PASS2(reposful_reposless_stanzas_inherit, "[foo:/] inherits [/]"), SVN_TEST_NULL };
Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/repos-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/repos-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/repos-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/repos-test.c Fri Jan 14 14:01:45 2022 @@ -1181,6 +1181,7 @@ struct check_access_tests { * as defined in TESTS. */ static svn_error_t * authz_check_access(svn_authz_t *authz_cfg, + const char *authz_contents, const struct check_access_tests *tests, apr_pool_t *pool) { @@ -1202,7 +1203,7 @@ authz_check_access(svn_authz_t *authz_cf { return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "Authz incorrectly %s %s%s access " - "to %s%s%s for user %s", + "to %s%s%s for user %s\n%s", access_granted ? "grants" : "denies", tests[i].required @@ -1217,7 +1218,8 @@ authz_check_access(svn_authz_t *authz_cf ":" : "", tests[i].path, tests[i].user ? - tests[i].user : "-"); + tests[i].user : "-", + authz_contents); } } @@ -1312,11 +1314,11 @@ authz(apr_pool_t *pool) SVN_ERR(authz_get_handle(&authz_cfg, contents, FALSE, subpool)); /* Loop over the test array and test each case. */ - SVN_ERR(authz_check_access(authz_cfg, test_set, subpool)); + SVN_ERR(authz_check_access(authz_cfg, contents, test_set, subpool)); /* Repeat the previous test on disk */ SVN_ERR(authz_get_handle(&authz_cfg, contents, TRUE, subpool)); - SVN_ERR(authz_check_access(authz_cfg, test_set, subpool)); + SVN_ERR(authz_check_access(authz_cfg, contents, test_set, subpool)); /* The authz rules for the phase 2 tests, first case (cyclic dependency). */ @@ -1496,7 +1498,7 @@ test_authz_wildcards(apr_pool_t *pool) SVN_ERR(authz_get_handle(&authz_cfg, contents, FALSE, pool)); /* Loop over the test array and test each case. */ - SVN_ERR(authz_check_access(authz_cfg, test_set, pool)); + SVN_ERR(authz_check_access(authz_cfg, contents, test_set, pool)); return SVN_NO_ERROR; } @@ -1652,7 +1654,7 @@ test_authz_prefixes(apr_pool_t *pool) test->path = test_paths[i]; /* Loop over the test array and test each case. */ - SVN_ERR(authz_check_access(authz_cfg, test_set, iterpool)); + SVN_ERR(authz_check_access(authz_cfg, contents, test_set, iterpool)); } } @@ -1719,7 +1721,7 @@ test_authz_recursive_override(apr_pool_t SVN_ERR(authz_get_handle(&authz_cfg, contents, FALSE, pool)); /* Loop over the test array and test each case. */ - SVN_ERR(authz_check_access(authz_cfg, test_set, pool)); + SVN_ERR(authz_check_access(authz_cfg, contents, test_set, pool)); /* That's a wrap! */ return SVN_NO_ERROR; @@ -1898,16 +1900,16 @@ test_authz_pattern_tests(apr_pool_t *poo /* Verify that the rules are applies as expected. */ SVN_ERR(authz_get_handle(&authz_cfg, contents, FALSE, pool)); - SVN_ERR(authz_check_access(authz_cfg, test_set, pool)); + SVN_ERR(authz_check_access(authz_cfg, contents, test_set, pool)); SVN_ERR(authz_get_handle(&authz_cfg, contents2, FALSE, pool)); - SVN_ERR(authz_check_access(authz_cfg, test_set2, pool)); + SVN_ERR(authz_check_access(authz_cfg, contents2, test_set2, pool)); SVN_ERR(authz_get_handle(&authz_cfg, contents3, FALSE, pool)); - SVN_ERR(authz_check_access(authz_cfg, test_set3, pool)); + SVN_ERR(authz_check_access(authz_cfg, contents3, test_set3, pool)); SVN_ERR(authz_get_handle(&authz_cfg, contents4, FALSE, pool)); - SVN_ERR(authz_check_access(authz_cfg, test_set4, pool)); + SVN_ERR(authz_check_access(authz_cfg, contents4, test_set4, pool)); /* That's a wrap! */ return SVN_NO_ERROR; @@ -1984,7 +1986,7 @@ in_repo_authz(const svn_test_opts_t *opt /* absolute file URL. */ SVN_ERR(svn_repos_authz_read2(&authz_cfg, authz_url, NULL, TRUE, pool)); - SVN_ERR(authz_check_access(authz_cfg, test_set, pool)); + SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set, pool)); /* Non-existent path in the repo with must_exist set to FALSE */ SVN_ERR(svn_repos_authz_read2(&authz_cfg, noent_authz_url, NULL, @@ -2127,7 +2129,7 @@ in_repo_groups_authz(const svn_test_opts /* absolute file URLs. */ SVN_ERR(svn_repos_authz_read2(&authz_cfg, authz_url, groups_url, TRUE, pool)); - SVN_ERR(authz_check_access(authz_cfg, test_set, pool)); + SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set, pool)); /* Non-existent path for the groups file with must_exist * set to TRUE */ @@ -2318,12 +2320,12 @@ groups_authz(const svn_test_opts_t *opts SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents, groups_contents, TRUE, pool)); - SVN_ERR(authz_check_access(authz_cfg, test_set1, pool)); + SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set1, pool)); SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents, groups_contents, FALSE, pool)); - SVN_ERR(authz_check_access(authz_cfg, test_set1, pool)); + SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set1, pool)); /* Access rights in the global groups file are forbidden. */ groups_contents = @@ -2355,12 +2357,12 @@ groups_authz(const svn_test_opts_t *opts SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents, groups_contents, TRUE, pool)); - SVN_ERR(authz_check_access(authz_cfg, test_set2, pool)); + SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set2, pool)); SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents, groups_contents, FALSE, pool)); - SVN_ERR(authz_check_access(authz_cfg, test_set2, pool)); + SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set2, pool)); /* Local groups cannot be used in conjunction with global groups. */ groups_contents = @@ -4154,7 +4156,7 @@ mkdir_delete_copy(svn_repos_t *repos, svn_fs_root_t *txn_root, *rev_root; SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool)); - + SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); SVN_ERR(svn_fs_make_dir(txn_root, "A/T", pool)); Propchange: subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/ ------------------------------------------------------------------------------ --- svn:ignore (original) +++ svn:ignore Fri Jan 14 14:01:45 2022 @@ -56,3 +56,7 @@ sqlite-test-* x509-test xml-test test_apr_trunc_workaround +save-cleartext +test_stream_readline_file_crlf +test_stream_readline_file_lf +test_stream_readline_file_nul Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/dirent_uri-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/dirent_uri-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/dirent_uri-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/dirent_uri-test.c Fri Jan 14 14:01:45 2022 @@ -36,6 +36,7 @@ #include "svn_pools.h" #include "svn_dirent_uri.h" +#include "private/svn_dirent_uri_private.h" #include "private/svn_fspath.h" #include "private/svn_cert.h" @@ -2331,11 +2332,12 @@ test_relpath_internal_style(apr_pool_t * for (i = 0; i < COUNT_OF(tests); i++) { - const char *internal = svn_relpath__internal_style(tests[i].path, pool); + const char *internal; + SVN_ERR(svn_relpath__make_internal(&internal, tests[i].path, pool, pool)); if (strcmp(internal, tests[i].result)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, - "svn_relpath__internal_style(\"%s\") returned " + "svn_relpath__make_internal(\"%s\") returned " "\"%s\" expected \"%s\"", tests[i].path, internal, tests[i].result); } Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/io-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/io-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/io-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/io-test.c Fri Jan 14 14:01:45 2022 @@ -31,6 +31,7 @@ #include "svn_pools.h" #include "svn_string.h" #include "svn_io.h" +#include "svn_time.h" #include "private/svn_skel.h" #include "private/svn_dep_compat.h" #include "private/svn_io_private.h" @@ -211,6 +212,38 @@ create_comparison_candidates(struct test return err; } +/* Create an on-disk tree with optional read-only attributes on some + files and/or directories. */ +static svn_error_t * +create_dir_tree(const char **dir_path, + const char *testname, + svn_boolean_t dir_readonly, + svn_boolean_t file_readonly, + apr_pool_t *pool) +{ + const char *test_dir_path; + const char *sub_dir_path; + const char *file_path; + + SVN_ERR(svn_test_make_sandbox_dir(&test_dir_path, testname, pool)); + + sub_dir_path = svn_dirent_join(test_dir_path, "dir", pool); + SVN_ERR(svn_io_dir_make(sub_dir_path, APR_OS_DEFAULT, pool)); + + file_path = svn_dirent_join(sub_dir_path, "file", pool); + SVN_ERR(svn_io_file_create_empty(file_path, pool)); + + if (file_readonly) + SVN_ERR(svn_io_set_file_read_only(file_path, FALSE, pool)); + + if (dir_readonly) + SVN_ERR(svn_io_set_file_read_only(sub_dir_path, FALSE, pool)); + + *dir_path = sub_dir_path; + return SVN_NO_ERROR; +} + + /* Functions to check the 2-way and 3-way file comparison functions. */ @@ -939,7 +972,11 @@ test_install_stream_to_longpath(apr_pool const char *final_abspath; const char *deep_dir; svn_stream_t *stream; + apr_time_t mtime; + apr_off_t size; + svn_boolean_t is_read_only; svn_stringbuf_t *actual_content; + apr_finfo_t finfo; int i; /* Create an empty directory. */ @@ -960,10 +997,21 @@ test_install_stream_to_longpath(apr_pool SVN_ERR(svn_stream__create_for_install(&stream, deep_dir, pool, pool)); SVN_ERR(svn_stream_puts(stream, "stream1 content")); SVN_ERR(svn_stream_close(stream)); + SVN_ERR(svn_stream__install_finalize(&mtime, &size, stream, pool)); + /* Ensure that we will notice a timestamp change, if it happens. */ + svn_io_sleep_for_timestamps(NULL, pool); SVN_ERR(svn_stream__install_stream(stream, final_abspath, TRUE, pool)); + SVN_ERR(svn_io_stat(&finfo, final_abspath, + APR_FINFO_MTIME | APR_FINFO_SIZE | SVN__APR_FINFO_READONLY, + pool)); + /* Should see the same values as before the install. */ + SVN_TEST_INT_ASSERT(finfo.mtime, mtime); + SVN_TEST_INT_ASSERT(finfo.size, size); + SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, pool)); + SVN_TEST_ASSERT(!is_read_only); SVN_ERR(svn_stringbuf_from_file2(&actual_content, final_abspath, @@ -980,7 +1028,11 @@ test_install_stream_over_readonly_file(a const char *tmp_dir; const char *final_abspath; svn_stream_t *stream; + apr_time_t mtime; + apr_off_t size; + svn_boolean_t is_read_only; svn_stringbuf_t *actual_content; + apr_finfo_t finfo; /* Create an empty directory. */ SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, @@ -996,10 +1048,69 @@ test_install_stream_over_readonly_file(a SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, pool, pool)); SVN_ERR(svn_stream_puts(stream, "stream1 content")); SVN_ERR(svn_stream_close(stream)); + SVN_ERR(svn_stream__install_finalize(&mtime, &size, stream, pool)); + /* Ensure that we will notice a timestamp change, if it happens. */ + svn_io_sleep_for_timestamps(NULL, pool); + SVN_ERR(svn_stream__install_stream(stream, + final_abspath, + TRUE, + pool)); + SVN_ERR(svn_io_stat(&finfo, final_abspath, + APR_FINFO_MTIME | APR_FINFO_SIZE | SVN__APR_FINFO_READONLY, + pool)); + /* Should see the same values as before the install. */ + SVN_TEST_INT_ASSERT(finfo.mtime, mtime); + SVN_TEST_INT_ASSERT(finfo.size, size); + SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, pool)); + SVN_TEST_ASSERT(!is_read_only); + + SVN_ERR(svn_stringbuf_from_file2(&actual_content, + final_abspath, + pool)); + + SVN_TEST_STRING_ASSERT(actual_content->data, "stream1 content"); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_install_stream_set_read_only(apr_pool_t *pool) +{ + const char *tmp_dir; + const char *final_abspath; + svn_stream_t *stream; + apr_time_t mtime; + apr_off_t size; + svn_boolean_t is_read_only; + svn_stringbuf_t *actual_content; + apr_finfo_t finfo; + + /* Create an empty directory. */ + SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, + "test_install_stream_set_read_only", + pool)); + + final_abspath = svn_dirent_join(tmp_dir, "stream1", pool); + + SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, pool, pool)); + SVN_ERR(svn_stream_puts(stream, "stream1 content")); + SVN_ERR(svn_stream_close(stream)); + svn_stream__install_set_read_only(stream, TRUE); + SVN_ERR(svn_stream__install_finalize(&mtime, &size, stream, pool)); + /* Ensure that we will notice a timestamp change, if it happens. */ + svn_io_sleep_for_timestamps(NULL, pool); SVN_ERR(svn_stream__install_stream(stream, final_abspath, TRUE, pool)); + SVN_ERR(svn_io_stat(&finfo, final_abspath, + APR_FINFO_MTIME | APR_FINFO_SIZE | SVN__APR_FINFO_READONLY, + pool)); + /* Should see the same values as before the install. */ + SVN_TEST_INT_ASSERT(finfo.mtime, mtime); + SVN_TEST_INT_ASSERT(finfo.size, size); + SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, pool)); + SVN_TEST_ASSERT(is_read_only); SVN_ERR(svn_stringbuf_from_file2(&actual_content, final_abspath, @@ -1011,6 +1122,162 @@ test_install_stream_over_readonly_file(a } static svn_error_t * +test_install_stream_set_affected_time(apr_pool_t *pool) +{ + const char *tmp_dir; + const char *final_abspath; + svn_stream_t *stream; + apr_time_t expected_timestamp; + apr_time_t mtime; + apr_off_t size; + svn_boolean_t is_read_only; + svn_stringbuf_t *actual_content; + apr_finfo_t finfo; + + /* Create an empty directory. */ + SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, + "test_install_stream_set_affected_time", + pool)); + + final_abspath = svn_dirent_join(tmp_dir, "stream1", pool); + + SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, pool, pool)); + SVN_ERR(svn_stream_puts(stream, "stream1 content")); + SVN_ERR(svn_stream_close(stream)); + + SVN_ERR(svn_time_from_cstring(&expected_timestamp, + "2002-05-13T19:00:50.966679Z", + pool)); + svn_stream__install_set_affected_time(stream, expected_timestamp); + + SVN_ERR(svn_stream__install_finalize(&mtime, &size, stream, pool)); + /* Ensure that we will notice a timestamp change, if it happens. */ + svn_io_sleep_for_timestamps(NULL, pool); + SVN_ERR(svn_stream__install_stream(stream, + final_abspath, + TRUE, + pool)); + SVN_ERR(svn_io_stat(&finfo, final_abspath, + APR_FINFO_MTIME | APR_FINFO_SIZE | SVN__APR_FINFO_READONLY, + pool)); + /* Should see the same values as before the install. */ + SVN_TEST_INT_ASSERT(finfo.mtime, mtime); + SVN_TEST_INT_ASSERT(finfo.size, size); + /* The actual filesystem might have a different timestamp precision, + so compare with proximity. */ + SVN_TEST_TIME_ASSERT(finfo.mtime, expected_timestamp, apr_time_from_sec(10)); + SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, pool)); + SVN_TEST_ASSERT(!is_read_only); + + SVN_ERR(svn_stringbuf_from_file2(&actual_content, + final_abspath, + pool)); + + SVN_TEST_STRING_ASSERT(actual_content->data, "stream1 content"); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_install_stream(apr_pool_t *pool) +{ + const char *tmp_dir; + const char *final_abspath; + svn_stream_t *stream; + apr_time_t mtime; + apr_off_t size; + svn_boolean_t is_read_only; + svn_stringbuf_t *actual_content; + apr_finfo_t finfo; + + /* Create an empty directory. */ + SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, + "test_install_stream", + pool)); + + final_abspath = svn_dirent_join(tmp_dir, "stream1", pool); + + SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, pool, pool)); + SVN_ERR(svn_stream_puts(stream, "stream1 content")); + SVN_ERR(svn_stream_close(stream)); + SVN_ERR(svn_stream__install_finalize(&mtime, &size, stream, pool)); + /* Ensure that we will notice a timestamp change, if it happens. */ + svn_io_sleep_for_timestamps(NULL, pool); + SVN_ERR(svn_stream__install_stream(stream, + final_abspath, + TRUE, + pool)); + SVN_ERR(svn_io_stat(&finfo, final_abspath, + APR_FINFO_MTIME | APR_FINFO_SIZE | SVN__APR_FINFO_READONLY, + pool)); + /* Should see the same values as before the install. */ + SVN_TEST_INT_ASSERT(finfo.mtime, mtime); + SVN_TEST_INT_ASSERT(finfo.size, size); + SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, pool)); + SVN_TEST_ASSERT(!is_read_only); + + SVN_ERR(svn_stringbuf_from_file2(&actual_content, + final_abspath, + pool)); + SVN_TEST_STRING_ASSERT(actual_content->data, "stream1 content"); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_install_stream_delete(apr_pool_t *pool) +{ + const char *tmp_dir; + apr_pool_t *subpool; + svn_stream_t *stream; + apr_hash_t *dirents; + + /* Create an empty directory. */ + SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, + "test_install_stream_delete", + pool)); + + subpool = svn_pool_create(pool); + SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, subpool, subpool)); + SVN_ERR(svn_stream_puts(stream, "stream1 content")); + SVN_ERR(svn_stream_close(stream)); + SVN_ERR(svn_stream__install_delete(stream, subpool)); + svn_pool_destroy(subpool); + + SVN_ERR(svn_io_get_dirents3(&dirents, tmp_dir, TRUE, pool, pool)); + SVN_TEST_INT_ASSERT(apr_hash_count(dirents), 0); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_install_stream_delete_after_finalize(apr_pool_t *pool) +{ + const char *tmp_dir; + apr_pool_t *subpool; + svn_stream_t *stream; + apr_hash_t *dirents; + + /* Create an empty directory. */ + SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, + "test_install_stream_delete_after_finalize", + pool)); + + subpool = svn_pool_create(pool); + SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, subpool, subpool)); + SVN_ERR(svn_stream_puts(stream, "stream1 content")); + SVN_ERR(svn_stream_close(stream)); + SVN_ERR(svn_stream__install_finalize(NULL, NULL, stream, subpool)); + SVN_ERR(svn_stream__install_delete(stream, subpool)); + svn_pool_destroy(subpool); + + SVN_ERR(svn_io_get_dirents3(&dirents, tmp_dir, TRUE, pool, pool)); + SVN_TEST_INT_ASSERT(apr_hash_count(dirents), 0); + + return SVN_NO_ERROR; +} + +static svn_error_t * test_file_size_get(apr_pool_t *pool) { const char *tmp_dir, *path; @@ -1113,11 +1380,8 @@ test_apr_trunc_workaround(apr_pool_t *po char dummy; /* create a temp folder & schedule it for automatic cleanup */ - SVN_ERR(svn_dirent_get_absolute(&tmp_dir, "test_apr_trunc_workaround", - pool)); - SVN_ERR(svn_io_remove_dir2(tmp_dir, TRUE, NULL, NULL, pool)); - SVN_ERR(svn_io_make_dir_recursively(tmp_dir, pool)); - svn_test_add_dir_cleanup(tmp_dir); + SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, "test_apr_trunc_workaround", + pool)); /* create an r/w file */ tmp_file = svn_dirent_join(tmp_dir, "file", pool); @@ -1144,9 +1408,59 @@ test_apr_trunc_workaround(apr_pool_t *po SVN_ERR(svn_io_file_seek(f, APR_CUR, &offset, pool)); SVN_TEST_ASSERT(offset == (int)len); - return SVN_NO_ERROR; + return SVN_NO_ERROR; +} + + +/* Issue #4806 */ +static svn_error_t * +test_rmtree_all_writable(apr_pool_t *pool) +{ + const char *dir_path = NULL; + + SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_all_writable", + FALSE, FALSE, pool)); + SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool)); + return SVN_NO_ERROR; +} + +/* Issue #4806 */ +static svn_error_t * +test_rmtree_file_readonly(apr_pool_t *pool) +{ + const char *dir_path = NULL; + + SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_file_readonly", + FALSE, TRUE, pool)); + SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool)); + return SVN_NO_ERROR; +} + +/* Issue #4806 */ +static svn_error_t * +test_rmtree_dir_readonly(apr_pool_t *pool) +{ + const char *dir_path = NULL; + + SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_dir_readonly", + TRUE, FALSE, pool)); + SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool)); + return SVN_NO_ERROR; +} + +/* Issue #4806 */ +static svn_error_t * +test_rmtree_all_readonly(apr_pool_t *pool) +{ + const char *dir_path = NULL; + + SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_all_readonly", + TRUE, TRUE, pool)); + SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool)); + return SVN_NO_ERROR; } + /* The test table. */ static int max_threads = 3; @@ -1172,6 +1486,16 @@ static struct svn_test_descriptor_t test "test svn_stream__install_stream to long path"), SVN_TEST_PASS2(test_install_stream_over_readonly_file, "test svn_stream__install_stream over RO file"), + SVN_TEST_PASS2(test_install_stream_set_read_only, + "test svn_stream__install_set_read_only"), + SVN_TEST_PASS2(test_install_stream_set_affected_time, + "test svn_stream__install_set_affected_time"), + SVN_TEST_PASS2(test_install_stream, + "test svn_stream__install_stream"), + SVN_TEST_PASS2(test_install_stream_delete, + "test svn_stream__install_delete"), + SVN_TEST_PASS2(test_install_stream_delete_after_finalize, + "test svn_stream__install_delete after finalize"), SVN_TEST_PASS2(test_file_size_get, "test svn_io_file_size_get"), SVN_TEST_PASS2(test_file_rename2, @@ -1184,6 +1508,14 @@ static struct svn_test_descriptor_t test "test svn_io_open_uniquely_named()"), SVN_TEST_PASS2(test_apr_trunc_workaround, "test workaround for APR in svn_io_file_trunc"), + SVN_TEST_PASS2(test_rmtree_all_writable, + "test svn_io_remove_dir2() with writable tree"), + SVN_TEST_PASS2(test_rmtree_file_readonly, + "test svn_io_remove_dir2() with read-only file"), + SVN_TEST_PASS2(test_rmtree_dir_readonly, + "test svn_io_remove_dir2() with read-only directory"), + SVN_TEST_PASS2(test_rmtree_all_readonly, + "test svn_io_remove_dir2() with read-only tree"), SVN_TEST_NULL }; Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/mergeinfo-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/mergeinfo-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/mergeinfo-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/mergeinfo-test.c Fri Jan 14 14:01:45 2022 @@ -33,6 +33,8 @@ #include "svn_types.h" #include "svn_mergeinfo.h" #include "private/svn_mergeinfo_private.h" +#include "private/svn_sorts_private.h" +#include "private/svn_error_private.h" #include "../svn_test.h" /* A quick way to create error messages. */ @@ -1783,6 +1785,701 @@ test_rangelist_loop(apr_pool_t *pool) return SVN_NO_ERROR; } + +/* A specific case where result was non-canonical, around svn 1.10 ~ 1.13. */ +static svn_error_t * +test_rangelist_merge_canonical_result(apr_pool_t *pool) +{ + const char *rangelist_str = "8-10"; + const char *changes_str = "5-10*,11-24"; + const char *expected_str = "5-7*,8-24"; + /* wrong result: "5-7*,8-10,11-24" */ + svn_rangelist_t *rangelist, *changes; + svn_string_t *result_string; + + /* prepare the inputs */ + SVN_ERR(svn_rangelist__parse(&rangelist, rangelist_str, pool)); + SVN_ERR(svn_rangelist__parse(&changes, changes_str, pool)); + SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist)); + SVN_TEST_ASSERT(svn_rangelist__is_canonical(changes)); + + /* perform the merge */ + SVN_ERR(svn_rangelist_merge2(rangelist, changes, pool, pool)); + + /* check the output */ + SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist)); + SVN_ERR(svn_rangelist_to_string(&result_string, rangelist, pool)); + SVN_TEST_STRING_ASSERT(result_string->data, expected_str); + + return SVN_NO_ERROR; +} + +/* Test svn_rangelist_merge2() with specific inputs derived from an + * occurrence of issue #4840 "in the wild", that triggered a hard + * assertion failure (abort) in a 1.10.6 release-mode build. + */ +static svn_error_t * +test_rangelist_merge_array_insert_failure(apr_pool_t *pool) +{ + svn_rangelist_t *rx, *ry; + svn_string_t *rxs; + + /* Simplified case with same failure mode as reported case: error + * "E200004: svn_sort__array_insert2: + * Attempted insert at index 4 in array length 3" */ + SVN_ERR(svn_rangelist__parse(&rx, "2*,4*,6*,8", pool)); + SVN_ERR(svn_rangelist__parse(&ry, "1-9*,11", pool)); + SVN_ERR(svn_rangelist_merge2(rx, ry, pool, pool)); + SVN_ERR(svn_rangelist_to_string(&rxs, rx, pool)); + SVN_TEST_STRING_ASSERT(rxs->data, "1-7*,8,9*,11"); + + /* Actual reported case: in v1.10.6, aborted; after r1872118, error + * "E200004: svn_sort__array_insert2: + * Attempted insert at index 57 in array length 55". The actual "index" + * and "array length" numbers vary with changes such as r1823728. */ + SVN_ERR(svn_rangelist__parse(&rx, "997347-997597*,997884-1000223*,1000542-1000551*,1001389-1001516,1002139-1002268*,1002896-1003064*,1003320-1003468,1005939-1006089*,1006443-1006630*,1006631-1006857,1007028-1007116*,1009467-1009629,1009630-1010007*,1010774-1010860,1011036-1011502,1011672-1014004*,1014023-1014197,1014484-1014542*,1015077-1015568,1016219-1016365,1016698-1016845,1017331-1018616,1027032-1027180,1027855-1028051,1028261-1028395,1028553-1028663,1028674-1028708,1028773-1028891*,1029223-1030557,1032239-1032284*,1032801-1032959,1032960-1033074*,1033745-1033810,1034990-1035104,1035435-1036108*,1036109-1036395,1036396-1036865*,1036866-1036951,1036952-1037647*,1037648-1037750,1037751-1038548*,1038549-1038700,1038701-1042103*,1042104-1042305,1042306-1046626*,1046627-1046910,1046911-1047676*,1047677-1047818,1047819-1047914*,1047915-1048025,1048026-1048616*,1048617-1048993,1048994-1050066*,1054605-1054739,1054854-1055021", pool)); + SVN_ERR(svn_rangelist__parse(&ry, "1035435-1050066*,1052459-1054617", pool)); + SVN_ERR(svn_rangelist_merge2(rx, ry, pool, pool)); + /* Here we don't care to check the result; just that it returns "success". */ + return SVN_NO_ERROR; +} + + +/* Random testing parameters and coverage + * + * The parameters for testing random inputs, in conjunction with the + * specific test case generation code, were adjusted so as to observe the + * tests generating each of the known failure modes. The aim is also to + * have sufficient coverage of inputs to discover other failure modes in + * future if the code is changed. + * + * There are neither theoretic nor empirical guarantees on the coverage. + */ + +/* Randomize revision numbers over this small range. + * (With a larger range, we would find edge cases more rarely.) + * See comment "Random testing parameters and coverage" */ +#define RANGELIST_TESTS_MAX_REV 15 + +/* A representation of svn_rangelist_t in which + * root[R] := (revision R is in the rangelist) + * inherit[R] := (revision R is in the rangelist and inheritable) + * + * Assuming all forward ranges. + */ +typedef struct rl_array_t { + svn_boolean_t root[RANGELIST_TESTS_MAX_REV + 1]; + svn_boolean_t inherit[RANGELIST_TESTS_MAX_REV + 1]; +} rl_array_t; + +static void +rangelist_to_array(rl_array_t *a, + const svn_rangelist_t *rl) +{ + int i; + + memset(a, 0, sizeof(*a)); + for (i = 0; i < rl->nelts; i++) + { + svn_merge_range_t *range = APR_ARRAY_IDX(rl, i, svn_merge_range_t *); + svn_revnum_t r; + + for (r = range->start + 1; r <= range->end; r++) + { + a->root[r] = TRUE; + a->inherit[r] = range->inheritable; + } + } +} + +/* Compute the union of two rangelists arrays. + * Let MA := union(BA, CA) + */ +static void +rangelist_array_union(rl_array_t *ma, + const rl_array_t *ba, + const rl_array_t *ca) +{ + svn_revnum_t r; + + for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++) + { + ma->root[r] = ba->root[r] || ca->root[r]; + ma->inherit[r] = ba->inherit[r] || ca->inherit[r]; + } +} + +/* Return TRUE iff two rangelist arrays are equal. + */ +static svn_boolean_t +rangelist_array_equal(const rl_array_t *ba, + const rl_array_t *ca) +{ + svn_revnum_t r; + + for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++) + { + if (ba->root[r] != ca->root[r] + || ba->inherit[r] != ca->inherit[r]) + { + return FALSE; + } + } + return TRUE; +} + +#define IS_VALID_FORWARD_RANGE(range) \ + (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end)) + +/* Check rangelist is sorted and contains forward ranges. */ +static svn_boolean_t +rangelist_is_sorted(const svn_rangelist_t *rangelist) +{ + int i; + + for (i = 0; i < rangelist->nelts; i++) + { + const svn_merge_range_t *thisrange + = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); + + if (!IS_VALID_FORWARD_RANGE(thisrange)) + return FALSE; + } + for (i = 1; i < rangelist->nelts; i++) + { + const svn_merge_range_t *lastrange + = APR_ARRAY_IDX(rangelist, i-1, svn_merge_range_t *); + const svn_merge_range_t *thisrange + = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); + + if (svn_sort_compare_ranges(&lastrange, &thisrange) > 0) + return FALSE; + } + return TRUE; +} + +/* Return a random number R, where 0 <= R < N. + */ +static int rand_less_than(int n, apr_uint32_t *seed) +{ + apr_uint32_t next = svn_test_rand(seed); + return ((apr_uint64_t)next * n) >> 32; +} + +/* Return a random integer in a triangular (centre-weighted) distribution in + * the inclusive interval [MIN, MAX]. */ +static int +rand_interval_triangular(int min, int max, apr_uint32_t *seed) +{ + int span = max - min + 1; + + return min + rand_less_than(span/2 + 1, seed) + + rand_less_than((span+1)/2, seed); +} + +/* Generate a rangelist with a random number of random ranges. + * Choose from 0 to NON_V_MAX_RANGES ranges, biased towards the middle. + */ +#define NON_V_MAX_RANGES 4 /* See "Random testing parameters and coverage" */ +static void +rangelist_random_non_validated(svn_rangelist_t **rl, + apr_uint32_t *seed, + apr_pool_t *pool) +{ + svn_rangelist_t *r = apr_array_make(pool, NON_V_MAX_RANGES, + sizeof(svn_merge_range_t *)); + int n_ranges = rand_interval_triangular(0, NON_V_MAX_RANGES, seed); + int i; + + for (i = 0; i < n_ranges; i++) + { + svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange)); + + mrange->start = rand_less_than(RANGELIST_TESTS_MAX_REV + 1, seed); + mrange->end = rand_less_than(RANGELIST_TESTS_MAX_REV + 1, seed); + mrange->inheritable = rand_less_than(2, seed); + APR_ARRAY_PUSH(r, svn_merge_range_t *) = mrange; + } + *rl = r; +} + +/* Compare two integers pointed to by A_P and B_P, for use with qsort(). */ +static int +int_compare(const void *a_p, const void *b_p) +{ + const int *a = a_p, *b = b_p; + + return (*a < *b) ? -1 : (*a > *b) ? 1 : 0; +} + +/* Fill an ARRAY[ARRAY_LENGTH] with values each in the inclusive range + * [0, MAX]. The values are in ascending order, possibly with the same + * value repeated any number of times. + */ +static void +ascending_values(int *array, + int array_length, + int max, + apr_uint32_t *seed) +{ + int i; + + for (i = 0; i < array_length; i++) + array[i] = rand_less_than(max + 1, seed); + /* Sort them. (Some values will be repeated.) */ + qsort(array, array_length, sizeof(*array), int_compare); +} + +/* Generate a random rangelist that is not necessarily canonical + * but is at least sorted according to svn_sort_compare_ranges() + * and on which svn_rangelist__canonicalize() would succeed. + * Choose from 0 to SEMI_C_MAX_RANGES ranges, biased towards the middle. + */ +#define SEMI_C_MAX_RANGES 8 +static void +rangelist_random_semi_canonical(svn_rangelist_t **rl, + apr_uint32_t *seed, + apr_pool_t *pool) +{ + svn_rangelist_t *r = apr_array_make(pool, 4, sizeof(svn_merge_range_t *)); + int n_ranges = rand_interval_triangular(0, SEMI_C_MAX_RANGES, seed); + int start_and_end_revs[SEMI_C_MAX_RANGES * 2]; + int i; + + /* Choose start and end revs of the ranges. To end up with ranges evenly + * distributed up to RANGELIST_TESTS_MAX_REV, we start with them evenly + * distributed up to RANGELIST_TESTS_MAX_REV - N_RANGES, before spreading. */ + ascending_values(start_and_end_revs, n_ranges * 2, + RANGELIST_TESTS_MAX_REV - n_ranges, + seed); + /* Some values will be repeated. Within one range, that is not allowed, + * so add 1 to the length of each range, spreading the ranges out so they + * still don't overlap: + * [(6,6), (6,8)] becomes [(6,7), (7, 10)] */ + for (i = 0; i < n_ranges; i++) + { + start_and_end_revs[i*2] += i; + start_and_end_revs[i*2 + 1] += i+1; + } + + for (i = 0; i < n_ranges; i++) + { + svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange)); + + mrange->start = start_and_end_revs[i * 2]; + mrange->end = start_and_end_revs[i * 2 + 1]; + mrange->inheritable = rand_less_than(2, seed); + APR_ARRAY_PUSH(r, svn_merge_range_t *) = mrange; + } + *rl = r; + + /* check postconditions */ + { + svn_rangelist_t *dup; + svn_error_t *err; + + SVN_ERR_ASSERT_NO_RETURN(rangelist_is_sorted(*rl)); + dup = svn_rangelist_dup(*rl, pool); + err = svn_rangelist__canonicalize(dup, pool); + SVN_ERR_ASSERT_NO_RETURN(!err); + } +} + +/* Generate a random rangelist that satisfies svn_rangelist__is_canonical(). + */ +static void +rangelist_random_canonical(svn_rangelist_t **rl, + apr_uint32_t *seed, + apr_pool_t *pool) +{ + svn_rangelist_t *r; + int i; + + rangelist_random_semi_canonical(&r, seed, pool); + for (i = 1; i < r->nelts; i++) + { + svn_merge_range_t *prev_mrange = APR_ARRAY_IDX(r, i-1, svn_merge_range_t *); + svn_merge_range_t *mrange = APR_ARRAY_IDX(r, i, svn_merge_range_t *); + + /* to be canonical: adjacent ranges need differing inheritability */ + if (mrange->start == prev_mrange->end) + { + mrange->inheritable = !prev_mrange->inheritable; + } + } + *rl = r; + + /* check postconditions */ + SVN_ERR_ASSERT_NO_RETURN(svn_rangelist__is_canonical(*rl)); +} + +static const char * +rangelist_to_string(const svn_rangelist_t *rl, + apr_pool_t *pool) +{ + svn_error_t *err; + svn_string_t *ss; + + err = svn_rangelist_to_string(&ss, rl, pool); + if (err) + { + const char *s + = apr_psprintf(pool, "<rangelist[%d ranges]: %s>", + rl->nelts, svn_error_purge_tracing(err)->message); + svn_error_clear(err); + return s; + } + return ss->data; +} + +/* Try svn_rangelist_merge2(rlx, rly) and check errors and result */ +static svn_error_t * +rangelist_merge_random_inputs(svn_rangelist_t *rlx, + svn_rangelist_t *rly, + apr_pool_t *pool) +{ + rl_array_t ax, ay, a_expected, a_actual; + svn_rangelist_t *rlm; + + rangelist_to_array(&ax, rlx); + rangelist_to_array(&ay, rly); + + rlm = svn_rangelist_dup(rlx, pool); + /*printf("testcase: %s / %s\n", rangelist_to_string(rlx, pool), rangelist_to_string(rly, pool));*/ + SVN_ERR(svn_rangelist_merge2(rlm, rly, pool, pool)); + + if (!svn_rangelist__is_canonical(rlm)) + { + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "non-canonical result %s", + rangelist_to_string(rlm, pool)); + } + + /*SVN_TEST_ASSERT(rangelist_equal(rlm, ...));*/ + rangelist_array_union(&a_expected, &ax, &ay); + rangelist_to_array(&a_actual, rlm); + if (!rangelist_array_equal(&a_actual, &a_expected)) + { + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "wrong result: (c? %d / %d) -> %s", + svn_rangelist__is_canonical(rlx), + svn_rangelist__is_canonical(rly), + rangelist_to_string(rlm, pool)); + } + + return SVN_NO_ERROR; +} + +/* Insert a failure mode (ERR_CHAIN) into RESPONSES, keyed by a message + * representing its failure mode. The failure mode message is the lowest + * level error message in ERR_CHAIN, with some case-specific details + * removed to aid de-duplication. If it is new failure mode (not already in + * RESPONSES), store the error and return the message (hash key), else + * clear the error and return NULL. + */ +static const char * +add_failure_mode(svn_error_t *err_chain, + apr_hash_t *failure_modes) +{ + svn_error_t *err = err_chain; + char buf[100]; + const char *message; + + if (!err_chain) + return NULL; + + while (err->child) + err = err->child; + message = svn_err_best_message(err, buf, sizeof(buf)); + + /* For deduplication, ignore case-specific data in certain messages. */ + if (strstr(message, "Unable to parse overlapping revision ranges '")) + message = "Unable to parse overlapping revision ranges '..."; + if (strstr(message, "wrong result: (c?")) + message = "wrong result: (c?..."; + if (strstr(message, "svn_sort__array_insert2: Attempted insert at index ")) + message = "svn_sort__array_insert2: Attempted insert at index ..."; + + if (!svn_hash_gets(failure_modes, message)) + { + svn_hash_sets(failure_modes, message, err); + return message; + } + else + { + svn_error_clear(err_chain); + return NULL; + } +} + +static void +clear_failure_mode_errors(apr_hash_t *failure_modes, apr_pool_t *scratch_pool) +{ + apr_hash_index_t *hi; + + for (hi = apr_hash_first(scratch_pool, failure_modes); + hi; + hi = apr_hash_next(hi)) + { + svn_error_t *err = apr_hash_this_val(hi); + svn_error_clear(err); + } +} + +static svn_error_t * +test_rangelist_merge_random_canonical_inputs(apr_pool_t *pool) +{ + static apr_uint32_t seed = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + apr_hash_t *failure_modes = apr_hash_make(pool); + svn_boolean_t pass = TRUE; + int ix, iy; + + /* "300": See comment "Random testing parameters and coverage" */ + for (ix = 0; ix < 300; ix++) + { + svn_rangelist_t *rlx; + + rangelist_random_canonical(&rlx, &seed, pool); + + for (iy = 0; iy < 300; iy++) + { + svn_rangelist_t *rly; + svn_error_t *err; + const char *failure_mode; + + svn_pool_clear(iterpool); + + rangelist_random_canonical(&rly, &seed, iterpool); + + err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, iterpool)); + failure_mode = add_failure_mode(err, failure_modes); + if (failure_mode) + { + printf("first example of a failure mode: %s / %s\n" + " %s\n", + rangelist_to_string(rlx, iterpool), + rangelist_to_string(rly, iterpool), + failure_mode); + /*svn_handle_error(err, stdout, FALSE);*/ + pass = FALSE; + } + } + } + + clear_failure_mode_errors(failure_modes, pool); + + if (!pass) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Test failed: %d failure modes", + apr_hash_count(failure_modes)); + return SVN_NO_ERROR; +} + +/* Test svn_rangelist_merge2() with inputs that confirm to its doc-string. + * Fail if any errors are produced. + */ +static svn_error_t * +test_rangelist_merge_random_semi_c_inputs(apr_pool_t *pool) +{ + static apr_uint32_t seed = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + apr_hash_t *failure_modes = apr_hash_make(pool); + svn_boolean_t pass = TRUE; + int ix, iy; + + /* "300": See comment "Random testing parameters and coverage" */ + for (ix = 0; ix < 300; ix++) + { + svn_rangelist_t *rlx; + + rangelist_random_semi_canonical(&rlx, &seed, pool); + + for (iy = 0; iy < 300; iy++) + { + svn_rangelist_t *rly; + svn_error_t *err; + const char *failure_mode; + + svn_pool_clear(iterpool); + + rangelist_random_semi_canonical(&rly, &seed, iterpool); + + err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, iterpool)); + failure_mode = add_failure_mode(err, failure_modes); + if (failure_mode) + { + printf("first example of a failure mode: %s / %s\n" + " %s\n", + rangelist_to_string(rlx, iterpool), + rangelist_to_string(rly, iterpool), + failure_mode); + /*svn_handle_error(err, stdout, FALSE);*/ + pass = FALSE; + } + } + } + + clear_failure_mode_errors(failure_modes, pool); + + if (!pass) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Test failed: %d failure modes", + apr_hash_count(failure_modes)); + return SVN_NO_ERROR; +} + +/* Test svn_rangelist_merge2() with random non-validated inputs. + * + * Unlike the tests with valid inputs, this test expects many assertion + * failures. We don't care about those. All we care about is that it does + * not crash. */ +static svn_error_t * +test_rangelist_merge_random_non_validated_inputs(apr_pool_t *pool) +{ + static apr_uint32_t seed = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + apr_hash_t *failure_modes = apr_hash_make(pool); + int ix, iy; + + /* "300": See comment "Random testing parameters and coverage" */ + for (ix = 0; ix < 300; ix++) + { + svn_rangelist_t *rlx; + + rangelist_random_non_validated(&rlx, &seed, pool); + + for (iy = 0; iy < 300; iy++) + { + svn_rangelist_t *rly; + svn_error_t *err; + + svn_pool_clear(iterpool); + + rangelist_random_non_validated(&rly, &seed, iterpool); + + err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, iterpool)); + add_failure_mode(err, failure_modes); + } + } + + clear_failure_mode_errors(failure_modes, pool); + + return SVN_NO_ERROR; +} + +/* Generate random mergeinfo, in which the paths and rangelists are not + * necessarily valid. */ +static svn_error_t * +mergeinfo_random_non_validated(svn_mergeinfo_t *mp, + apr_uint32_t *seed, + apr_pool_t *pool) +{ + svn_mergeinfo_t m = apr_hash_make(pool); + int n_paths = 3; /* See comment "Random testing parameters and coverage" */ + int i; + + for (i = 0; i < n_paths; i++) + { + const char *path; + svn_rangelist_t *rl; + + /* A manually chosen distribution of valid and invalid paths: + See comment "Random testing parameters and coverage" */ + switch (rand_less_than(8, seed)) + { + case 0: case 1: case 2: case 3: + path = apr_psprintf(pool, "/path%d", i); break; + case 4: + path = apr_psprintf(pool, "path%d", i); break; + case 5: + path = apr_psprintf(pool, "//path%d", i); break; + case 6: + path = "/"; break; + case 7: + path = ""; break; + } + rangelist_random_non_validated(&rl, seed, pool); + svn_hash_sets(m, path, rl); + } + *mp = m; + return SVN_NO_ERROR; +} + +#if 0 +static const char * +mergeinfo_to_string_debug(svn_mergeinfo_t m, + apr_pool_t *pool) +{ + svn_string_t *s; + svn_error_t *err; + + err = svn_mergeinfo_to_string(&s, m, pool); + if (err) + { + const char *s2 = err->message; + svn_error_clear(err); + return s2; + } + return s->data; +} +#endif + +/* Try a mergeinfo merge. This does not check the result. */ +static svn_error_t * +mergeinfo_merge_random_inputs(const svn_mergeinfo_t mx, + const svn_mergeinfo_t my, + apr_pool_t *pool) +{ + svn_mergeinfo_t mm = svn_mergeinfo_dup(mx, pool); + + SVN_ERR(svn_mergeinfo_merge2(mm, my, pool, pool)); + return SVN_NO_ERROR; +} + +/* Test svn_mergeinfo_merge2() with random non-validated inputs. + * + * Unlike the tests with valid inputs, this test expects many assertion + * failures. We don't care about those. All we care about is that it does + * not crash. */ +static svn_error_t * +test_mergeinfo_merge_random_non_validated_inputs(apr_pool_t *pool) +{ + static apr_uint32_t seed = 0; + apr_pool_t *iterpool = svn_pool_create(pool); + int ix, iy; + + for (ix = 0; ix < 300; ix++) + { + svn_mergeinfo_t mx; + + SVN_ERR(mergeinfo_random_non_validated(&mx, &seed, pool)); + + for (iy = 0; iy < 300; iy++) + { + svn_mergeinfo_t my; + svn_error_t *err; + + svn_pool_clear(iterpool); + + SVN_ERR(mergeinfo_random_non_validated(&my, &seed, iterpool)); + + err = mergeinfo_merge_random_inputs(mx, my, iterpool); + if (err) + { + /* + printf("testcase FAIL: %s / %s\n", + mergeinfo_to_string_debug(mx, iterpool), + mergeinfo_to_string_debug(my, iterpool)); + svn_handle_error(err, stdout, FALSE); + */ + svn_error_clear(err); + } + } + } + + return SVN_NO_ERROR; +} /* The test table. */ @@ -1831,6 +2528,18 @@ static struct svn_test_descriptor_t test "merge of rangelists with overlaps (issue 4686)"), SVN_TEST_PASS2(test_rangelist_loop, "test rangelist edgecases via loop"), + SVN_TEST_PASS2(test_rangelist_merge_canonical_result, + "test rangelist merge canonical result (#4840)"), + SVN_TEST_PASS2(test_rangelist_merge_array_insert_failure, + "test rangelist merge array insert failure (#4840)"), + SVN_TEST_PASS2(test_rangelist_merge_random_canonical_inputs, + "test rangelist merge random canonical inputs"), + SVN_TEST_PASS2(test_rangelist_merge_random_semi_c_inputs, + "test rangelist merge random semi-c inputs"), + SVN_TEST_PASS2(test_rangelist_merge_random_non_validated_inputs, + "test rangelist merge random non-validated inputs"), + SVN_TEST_PASS2(test_mergeinfo_merge_random_non_validated_inputs, + "test mergeinfo merge random non-validated inputs"), SVN_TEST_NULL }; Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/stream-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/stream-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/stream-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/stream-test.c Fri Jan 14 14:01:45 2022 @@ -1000,6 +1000,71 @@ test_stream_readline_file_crlf(apr_pool_ return SVN_NO_ERROR; } +static svn_error_t * +test_stream_readline_file_nul(apr_pool_t *pool) +{ + /* Test is written based on the problem report in + https://lists.apache.org/thread.html/c96eb5618ac0bf6e083345e0fdcdcf834e30913f26eabe6ada7bab62@%3Cusers.subversion.apache.org%3E + where the user had an OOM when calling `svndumpfilter` with + a (most likely) invalid dump containing nul bytes. + */ + const char data[] = + { + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', + 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n' + }; + const char *tmp_dir; + const char *tmp_file; + svn_stream_t *stream; + + SVN_ERR(svn_dirent_get_absolute(&tmp_dir, "test_stream_readline_file_nul", pool)); + SVN_ERR(svn_io_remove_dir2(tmp_dir, TRUE, NULL, NULL, pool)); + SVN_ERR(svn_io_make_dir_recursively(tmp_dir, pool)); + svn_test_add_dir_cleanup(tmp_dir); + + tmp_file = svn_dirent_join(tmp_dir, "file", pool); + SVN_ERR(svn_io_file_create_bytes(tmp_file, data, sizeof(data), pool)); + SVN_ERR(svn_stream_open_readonly(&stream, tmp_file, pool, pool)); + + while (1) + { + svn_boolean_t eof; + svn_stringbuf_t *line; + + SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, pool)); + if (eof) + break; + + /* Don't check the contents of the `line` here, at least for now. + + In other words, we just test that this case does not crash or cause + unexpected errors. The reason is that currently our `readline_fn` + implementations have inconsistent behavior when dealing with \0 bytes, + handling those differently, either in strchr() or memchr() styles. + + Once we make them consistent (or even decide to bail out with an error + for \0), this part of the test should start properly checking `data`; + maybe also for non-file streams. + */ + } + + SVN_ERR(svn_stream_close(stream)); + + return SVN_NO_ERROR; +} + /* The test table. */ static int max_threads = 1; @@ -1037,6 +1102,8 @@ static struct svn_test_descriptor_t test "test reading LF-terminated lines from file"), SVN_TEST_PASS2(test_stream_readline_file_crlf, "test reading CRLF-terminated lines from file"), + SVN_TEST_PASS2(test_stream_readline_file_nul, + "test reading line from file with nul bytes"), SVN_TEST_NULL }; Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/conflict-data-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/conflict-data-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/conflict-data-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/conflict-data-test.c Fri Jan 14 14:01:45 2022 @@ -911,7 +911,7 @@ test_prop_conflict_resolving(const svn_t SVN_TEST_STRING_ASSERT(value, "r1"); value = svn_prop_get_value(props, "prop-3"); SVN_TEST_STRING_ASSERT(value, "mod"); - + return SVN_NO_ERROR; } Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/db-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/db-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/db-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/db-test.c Fri Jan 14 14:01:45 2022 @@ -635,7 +635,7 @@ test_inserting_nodes(apr_pool_t *pool) 1, TIME_1a, AUTHOR_1, checksum, NULL, FALSE, FALSE, NULL, NULL, FALSE, FALSE, - NULL, NULL, + NULL, NULL, FALSE, NULL, pool)); /* Create a new symlink node. */ Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/op-depth-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/op-depth-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/op-depth-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/op-depth-test.c Fri Jan 14 14:01:45 2022 @@ -1770,7 +1770,7 @@ test_db_make_copy(const svn_test_opts_t }; SVN_ERR(insert_dirs(&b, before)); - SVN_ERR(svn_wc__db_op_make_copy(b.wc_ctx->db, sbox_wc_path(&b, "A"), + SVN_ERR(svn_wc__db_op_make_copy(b.wc_ctx->db, sbox_wc_path(&b, "A"), NULL, NULL, pool)); SVN_ERR(check_db_rows(&b, "", after)); Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-queries-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-queries-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-queries-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-queries-test.c Fri Jan 14 14:01:45 2022 @@ -100,6 +100,7 @@ static const int slow_statements[] = STMT_SELECT_UPDATE_MOVE_LIST, STMT_FIND_REPOS_PATH_IN_WC, STMT_SELECT_PRESENT_HIGHEST_WORKING_NODES_BY_BASENAME_AND_KIND, + STMT_SELECT_COPIES_OF_REPOS_RELPATH, /* Designed as slow to avoid penalty on other queries */ STMT_SELECT_UNREFERENCED_PRISTINES, @@ -266,6 +267,14 @@ struct explanation_item const char *compound_right; svn_boolean_t create_btree; + svn_boolean_t create_union; + svn_boolean_t union_all; + + svn_boolean_t merge; + + svn_boolean_t multi_index; + svn_boolean_t multi_index_or; + int expression_vars; int expected_rows; }; @@ -312,6 +321,18 @@ parse_explanation_item(struct explanatio item->table = apr_psprintf(result_pool, "SUBQUERY-%s", apr_strtok(NULL, " ", &last)); } + else if (MATCH_TOKEN(token, "CONSTANT")) + { + item->table = "sqlite_master"; /* not worth checking. + Just a lookup */ + token = apr_strtok(NULL, " ", &last); + if (!MATCH_TOKEN(token, "ROW")) + { + printf("DBG: Expected 'ROW', got '%s' in '%s'\n", + token, text); + return SVN_NO_ERROR; + } + } else { printf("DBG: Expected 'TABLE', got '%s' in '%s'\n", token, text); @@ -433,39 +454,44 @@ parse_explanation_item(struct explanatio /* Handling temporary table (E.g. UNION) */ token = apr_strtok(NULL, " ", &last); - if (!MATCH_TOKEN(token, "SUBQUERIES")) - { - printf("DBG: Expected 'SUBQUERIES', got '%s' in '%s'\n", token, - text); - return SVN_NO_ERROR; - } - - item->compound_left = apr_strtok(NULL, " ", &last); - token = apr_strtok(NULL, " ", &last); - - if (!MATCH_TOKEN(token, "AND")) - { - printf("DBG: Expected 'AND', got '%s' in '%s'\n", token, text); - return SVN_NO_ERROR; - } - - item->compound_right = apr_strtok(NULL, " ", &last); - - token = apr_strtok(NULL, " ", &last); - if (MATCH_TOKEN(token, "USING")) + if (MATCH_TOKEN(token, "SUBQUERIES")) { + item->compound_left = apr_strtok(NULL, " ", &last); token = apr_strtok(NULL, " ", &last); - if (!MATCH_TOKEN(token, "TEMP")) + + if (!MATCH_TOKEN(token, "AND")) { - printf("DBG: Expected 'TEMP', got '%s' in '%s'\n", token, text); + printf("DBG: Expected 'AND', got '%s' in '%s'\n", token, text); + return SVN_NO_ERROR; } + + item->compound_right = apr_strtok(NULL, " ", &last); + token = apr_strtok(NULL, " ", &last); - if (!MATCH_TOKEN(token, "B-TREE")) + if (MATCH_TOKEN(token, "USING")) { - printf("DBG: Expected 'B-TREE', got '%s' in '%s'\n", token, - text); + token = apr_strtok(NULL, " ", &last); + if (!MATCH_TOKEN(token, "TEMP")) + { + printf("DBG: Expected 'TEMP', got '%s' in '%s'\n", token, text); + } + token = apr_strtok(NULL, " ", &last); + if (!MATCH_TOKEN(token, "B-TREE")) + { + printf("DBG: Expected 'B-TREE', got '%s' in '%s'\n", token, + text); + } + item->create_btree = TRUE; } - item->create_btree = TRUE; + } + else if (MATCH_TOKEN(token, "QUERY")) + { + } + else + { + printf("DBG: Expected 'SUBQUERIES', got '%s' in '%s'\n", token, + text); + return SVN_NO_ERROR; } } else if (MATCH_TOKEN(item->operation, "USE")) @@ -474,6 +500,48 @@ parse_explanation_item(struct explanatio /* ### Need parsing */ item->create_btree = TRUE; } + else if (MATCH_TOKEN(item->operation, "UNION")) + { + item->create_union = TRUE; + + token = apr_strtok(NULL, " ", &last); + if (MATCH_TOKEN(token, "ALL")) + item->union_all = TRUE; + else + item->union_all = FALSE; + } + else if (MATCH_TOKEN(item->operation, "INDEX")) + { + + } + else if (MATCH_TOKEN(item->operation, "MULTI-INDEX")) + { + item->multi_index = TRUE; + token = apr_strtok(NULL, " ", &last); + if (MATCH_TOKEN(token, "OR")) + item->multi_index_or = TRUE; + } + else if (MATCH_TOKEN(item->operation, "MERGE")) + { + item->merge = TRUE; + } + else if (MATCH_TOKEN(item->operation, "LEFT") + || MATCH_TOKEN(item->operation, "RIGHT")) + { + } + else if (MATCH_TOKEN(item->operation, "CORRELATED")) + { + item->merge = TRUE; + } + else if (MATCH_TOKEN(item->operation, "CO-ROUTINE")) + { + } + else if (MATCH_TOKEN(item->operation, "SCALAR")) + { + } + else if (MATCH_TOKEN(item->operation, "LEFT-MOST")) + { + } else { printf("DBG: Unhandled sqlite operation '%s' in explanation\n", item->operation); @@ -670,7 +738,20 @@ test_query_expectations(apr_pool_t *scra || (item->expression_vars < 1)) && !is_result_table(item->table)) { - if (in_list(primary_key_statements, i)) + if (MATCH_TOKEN(item->table, "sqlite_master")) + { + /* The sqlite_master table does not have an index. + Query explanations that say 'SCAN TABLE sqlite_master' + will appear if SQLite was compiled with the option + SQLITE_ENABLE_STMT_SCANSTATUS, for queries such + as 'DROP TABLE foo', but the performance of such + statements is not our concern here. */ + + /* "Slow" statements do expect to see a warning, however. */ + if (is_slow_statement(i)) + warned = TRUE; + } + else if (in_list(primary_key_statements, i)) { /* Reported as primary key index usage in Sqlite 3.7, as table scan in 3.8+, while the execution plan is Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-test.c Fri Jan 14 14:01:45 2022 @@ -35,6 +35,7 @@ #include "svn_wc.h" #include "svn_client.h" #include "svn_hash.h" +#include "svn_props.h" #include "utils.h" @@ -493,6 +494,283 @@ test_internal_file_modified(const svn_te return SVN_NO_ERROR; } +static svn_error_t * +test_working_file_writer_simple(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + const char *tmp_dir; + svn_wc__working_file_writer_t *writer; + svn_stream_t *stream; + const char *final_abspath; + svn_stringbuf_t *actual_content; + + SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, + "working_file_writer_simple", + pool)); + + SVN_ERR(svn_wc__working_file_writer_open(&writer, tmp_dir, -1, + svn_subst_eol_style_none, NULL, + FALSE, NULL, FALSE, FALSE, + FALSE, + pool, pool)); + + stream = svn_wc__working_file_writer_get_stream(writer); + SVN_ERR(svn_stream_puts(stream, "content")); + SVN_ERR(svn_stream_close(stream)); + + SVN_ERR(svn_wc__working_file_writer_finalize(NULL, NULL, writer, pool)); + final_abspath = svn_dirent_join(tmp_dir, "file", pool); + SVN_ERR(svn_wc__working_file_writer_install(writer, final_abspath, pool)); + + SVN_ERR(svn_stringbuf_from_file2(&actual_content, + final_abspath, + pool)); + + SVN_TEST_STRING_ASSERT(actual_content->data, "content"); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_working_file_writer_eol_repair(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + const char *tmp_dir; + svn_wc__working_file_writer_t *writer; + svn_stream_t *stream; + const char *final_abspath; + svn_stringbuf_t *actual_content; + + SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, + "working_file_writer_eol_repair", + pool)); + + SVN_ERR(svn_wc__working_file_writer_open(&writer, tmp_dir, -1, + svn_subst_eol_style_fixed, "\r\n", + TRUE /* repair_eol */, + NULL, FALSE, FALSE, + FALSE, + pool, pool)); + + stream = svn_wc__working_file_writer_get_stream(writer); + SVN_ERR(svn_stream_puts(stream, "content\n\r\n")); + SVN_ERR(svn_stream_close(stream)); + + SVN_ERR(svn_wc__working_file_writer_finalize(NULL, NULL, writer, pool)); + final_abspath = svn_dirent_join(tmp_dir, "file", pool); + SVN_ERR(svn_wc__working_file_writer_install(writer, final_abspath, pool)); + + SVN_ERR(svn_stringbuf_from_file2(&actual_content, + final_abspath, + pool)); + + SVN_TEST_STRING_ASSERT(actual_content->data, "content\r\n\r\n"); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_working_file_writer_eol_inconsistent(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + const char *tmp_dir; + svn_wc__working_file_writer_t *writer; + svn_stream_t *stream; + apr_hash_t *dirents; + svn_error_t *err; + + SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, + "working_file_writer_eol_inconsistent", + pool)); + + SVN_ERR(svn_wc__working_file_writer_open(&writer, tmp_dir, -1, + svn_subst_eol_style_fixed, "\r\n", + FALSE /* repair_eol */, + NULL, FALSE, FALSE, + FALSE, + pool, pool)); + + /* With REPAIR_EOL disabled, expect to see an error when the line ending + inconsistency is detected by the stream. */ + stream = svn_wc__working_file_writer_get_stream(writer); + err = svn_stream_puts(stream, "content\n\r\n"); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_IO_INCONSISTENT_EOL); + + SVN_ERR(svn_wc__working_file_writer_close(writer)); + + SVN_ERR(svn_io_get_dirents3(&dirents, tmp_dir, TRUE, pool, pool)); + SVN_TEST_INT_ASSERT(apr_hash_count(dirents), 0); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_internal_file_modified_keywords(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t b; + svn_boolean_t modified; + const char *iota_path; + + SVN_ERR(svn_test__sandbox_create(&b, "internal_file_modified_keywords", + opts, pool)); + SVN_ERR(sbox_add_and_commit_greek_tree(&b)); + + iota_path = sbox_wc_path(&b, "iota"); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(!modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(!modified); + + /* Set svn:keywords, edit the file. The file is modified. */ + SVN_ERR(sbox_wc_propset(&b, SVN_PROP_KEYWORDS, "Revision", iota_path)); + SVN_ERR(sbox_file_write(&b, iota_path, "$Revision$\n")); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(modified); + + /* Commit the changes. The file is not modified. */ + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(!modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(!modified); + + /* Manually contract the keywords. The file is modified, but only + if we ask for the exact comparison. */ + SVN_ERR(sbox_file_write(&b, iota_path, "$Revision$\n")); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(!modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(modified); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_internal_file_modified_eol_style(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t b; + svn_boolean_t modified; + const char *iota_path; + + SVN_ERR(svn_test__sandbox_create(&b, "internal_file_modified_eol_style", + opts, pool)); + SVN_ERR(sbox_add_and_commit_greek_tree(&b)); + + iota_path = sbox_wc_path(&b, "iota"); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(!modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(!modified); + + /* Set svn:eol-style, edit the file. The file is modified. */ + SVN_ERR(sbox_wc_propset(&b, SVN_PROP_EOL_STYLE, "CRLF", iota_path)); + SVN_ERR(sbox_file_write(&b, iota_path, "contents\r\n")); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(modified); + + /* Commit the changes. The file is not modified. */ + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(!modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(!modified); + + /* Manually change the line ending to LF. The file is modified, but only + if we ask for the exact comparison. */ + SVN_ERR(sbox_file_write(&b, iota_path, "contents\n")); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(!modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(modified); + + /* Manually change the line ending to CR. The file is modified, but only + if we ask for the exact comparison. */ + SVN_ERR(sbox_file_write(&b, iota_path, "contents\r")); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(!modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(modified); + + /* Change the line ending back to CRLF. The file is not modified. */ + SVN_ERR(sbox_file_write(&b, iota_path, "contents\r\n")); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(!modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(!modified); + + /* Add an empty line and commit. The file is not modified. */ + SVN_ERR(sbox_file_write(&b, iota_path, "contents\r\n\r\n")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(!modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(!modified); + + /* Change one of the line endings to LF. The file is modified, but only + if we ask for the exact comparison. */ + SVN_ERR(sbox_file_write(&b, iota_path, "contents\n\r\n")); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, FALSE, pool)); + SVN_TEST_ASSERT(!modified); + + SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, + iota_path, TRUE, pool)); + SVN_TEST_ASSERT(modified); + + return SVN_NO_ERROR; +} + /* ---------------------------------------------------------------------- */ /* The list of test functions */ @@ -515,6 +793,16 @@ static struct svn_test_descriptor_t test "test legacy commit2"), SVN_TEST_OPTS_PASS(test_internal_file_modified, "test internal_file_modified"), + SVN_TEST_OPTS_PASS(test_working_file_writer_simple, + "working file writer simple"), + SVN_TEST_OPTS_PASS(test_working_file_writer_eol_repair, + "working file writer eol repair"), + SVN_TEST_OPTS_PASS(test_working_file_writer_eol_inconsistent, + "working file writer eol inconsistent"), + SVN_TEST_OPTS_PASS(test_internal_file_modified_keywords, + "test internal_file_modified with keywords"), + SVN_TEST_OPTS_PASS(test_internal_file_modified_eol_style, + "test internal_file_modified with eol-style"), SVN_TEST_NULL }; Modified: subversion/branches/multi-wc-format/subversion/tests/svn_test.h URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/svn_test.h?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/svn_test.h (original) +++ subversion/branches/multi-wc-format/subversion/tests/svn_test.h Fri Jan 14 14:01:45 2022 @@ -37,6 +37,7 @@ #include "svn_error.h" #include "svn_string.h" #include "svn_auth.h" +#include "svn_time.h" #ifdef __cplusplus extern "C" { @@ -123,8 +124,17 @@ extern "C" { \ if (tst_str2 == NULL && tst_str1 == NULL) \ break; \ - if ((tst_str1 == NULL) || (tst_str2 == NULL) \ - || (strcmp(tst_str2, tst_str1) != 0)) \ + if (tst_str1 == NULL) \ + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, \ + "Strings not equal\n Expected: '%s'\n Found: NULL" \ + "\n at %s:%d", \ + tst_str2, __FILE__, __LINE__); \ + if (tst_str2 == NULL) \ + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, \ + "Strings not equal\n Expected: NULL\n Found: '%s'" \ + "\n at %s:%d", \ + tst_str1, __FILE__, __LINE__); \ + if (strcmp(tst_str2, tst_str1) != 0) \ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, \ "Strings not equal\n Expected: '%s'\n Found: '%s'" \ "\n at %s:%d", \ @@ -147,6 +157,40 @@ extern "C" { tst_int2, tst_int1, __FILE__, __LINE__); \ } while(0) +/** Handy macro for testing apr_time_t equality. + * + * WITHIN_EXPR specifies the proximity of the comparison. + */ +#define SVN_TEST_TIME_ASSERT(expr, expected_expr, within_expr) \ + do { \ + apr_time_t actual_time = (expr); \ + apr_time_t expected_time = (expected_expr); \ + apr_interval_time_t within_time = (within_expr); \ + \ + if (actual_time < expected_time - within_time || \ + actual_time > expected_time + within_time) \ + { \ + svn_error_t *err = \ + svn_error_create(SVN_ERR_TEST_FAILED, NULL, NULL); \ + \ + err->message = apr_psprintf( \ + err->pool, \ + "Time values not equal\n" \ + " Expected: %s (%" APR_TIME_T_FMT ")\n" \ + " Found: %s (%" APR_TIME_T_FMT ")\n" \ + " Proximity: %" APR_TIME_T_FMT "ms\n" \ + " at %s:%d", \ + svn_time_to_cstring(expected_time, err->pool), \ + expected_time, \ + svn_time_to_cstring(actual_time, err->pool), \ + actual_time, \ + apr_time_as_msec(within_time), \ + __FILE__, __LINE__); \ + \ + return err; \ + } \ + } while(0) + /* Baton for any arguments that need to be passed from main() to svn * test functions. Modified: subversion/branches/multi-wc-format/subversion/tests/svn_test_main.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/svn_test_main.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/svn_test_main.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/svn_test_main.c Fri Jan 14 14:01:45 2022 @@ -880,7 +880,7 @@ svn_test_main(int argc, const char *argv _set_error_mode(_OUT_TO_STDERR); /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr. - (Ignored in releas builds) */ + (Ignored in release builds) */ _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); @@ -1089,9 +1089,9 @@ svn_test_main(int argc, const char *argv svn_pool_clear(cleanup_pool); } } -#if APR_HAS_THREADS else { +#if APR_HAS_THREADS got_error = do_tests_concurrently(opts.prog_name, test_funcs, array_size, max_threads, &opts, test_pool); @@ -1099,8 +1099,11 @@ svn_test_main(int argc, const char *argv /* Execute all cleanups */ svn_pool_clear(test_pool); svn_pool_clear(cleanup_pool); - } +#else + /* Can't happen */ + SVN_ERR_MALFUNCTION(); #endif + } } /* Clean up APR */ Modified: subversion/branches/multi-wc-format/tools/backup/hot-backup.py.in URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/tools/backup/hot-backup.py.in?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/tools/backup/hot-backup.py.in (original) +++ subversion/branches/multi-wc-format/tools/backup/hot-backup.py.in Fri Jan 14 14:01:45 2022 @@ -36,6 +36,8 @@ ###################################################################### import sys, os, getopt, stat, re, time, shutil, subprocess +import locale +import functools ###################################################################### # Global Settings @@ -193,7 +195,7 @@ def comparator(a, b): if not inca: return -1 elif not incb: - return 1; + return 1 elif (int(inca) < int(incb)): return -1 else: @@ -203,10 +205,18 @@ def get_youngest_revision(): """Examine the repository REPO_DIR using the svnlook binary specified by SVNLOOK, and return the youngest revision.""" - p = subprocess.Popen([svnlook, 'youngest', '--', repo_dir], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + if b'' == '': + p = subprocess.Popen([svnlook, 'youngest', '--', repo_dir], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + else: + p = subprocess.Popen([svnlook, 'youngest', '--', repo_dir], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + errors='backslashreplace', # foolproof + encoding=locale.getpreferredencoding()) infile, outfile, errfile = p.stdin, p.stdout, p.stderr stdout_lines = outfile.readlines() @@ -255,7 +265,7 @@ regexp = re.compile("^" + re.escape(repo directory_list = os.listdir(backup_dir) young_list = [x for x in directory_list if regexp.search(x)] if young_list: - young_list.sort(comparator) + young_list.sort(key = functools.cmp_to_key(comparator)) increment = regexp.search(young_list.pop()).groupdict()['increment'] if increment: backup_subdir = os.path.join(backup_dir, repo + "-" + youngest + "-" @@ -348,7 +358,7 @@ if num_backups > 0: regexp = re.compile("^" + re.escape(repo) + "-[0-9]+(-[0-9]+)?" + ext_re + "$") directory_list = os.listdir(backup_dir) old_list = [x for x in directory_list if regexp.search(x)] - old_list.sort(comparator) + old_list.sort(key = functools.cmp_to_key(comparator)) del old_list[max(0,len(old_list)-num_backups):] for item in old_list: old_backup_item = os.path.join(backup_dir, item) Modified: subversion/branches/multi-wc-format/tools/buildbot/slaves/README URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/tools/buildbot/slaves/README?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/tools/buildbot/slaves/README (original) +++ subversion/branches/multi-wc-format/tools/buildbot/slaves/README Fri Jan 14 14:01:45 2022 @@ -63,7 +63,7 @@ branches/1.4.x. If the buildslave is not stored in the queue and started when the buildslave connects. Normally each build only includes the changes of one commit. However, when -multiple changes arive during a previous build, those will be combined in +multiple changes arrive during a previous build, those will be combined in a next build. The buildmaster looks at each commit and decides if they are important enough Modified: subversion/branches/multi-wc-format/tools/buildbot/slaves/bb-openbsd/svnbuild.sh URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/tools/buildbot/slaves/bb-openbsd/svnbuild.sh?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/tools/buildbot/slaves/bb-openbsd/svnbuild.sh (original) +++ subversion/branches/multi-wc-format/tools/buildbot/slaves/bb-openbsd/svnbuild.sh Fri Jan 14 14:01:45 2022 @@ -22,8 +22,10 @@ set -e set -x +(test -h ../GNUmakefile || ln -s ../unix-build/Makefile.svn ../GNUmakefile) +(cd .. && gmake dirs-create fetch) url="$(svn info --show-item url)" branch="${url##*/}" -(test -h ../GNUmakefile || ln -s ../unix-build/Makefile.svn ../GNUmakefile) +mkdir -p ../objdir/svn-${branch} touch ../objdir/svn-${branch}/.retrieved -(cd .. && gmake BRANCH="$branch" THREADING="no" JAVA="no" MAKE_JOBS=8) +(cd .. && gmake BRANCH="$branch" THREADING="no" JAVA="no" MAKE_JOBS=2)
