diff options
Diffstat (limited to 'subversion/tests/libsvn_repos/repos-test.c')
-rw-r--r-- | subversion/tests/libsvn_repos/repos-test.c | 843 |
1 files changed, 588 insertions, 255 deletions
diff --git a/subversion/tests/libsvn_repos/repos-test.c b/subversion/tests/libsvn_repos/repos-test.c index 87265f0..8208958 100644 --- a/subversion/tests/libsvn_repos/repos-test.c +++ b/subversion/tests/libsvn_repos/repos-test.c @@ -36,13 +36,15 @@ #include "svn_config.h" #include "svn_props.h" #include "svn_version.h" +#include "private/svn_repos_private.h" + +/* be able to look into svn_config_t */ +#include "../../libsvn_subr/config_impl.h" #include "../svn_test_fs.h" #include "dir-delta-editor.h" -#include "private/svn_repos_private.h" - /* Used to terminate lines in large multi-line string literals. */ #define NL APR_EOL_STR @@ -486,19 +488,19 @@ print_chrevs(const apr_array_header_t *revs_got, outstr = apr_pstrcat(pool, outstr, apr_psprintf(pool, "%ld ", rev), - (char *)NULL); + SVN_VA_NULL); } } - outstr = apr_pstrcat(pool, outstr, "} Expected: { ", (char *)NULL); + outstr = apr_pstrcat(pool, outstr, "} Expected: { ", SVN_VA_NULL); for (i = 0; i < num_revs_expected; i++) { outstr = apr_pstrcat(pool, outstr, apr_psprintf(pool, "%ld ", revs_expected[i]), - (char *)NULL); + SVN_VA_NULL); } - return apr_pstrcat(pool, outstr, "}", (char *)NULL); + return apr_pstrcat(pool, outstr, "}", SVN_VA_NULL); } @@ -1425,8 +1427,8 @@ in_repo_authz(const svn_test_opts_t *opts, * Create an authz file and put it in the repository. * Verify it can be read with an relative URL. * Verify it can be read with an absolute URL. - * Verify non-existant path does not error out when must_exist is FALSE. - * Verify non-existant path does error out when must_exist is TRUE. + * Verify non-existent path does not error out when must_exist is FALSE. + * Verify non-existent path does error out when must_exist is TRUE. * Verify that an http:// URL produces an error. * Verify that an svn:// URL produces an error. */ @@ -1456,18 +1458,18 @@ in_repo_authz(const svn_test_opts_t *opts, repos_root = svn_repos_path(repos, pool); SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_url, repos_root, pool)); - authz_url = apr_pstrcat(pool, repos_url, "/authz", (char *)NULL); - noent_authz_url = apr_pstrcat(pool, repos_url, "/A/authz", (char *)NULL); + authz_url = svn_path_url_add_component2(repos_url, "authz", pool); + noent_authz_url = svn_path_url_add_component2(repos_url, "A/authz", pool); /* 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)); - /* Non-existant path in the repo with must_exist set to FALSE */ + /* Non-existent path in the repo with must_exist set to FALSE */ SVN_ERR(svn_repos_authz_read2(&authz_cfg, noent_authz_url, NULL, FALSE, pool)); - /* Non-existant path in the repo with must_exist set to TRUE */ + /* Non-existent path in the repo with must_exist set to TRUE */ err = svn_repos_authz_read2(&authz_cfg, noent_authz_url, NULL, TRUE, pool); if (!err || err->apr_err != SVN_ERR_ILLEGAL_TARGET) return svn_error_createf(SVN_ERR_TEST_FAILED, err, @@ -1595,15 +1597,14 @@ in_repo_groups_authz(const svn_test_opts_t *opts, /* Calculate URLs */ repos_root = svn_repos_path(repos, pool); SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_url, repos_root, pool)); - authz_url = apr_pstrcat(pool, repos_url, "/authz", (char *)NULL); - empty_authz_url = apr_pstrcat(pool, repos_url, "/empty-authz", (char *)NULL); - noent_authz_url = apr_pstrcat(pool, repos_url, "/A/authz", (char *)NULL); - groups_url = apr_pstrcat(pool, repos_url, "/groups", (char *)NULL); - noent_groups_url = apr_pstrcat(pool, repos_url, "/A/groups", (char *)NULL); + authz_url = svn_path_url_add_component2(repos_url, "authz", pool); + empty_authz_url = svn_path_url_add_component2(repos_url, "empty-authz", pool); + noent_authz_url = svn_path_url_add_component2(repos_url, "A/authz", pool); + groups_url = svn_path_url_add_component2(repos_url, "groups", pool); + noent_groups_url = svn_path_url_add_component2(repos_url, "A/groups", pool); /* absolute file URLs. */ - groups_url = apr_pstrcat(pool, repos_url, "/groups", (char *)NULL); SVN_ERR(svn_repos_authz_read2(&authz_cfg, authz_url, groups_url, TRUE, pool)); SVN_ERR(authz_check_access(authz_cfg, test_set, pool)); @@ -1656,7 +1657,7 @@ in_repo_groups_authz(const svn_test_opts_t *opts, /* Helper for the groups_authz test. Set *AUTHZ_P to a representation of - AUTHZ_CONTENTS in conjuction with GROUPS_CONTENTS, using POOL for + AUTHZ_CONTENTS in conjunction with GROUPS_CONTENTS, using POOL for temporary allocation. If DISK is TRUE then write the contents to temporary files and use svn_repos_authz_read2() to get the data if FALSE write the data to a buffered stream and use svn_repos_authz_parse(). */ @@ -1771,7 +1772,7 @@ groups_authz(const svn_test_opts_t *opts, * 2. Verify that access rights written in the global groups file are * discarded and affect nothing in authorization terms. * 3. Verify that local groups in the authz file are prohibited in - * conjuction with global groups (and that a configuration error is + * conjunction with global groups (and that a configuration error is * reported in this scenario). * 4. Ensure that group cycles in the global groups file are reported. * @@ -1827,7 +1828,7 @@ groups_authz(const svn_test_opts_t *opts, SVN_ERR(authz_check_access(authz_cfg, test_set2, pool)); - /* Local groups cannot be used in conjuction with global groups. */ + /* Local groups cannot be used in conjunction with global groups. */ groups_contents = "[groups]" NL "slaves = maximus" NL @@ -2460,7 +2461,8 @@ node_location_segments(const svn_test_opts_t *opts, /* Bail (with success) on known-untestable scenarios */ if ((strcmp(opts->fs_type, "bdb") == 0) && (opts->server_minor_version == 4)) - return SVN_NO_ERROR; + return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, + "not supported for BDB in SVN 1.4"); /* Create the repository. */ SVN_ERR(svn_test__create_repos(&repos, "test-repo-node-location-segments", @@ -2886,7 +2888,7 @@ log_receiver(void *baton, svn_log_entry_t *log_entry, apr_pool_t *pool) { - int *count = baton; + svn_revnum_t *count = baton; (*count)++; return SVN_NO_ERROR; } @@ -2944,13 +2946,18 @@ get_logs(const svn_test_opts_t *opts, svn_revnum_t end_arg = end ? end : SVN_INVALID_REVNUM; svn_revnum_t eff_start = start ? start : youngest_rev; svn_revnum_t eff_end = end ? end : youngest_rev; - int limit, max_logs = + int limit; + svn_revnum_t max_logs = MAX(eff_start, eff_end) + 1 - MIN(eff_start, eff_end); - int num_logs; + svn_revnum_t num_logs; + /* this may look like it can get in an infinite loop if max_logs + * ended up being larger than the size limit can represent. It + * can't because a negative limit will end up failing to match + * the existed number of logs. */ for (limit = 0; limit <= max_logs; limit++) { - int num_expected = limit ? limit : max_logs; + svn_revnum_t num_expected = limit ? limit : max_logs; svn_pool_clear(subpool); num_logs = 0; @@ -2961,9 +2968,9 @@ get_logs(const svn_test_opts_t *opts, if (num_logs != num_expected) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "Log with start=%ld,end=%ld,limit=%d " - "returned %d entries (expected %d)", + "returned %ld entries (expected %ld)", start_arg, end_arg, limit, - num_logs, max_logs); + num_logs, num_expected); } } } @@ -3059,6 +3066,11 @@ test_get_file_revs(const svn_test_opts_t *opts, apr_hash_set(ht_reverse_results, &trunk_results[i].rev, sizeof(svn_revnum_t), &trunk_results[i]); + /* Check for feature support */ + if (opts->server_minor_version && (opts->server_minor_version < 5)) + return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, + "not supported in pre-1.5 SVN"); + /* Create the repository and verify blame results. */ SVN_ERR(svn_test__create_blame_repository(&repos, "test-repo-get-filerevs", opts, subpool)); @@ -3167,7 +3179,26 @@ test_delete_repos(const svn_test_opts_t *opts, return SVN_NO_ERROR; } -/* Related to issue 4340, "fs layer should reject filenames with trailing \n" */ +/* Prepare a commit for the filename_with_control_chars() tests */ +static svn_error_t * +fwcc_prepare(const svn_delta_editor_t **editor_p, + void **edit_baton_p, + void **root_baton, + svn_repos_t *repos, + apr_pool_t *scratch_pool) +{ + /* Checks for control characters are implemented in the commit editor, + * not in the FS API. */ + SVN_ERR(svn_repos_get_commit_editor4(editor_p, edit_baton_p, repos, + NULL, "file://test", "/", + "plato", "test commit", + dummy_commit_cb, NULL, NULL, NULL, + scratch_pool)); + SVN_ERR((*editor_p)->open_root(*edit_baton_p, 1, scratch_pool, root_baton)); + return SVN_NO_ERROR; +} + +/* Related to issue 4340, "filenames containing \n corrupt FSFS repositories" */ static svn_error_t * filename_with_control_chars(const svn_test_opts_t *opts, apr_pool_t *pool) @@ -3206,17 +3237,6 @@ filename_with_control_chars(const svn_test_opts_t *opts, SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); svn_pool_clear(subpool); - /* Checks for control characters are implemented in the commit editor, - * not in the FS API. */ - SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool)); - SVN_ERR(svn_repos_get_commit_editor4(&editor, &edit_baton, repos, - txn, "file://test", "/", - "plato", "test commit", - dummy_commit_cb, NULL, NULL, NULL, - pool)); - - SVN_ERR(editor->open_root(edit_baton, 1, pool, &root_baton)); - /* Attempt to copy /foo to a bad path P. This should fail. */ i = 0; do @@ -3225,8 +3245,13 @@ filename_with_control_chars(const svn_test_opts_t *opts, if (p == NULL) break; svn_pool_clear(subpool); + + SVN_ERR(fwcc_prepare(&editor, &edit_baton, &root_baton, repos, subpool)); err = editor->add_directory(p, root_baton, "/foo", 1, subpool, &out_baton); + if (!err) + err = editor->close_edit(edit_baton, subpool); + svn_error_clear(editor->abort_edit(edit_baton, subpool)); SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_PATH_SYNTAX); } while (p); @@ -3238,8 +3263,13 @@ filename_with_control_chars(const svn_test_opts_t *opts, if (p == NULL) break; svn_pool_clear(subpool); + + SVN_ERR(fwcc_prepare(&editor, &edit_baton, &root_baton, repos, subpool)); err = editor->add_file(p, root_baton, NULL, SVN_INVALID_REVNUM, subpool, &out_baton); + if (!err) + err = editor->close_edit(edit_baton, subpool); + svn_error_clear(editor->abort_edit(edit_baton, subpool)); SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_PATH_SYNTAX); } while (p); @@ -3252,281 +3282,576 @@ filename_with_control_chars(const svn_test_opts_t *opts, if (p == NULL) break; svn_pool_clear(subpool); + + SVN_ERR(fwcc_prepare(&editor, &edit_baton, &root_baton, repos, subpool)); err = editor->add_directory(p, root_baton, NULL, SVN_INVALID_REVNUM, subpool, &out_baton); + if (!err) + err = editor->close_edit(edit_baton, subpool); + svn_error_clear(editor->abort_edit(edit_baton, subpool)); SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_PATH_SYNTAX); } while (p); - SVN_ERR(editor->abort_edit(edit_baton, subpool)); + return SVN_NO_ERROR; +} + +static svn_error_t * +test_repos_info(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_repos_t *repos; + svn_test_opts_t opts2; + apr_hash_t *capabilities; + svn_version_t *supports_version; + svn_version_t v1_0_0 = {1, 0, 0, ""}; + svn_version_t v1_4_0 = {1, 4, 0, ""}; + int repos_format; + svn_boolean_t is_fsx = strcmp(opts->fs_type, "fsx") == 0; + + opts2 = *opts; + + /* for repo types that have been around before 1.4 */ + if (!is_fsx) + { + opts2.server_minor_version = 3; + SVN_ERR(svn_test__create_repos(&repos, "test-repo-info-3", + &opts2, pool)); + SVN_ERR(svn_repos_capabilities(&capabilities, repos, pool, pool)); + SVN_TEST_ASSERT(apr_hash_count(capabilities) == 0); + SVN_ERR(svn_repos_info_format(&repos_format, &supports_version, repos, + pool, pool)); + SVN_TEST_ASSERT(repos_format == 3); + SVN_TEST_ASSERT(svn_ver_equal(supports_version, &v1_0_0)); + } + + opts2.server_minor_version = 9; + SVN_ERR(svn_test__create_repos(&repos, "test-repo-info-9", + &opts2, pool)); + SVN_ERR(svn_repos_capabilities(&capabilities, repos, pool, pool)); + SVN_TEST_ASSERT(apr_hash_count(capabilities) == 1); + SVN_TEST_ASSERT(svn_hash_gets(capabilities, SVN_REPOS_CAPABILITY_MERGEINFO)); + SVN_ERR(svn_repos_info_format(&repos_format, &supports_version, repos, + pool, pool)); + SVN_TEST_ASSERT(repos_format == 5); + SVN_TEST_ASSERT(svn_ver_equal(supports_version, &v1_4_0)); return SVN_NO_ERROR; } - -/* Notification receiver for test_dump_bad_mergeinfo(). This does not - need to do anything, it just needs to exist. - */ -static void -dump_r0_mergeinfo_notifier(void *baton, - const svn_repos_notify_t *notify, - apr_pool_t *scratch_pool) +static svn_error_t * +test_config_pool(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + const char *repo_name = "test-repo-config-pool"; + svn_repos_t *repos; + svn_stringbuf_t *cfg_buffer1, *cfg_buffer2; + svn_config_t *cfg; + apr_hash_t *sections1, *sections2; + int i; + svn_fs_txn_t *txn; + svn_fs_root_t *root, *rev_root; + svn_revnum_t rev; + const char *repo_root_url; + const char *srcdir; + svn_error_t *err; + + svn_repos__config_pool_t *config_pool; + apr_pool_t *config_pool_pool; + apr_pool_t *subpool = svn_pool_create(pool); + + const char *wrk_dir = svn_test_data_path("config_pool", pool); + + SVN_ERR(svn_io_make_dir_recursively(wrk_dir, pool)); + + /* read all config info through a single config pool and we want to be + able to control its lifetime. The latter requires a separate pool. */ + config_pool_pool = svn_pool_create(pool); + SVN_ERR(svn_repos__config_pool_create(&config_pool, TRUE, + config_pool_pool)); + + /* have two different configurations */ + SVN_ERR(svn_test_get_srcdir(&srcdir, opts, pool)); + SVN_ERR(svn_stringbuf_from_file2( + &cfg_buffer1, + svn_dirent_join(srcdir, + "../libsvn_subr/config-test.cfg", + pool), + pool)); + cfg_buffer2 = svn_stringbuf_dup(cfg_buffer1, pool); + svn_stringbuf_appendcstr(cfg_buffer2, "\n[more]\nU=\"X\"\n"); + + /* write them to 2x2 files */ + SVN_ERR(svn_io_write_atomic(svn_dirent_join(wrk_dir, + "config-pool-test1.cfg", + pool), + cfg_buffer1->data, cfg_buffer1->len, NULL, + pool)); + SVN_ERR(svn_io_write_atomic(svn_dirent_join(wrk_dir, + "config-pool-test2.cfg", + pool), + cfg_buffer1->data, cfg_buffer1->len, NULL, + pool)); + SVN_ERR(svn_io_write_atomic(svn_dirent_join(wrk_dir, + "config-pool-test3.cfg", + pool), + cfg_buffer2->data, cfg_buffer2->len, NULL, + pool)); + SVN_ERR(svn_io_write_atomic(svn_dirent_join(wrk_dir, + "config-pool-test4.cfg", + pool), + cfg_buffer2->data, cfg_buffer2->len, NULL, + pool)); + + /* requesting a config over and over again should return the same + (even though it is not being referenced) */ + sections1 = NULL; + for (i = 0; i < 4; ++i) + { + SVN_ERR(svn_repos__config_pool_get( + &cfg, NULL, config_pool, + svn_dirent_join(wrk_dir, + "config-pool-test1.cfg", + pool), + TRUE, TRUE, NULL, subpool)); + + if (sections1 == NULL) + sections1 = cfg->sections; + else + SVN_TEST_ASSERT(cfg->sections == sections1); + + svn_pool_clear(subpool); + } + + /* requesting the same config from another file should return the same + (even though it is not being referenced) */ + for (i = 0; i < 4; ++i) + { + SVN_ERR(svn_repos__config_pool_get( + &cfg, NULL, config_pool, + svn_dirent_join(wrk_dir, + "config-pool-test2.cfg", + pool), + TRUE, TRUE, NULL, subpool)); + + SVN_TEST_ASSERT(cfg->sections == sections1); + + svn_pool_clear(subpool); + } + + /* reading a different configuration should return a different pointer */ + sections2 = NULL; + for (i = 0; i < 2; ++i) + { + SVN_ERR(svn_repos__config_pool_get( + &cfg, NULL, config_pool, + svn_dirent_join(wrk_dir, + "config-pool-test3.cfg", + pool), + TRUE, TRUE, NULL, subpool)); + + if (sections2 == NULL) + sections2 = cfg->sections; + else + SVN_TEST_ASSERT(cfg->sections == sections2); + + SVN_TEST_ASSERT(sections1 != sections2); + svn_pool_clear(subpool); + } + + /* create an in-repo config */ + SVN_ERR(svn_dirent_get_absolute(&repo_root_url, repo_name, pool)); + SVN_ERR(svn_uri_get_file_url_from_dirent(&repo_root_url, repo_root_url, + pool)); + + SVN_ERR(svn_test__create_repos(&repos, repo_name, opts, pool)); + SVN_ERR(svn_fs_begin_txn2(&txn, svn_repos_fs(repos), 0, 0, pool)); + SVN_ERR(svn_fs_txn_root(&root, txn, pool)); + SVN_ERR(svn_fs_make_dir(root, "dir", pool)); + SVN_ERR(svn_fs_make_file(root, "dir/config", pool)); + SVN_ERR(svn_test__set_file_contents(root, "dir/config", + cfg_buffer1->data, pool)); + SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool)); + + /* reading the config from the repo should still give cfg1 */ + SVN_ERR(svn_repos__config_pool_get(&cfg, NULL, config_pool, + svn_path_url_add_component2( + repo_root_url, + "dir/config", pool), + TRUE, TRUE, NULL, subpool)); + SVN_TEST_ASSERT(cfg->sections == sections1); + svn_pool_clear(subpool); + + /* create another in-repo config */ + SVN_ERR(svn_fs_begin_txn2(&txn, svn_repos_fs(repos), rev, 0, pool)); + SVN_ERR(svn_fs_txn_root(&root, txn, pool)); + SVN_ERR(svn_fs_revision_root(&rev_root, svn_repos_fs(repos), rev, pool)); + SVN_ERR(svn_fs_copy(rev_root, "dir", root, "another-dir", pool)); + SVN_ERR(svn_test__set_file_contents(root, "dir/config", + cfg_buffer2->data, pool)); + SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool)); + + /* reading the config from the repo should give cfg2 now */ + SVN_ERR(svn_repos__config_pool_get(&cfg, NULL, config_pool, + svn_path_url_add_component2( + repo_root_url, + "dir/config", pool), + TRUE, TRUE, NULL, subpool)); + SVN_TEST_ASSERT(cfg->sections == sections2); + svn_pool_clear(subpool); + + /* reading the copied config should still give cfg1 */ + SVN_ERR(svn_repos__config_pool_get(&cfg, NULL, config_pool, + svn_path_url_add_component2( + repo_root_url, + "another-dir/config", + pool), + TRUE, TRUE, NULL, subpool)); + SVN_TEST_ASSERT(cfg->sections == sections1); + svn_pool_clear(subpool); + + /* once again: repeated reads. This triggers a different code path. */ + SVN_ERR(svn_repos__config_pool_get(&cfg, NULL, config_pool, + svn_path_url_add_component2( + repo_root_url, + "dir/config", pool), + TRUE, TRUE, NULL, subpool)); + SVN_TEST_ASSERT(cfg->sections == sections2); + SVN_ERR(svn_repos__config_pool_get(&cfg, NULL, config_pool, + svn_path_url_add_component2( + repo_root_url, + "another-dir/config", + pool), + TRUE, TRUE, NULL, subpool)); + SVN_TEST_ASSERT(cfg->sections == sections1); + svn_pool_clear(subpool); + + /* access paths that don't exist */ + SVN_TEST_ASSERT_ERROR(svn_repos__config_pool_get(&cfg, NULL, config_pool, + svn_path_url_add_component2(repo_root_url, "X", + pool), + TRUE, TRUE, NULL, subpool), + SVN_ERR_ILLEGAL_TARGET); + err = svn_repos__config_pool_get(&cfg, NULL, config_pool, "X.cfg", + TRUE, TRUE, NULL, subpool); + SVN_TEST_ASSERT(err && APR_STATUS_IS_ENOENT(err->apr_err)); + svn_error_clear(err); + svn_pool_clear(subpool); + + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_repos_fs_type(const svn_test_opts_t *opts, + apr_pool_t *pool) { + svn_repos_t *repos; + + /* Create test repository. */ + SVN_ERR(svn_test__create_repos(&repos, "test-repo-repos_fs_type", + opts, pool)); + + SVN_TEST_STRING_ASSERT(svn_repos_fs_type(repos, pool), opts->fs_type); + + /* Re-open repository and verify fs-type again. */ + SVN_ERR(svn_repos_open3(&repos, svn_repos_path(repos, pool), NULL, + pool, pool)); + + SVN_TEST_STRING_ASSERT(svn_repos_fs_type(repos, pool), opts->fs_type); + + return SVN_NO_ERROR; } -/* Regression test for part the 'dump' part of issue #4476 "Mergeinfo - containing r0 makes svnsync and svnadmin dump fail". */ static svn_error_t * -test_dump_r0_mergeinfo(const svn_test_opts_t *opts, - apr_pool_t *pool) +deprecated_access_context_api(const svn_test_opts_t *opts, + apr_pool_t *pool) { svn_repos_t *repos; - svn_fs_t *fs; + svn_fs_access_t *access; svn_fs_txn_t *txn; - svn_fs_root_t *txn_root; - svn_revnum_t youngest_rev = 0; - const svn_string_t *bad_mergeinfo = svn_string_create("/foo:0", pool); + svn_fs_root_t *root; + const char *conflict; + svn_revnum_t new_rev; + const char *hook; - SVN_ERR(svn_test__create_repos(&repos, "test-repo-dump-r0-mergeinfo", + /* Create test repository. */ + SVN_ERR(svn_test__create_repos(&repos, + "test-repo-deprecated-access-context-api", opts, pool)); - fs = svn_repos_fs(repos); - /* Revision 1: Any commit will do, here */ - 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, "/bar", pool)); - SVN_ERR(svn_repos_fs_commit_txn(NULL, repos, &youngest_rev, txn, pool)); - SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); + /* Set an empty pre-commit hook. */ +#ifdef WIN32 + hook = apr_pstrcat(pool, svn_repos_pre_commit_hook(repos, pool), ".bat", + SVN_VA_NULL); + SVN_ERR(svn_io_file_create(hook, + "exit 0" APR_EOL_STR, + pool)); +#else + hook = svn_repos_pre_commit_hook(repos, pool); + SVN_ERR(svn_io_file_create(hook, + "#!/bin/sh" APR_EOL_STR "exit 0" APR_EOL_STR, + pool)); + SVN_ERR(svn_io_set_file_executable(hook, TRUE, FALSE, pool)); +#endif - /* Revision 2: Add bad mergeinfo */ - 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_change_node_prop(txn_root, "/bar", "svn:mergeinfo", bad_mergeinfo, pool)); - SVN_ERR(svn_repos_fs_commit_txn(NULL, repos, &youngest_rev, txn, pool)); - SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); + /* Set some access context using svn_fs_access_add_lock_token(). */ + SVN_ERR(svn_fs_create_access(&access, "jrandom", pool)); + SVN_ERR(svn_fs_access_add_lock_token(access, "opaquelocktoken:abc")); + SVN_ERR(svn_fs_set_access(svn_repos_fs(repos), access)); - /* Test that a dump completes without error. In order to exercise the - functionality under test -- that is, in order for the dump to try to - parse the mergeinfo it is dumping -- the dump must start from a - revision greater than 1 and must take a notification callback. */ - { - svn_stringbuf_t *stringbuf = svn_stringbuf_create_empty(pool); - svn_stream_t *stream = svn_stream_from_stringbuf(stringbuf, pool); - - SVN_ERR(svn_repos_dump_fs3(repos, stream, 2, SVN_INVALID_REVNUM, - FALSE, FALSE, - dump_r0_mergeinfo_notifier, NULL, - NULL, NULL, - pool)); - } + /* Commit a new revision. */ + SVN_ERR(svn_repos_fs_begin_txn_for_commit2(&txn, repos, 0, + apr_hash_make(pool), pool)); + SVN_ERR(svn_fs_txn_root(&root, txn, pool)); + SVN_ERR(svn_fs_make_dir(root, "/whatever", pool)); + SVN_ERR(svn_repos_fs_commit_txn(&conflict, repos, &new_rev, txn, pool)); + + SVN_TEST_STRING_ASSERT(conflict, NULL); + SVN_TEST_ASSERT(new_rev == 1); return SVN_NO_ERROR; } - -/* Test dumping in the presence of the property PROP_NAME:PROP_VAL. - * Return the dumped data in *DUMP_DATA_P (if DUMP_DATA_P is not null). - * REPOS is an empty repository. - * See svn_repos_dump_fs3() for START_REV, END_REV, NOTIFY_FUNC, NOTIFY_BATON. - */ static svn_error_t * -test_dump_bad_props(svn_stringbuf_t **dump_data_p, - svn_repos_t *repos, - const char *prop_name, - const svn_string_t *prop_val, - svn_revnum_t start_rev, - svn_revnum_t end_rev, - svn_repos_notify_func_t notify_func, - void *notify_baton, - apr_pool_t *pool) +mkdir_delete_copy(svn_repos_t *repos, + const char *src, + const char *dst, + apr_pool_t *pool) { - const char *test_path = "/bar"; svn_fs_t *fs = svn_repos_fs(repos); + svn_revnum_t youngest_rev; svn_fs_txn_t *txn; - svn_fs_root_t *txn_root; - svn_revnum_t youngest_rev = 0; - svn_stringbuf_t *dump_data = svn_stringbuf_create_empty(pool); - svn_stream_t *stream = svn_stream_from_stringbuf(dump_data, pool); - const char *expected_str; + svn_fs_root_t *txn_root, *rev_root; - /* Revision 1: Any commit will do, here */ - SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool)); + 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, test_path , pool)); + SVN_ERR(svn_fs_make_dir(txn_root, "A/T", pool)); + SVN_ERR(svn_repos_fs_commit_txn(NULL, repos, &youngest_rev, txn, 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_delete(txn_root, "A/T", pool)); SVN_ERR(svn_repos_fs_commit_txn(NULL, repos, &youngest_rev, txn, pool)); - SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); - /* Revision 2: Add the bad property */ - SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, 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_change_node_prop(txn_root, test_path , prop_name, prop_val, - pool)); + SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev - 1, pool)); + SVN_ERR(svn_fs_copy(rev_root, src, txn_root, dst, pool)); SVN_ERR(svn_repos_fs_commit_txn(NULL, repos, &youngest_rev, txn, pool)); - SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); - /* Test that a dump completes without error. */ - SVN_ERR(svn_repos_dump_fs3(repos, stream, start_rev, end_rev, - FALSE, FALSE, - notify_func, notify_baton, - NULL, NULL, - pool)); - svn_stream_close(stream); - - /* Check that the property appears in the dump data */ - expected_str = apr_psprintf(pool, "K %d\n%s\n" - "V %d\n%s\n" - "PROPS-END\n", - (int)strlen(prop_name), prop_name, - (int)prop_val->len, prop_val->data); - SVN_TEST_ASSERT(strstr(dump_data->data, expected_str)); - - if (dump_data_p) - *dump_data_p = dump_data; return SVN_NO_ERROR; } -/* Test loading in the presence of the property PROP_NAME:PROP_VAL. - * Load data from DUMP_DATA. - * REPOS is an empty repository. - */ +struct authz_read_baton_t { + apr_hash_t *paths; + apr_pool_t *pool; + const char *deny; +}; + static svn_error_t * -test_load_bad_props(svn_stringbuf_t *dump_data, - svn_repos_t *repos, - const char *prop_name, - const svn_string_t *prop_val, - const char *parent_fspath, - svn_boolean_t validate_props, - svn_repos_notify_func_t notify_func, - void *notify_baton, - apr_pool_t *pool) +authz_read_func(svn_boolean_t *allowed, + svn_fs_root_t *root, + const char *path, + void *baton, + apr_pool_t *pool) { - const char *test_path = apr_psprintf(pool, "%s%s", - parent_fspath ? parent_fspath : "", - "/bar"); - svn_stream_t *stream = svn_stream_from_stringbuf(dump_data, pool); - svn_fs_t *fs; - svn_fs_root_t *rev_root; - svn_revnum_t youngest_rev; - svn_string_t *loaded_prop_val; - - SVN_ERR(svn_repos_load_fs4(repos, stream, - SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, - svn_repos_load_uuid_default, - parent_fspath, - FALSE, FALSE, /*use_*_commit_hook*/ - validate_props, - notify_func, notify_baton, - NULL, NULL, /*cancellation*/ - pool)); - svn_stream_close(stream); + struct authz_read_baton_t *b = baton; + + if (b->deny && !strcmp(b->deny, path)) + *allowed = FALSE; + else + *allowed = TRUE; + + svn_hash_sets(b->paths, apr_pstrdup(b->pool, path), (void*)1); - /* Check the loaded property */ - fs = svn_repos_fs(repos); - SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool)); - SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, pool)); - SVN_ERR(svn_fs_node_prop(&loaded_prop_val, - rev_root, test_path, prop_name, pool)); - SVN_TEST_ASSERT(svn_string_compare(loaded_prop_val, prop_val)); return SVN_NO_ERROR; } -static void -load_r0_mergeinfo_notifier(void *baton, - const svn_repos_notify_t *notify, - apr_pool_t *scratch_pool) +static svn_error_t * +verify_locations(apr_hash_t *actual, + apr_hash_t *expected, + apr_hash_t *checked, + apr_pool_t *pool) { - svn_boolean_t *had_mergeinfo_warning = baton; + apr_hash_index_t *hi; - if (notify->action == svn_repos_notify_warning) + for (hi = apr_hash_first(pool, expected); hi; hi = apr_hash_next(hi)) { - if (notify->warning == svn_repos__notify_warning_invalid_mergeinfo) - { - *had_mergeinfo_warning = TRUE; - } + const svn_revnum_t *rev = apr_hash_this_key(hi); + const char *path = apr_hash_get(actual, rev, sizeof(svn_revnum_t)); + + if (!path) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "expected %s for %d found (null)", + (char*)apr_hash_this_val(hi), (int)*rev); + else if (strcmp(path, apr_hash_this_val(hi))) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "expected %s for %d found %s", + (char*)apr_hash_this_val(hi), (int)*rev, path); + } + + for (hi = apr_hash_first(pool, actual); hi; hi = apr_hash_next(hi)) + { + const svn_revnum_t *rev = apr_hash_this_key(hi); + const char *path = apr_hash_get(expected, rev, sizeof(svn_revnum_t)); + + if (!path) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "found %s for %d expected (null)", + (char*)apr_hash_this_val(hi), (int)*rev); + else if (strcmp(path, apr_hash_this_val(hi))) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "found %s for %d expected %s", + (char*)apr_hash_this_val(hi), (int)*rev, path); + + if (!svn_hash_gets(checked, path)) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "did not check %s", path); + } + + return SVN_NO_ERROR; +} + +static void +set_expected(apr_hash_t *expected, + svn_revnum_t rev, + const char *path, + apr_pool_t *pool) +{ + svn_revnum_t *rp = apr_palloc(pool, sizeof(svn_revnum_t)); + *rp = rev; + apr_hash_set(expected, rp, sizeof(svn_revnum_t), path); } -/* Regression test for the 'load' part of issue #4476 "Mergeinfo - * containing r0 makes svnsync and svnadmin dump fail". - * - * Bad mergeinfo should not prevent loading a backup, at least when we do not - * require mergeinfo revision numbers or paths to be adjusted during loading. - */ static svn_error_t * -test_load_r0_mergeinfo(const svn_test_opts_t *opts, - apr_pool_t *pool) +trace_node_locations_authz(const svn_test_opts_t *opts, + apr_pool_t *pool) { - const char *prop_name = "svn:mergeinfo"; - const svn_string_t *prop_val = svn_string_create("/foo:0", pool); - svn_stringbuf_t *dump_data = svn_stringbuf_create_empty(pool); + svn_repos_t *repos; + svn_fs_t *fs; + svn_revnum_t youngest_rev = 0; + svn_fs_txn_t *txn; + svn_fs_root_t *txn_root; + struct authz_read_baton_t arb; + apr_array_header_t *revs = apr_array_make(pool, 10, sizeof(svn_revnum_t)); + apr_hash_t *locations; + apr_hash_t *expected = apr_hash_make(pool); + int i; - /* Produce a dump file containing bad mergeinfo */ - { - svn_repos_t *repos; + /* Create test repository. */ + SVN_ERR(svn_test__create_repos(&repos, "test-repo-trace-node-locations-authz", + opts, pool)); + fs = svn_repos_fs(repos); - SVN_ERR(svn_test__create_repos(&repos, "test-repo-load-r0-mi-1", - opts, pool)); - SVN_ERR(test_dump_bad_props(&dump_data, repos, - prop_name, prop_val, - SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, - NULL, NULL, pool)); - } + /* r1 create A */ + 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", pool)); + SVN_ERR(svn_fs_make_file(txn_root, "A/f", pool)); + SVN_ERR(svn_test__set_file_contents(txn_root, "A/f", "foobar", pool)); + SVN_ERR(svn_repos_fs_commit_txn(NULL, repos, &youngest_rev, txn, pool)); - /* Test loading without validating properties: should warn and succeed */ - { - svn_repos_t *repos; - svn_boolean_t had_mergeinfo_warning = FALSE; - - SVN_ERR(svn_test__create_repos(&repos, "test-repo-load-r0-mi-2", - opts, pool)); - - /* Without changing revision numbers or paths */ - SVN_ERR(test_load_bad_props(dump_data, repos, - prop_name, prop_val, - NULL /*parent_dir*/, FALSE /*validate_props*/, - load_r0_mergeinfo_notifier, &had_mergeinfo_warning, - pool)); - SVN_TEST_ASSERT(had_mergeinfo_warning); - - /* With changing revision numbers and/or paths (by loading the same data - again, on top of existing revisions, into subdirectory 'bar') */ - had_mergeinfo_warning = FALSE; - SVN_ERR(test_load_bad_props(dump_data, repos, - prop_name, prop_val, - "/bar", FALSE /*validate_props*/, - load_r0_mergeinfo_notifier, &had_mergeinfo_warning, - pool)); - SVN_TEST_ASSERT(had_mergeinfo_warning); - } + /* r4 copy A to B */ + SVN_ERR(mkdir_delete_copy(repos, "A", "B", pool)); - /* Test loading with validating properties: should return an error */ - { - svn_repos_t *repos; + /* r7 copy B to C */ + SVN_ERR(mkdir_delete_copy(repos, "B", "C", pool)); - SVN_ERR(svn_test__create_repos(&repos, "test-repo-load-r0-mi-3", - opts, pool)); - - /* Without changing revision numbers or paths */ - SVN_TEST__ASSERT_ANY_ERROR(test_load_bad_props(dump_data, repos, - prop_name, prop_val, - NULL /*parent_dir*/, TRUE /*validate_props*/, - NULL, NULL, - pool)); - - /* With changing revision numbers and/or paths (by loading the same data - again, on top of existing revisions, into subdirectory 'bar') */ - SVN_TEST__ASSERT_ANY_ERROR(test_load_bad_props(dump_data, repos, - prop_name, prop_val, - "/bar", TRUE /*validate_props*/, - NULL, NULL, - pool)); - } + /* r10 copy C to D */ + SVN_ERR(mkdir_delete_copy(repos, "C", "D", pool)); + + SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool)); + SVN_ERR_ASSERT(youngest_rev == 10); + + arb.paths = apr_hash_make(pool); + arb.pool = pool; + arb.deny = NULL; + + apr_array_clear(revs); + for (i = 0; i <= youngest_rev; ++i) + APR_ARRAY_PUSH(revs, svn_revnum_t) = i; + set_expected(expected, 10, "/D/f", pool); + set_expected(expected, 8, "/C/f", pool); + set_expected(expected, 7, "/C/f", pool); + set_expected(expected, 5, "/B/f", pool); + set_expected(expected, 4, "/B/f", pool); + set_expected(expected, 2, "/A/f", pool); + set_expected(expected, 1, "/A/f", pool); + apr_hash_clear(arb.paths); + SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, + authz_read_func, &arb, pool)); + SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); + + apr_array_clear(revs); + for (i = 1; i <= youngest_rev; ++i) + APR_ARRAY_PUSH(revs, svn_revnum_t) = i; + apr_hash_clear(arb.paths); + SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, + authz_read_func, &arb, pool)); + SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); + + apr_array_clear(revs); + for (i = 2; i <= youngest_rev; ++i) + APR_ARRAY_PUSH(revs, svn_revnum_t) = i; + set_expected(expected, 1, NULL, pool); + apr_hash_clear(arb.paths); + SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, + authz_read_func, &arb, pool)); + SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); + + apr_array_clear(revs); + for (i = 3; i <= youngest_rev; ++i) + APR_ARRAY_PUSH(revs, svn_revnum_t) = i; + set_expected(expected, 2, NULL, pool); + apr_hash_clear(arb.paths); + SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, + authz_read_func, &arb, pool)); + SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); + + apr_array_clear(revs); + for (i = 6; i <= youngest_rev; ++i) + APR_ARRAY_PUSH(revs, svn_revnum_t) = i; + set_expected(expected, 5, NULL, pool); + set_expected(expected, 4, NULL, pool); + apr_hash_clear(arb.paths); + SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, + authz_read_func, &arb, pool)); + SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); + + arb.deny = "/B/f"; + apr_array_clear(revs); + for (i = 0; i <= youngest_rev; ++i) + APR_ARRAY_PUSH(revs, svn_revnum_t) = i; + apr_hash_clear(arb.paths); + SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, + authz_read_func, &arb, pool)); + SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); + + apr_array_clear(revs); + for (i = 6; i <= youngest_rev; ++i) + APR_ARRAY_PUSH(revs, svn_revnum_t) = i; + apr_hash_clear(arb.paths); + SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, + authz_read_func, &arb, pool)); + SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); + + APR_ARRAY_PUSH(revs, svn_revnum_t) = 0; + apr_hash_clear(arb.paths); + SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, + authz_read_func, &arb, pool)); + SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); return SVN_NO_ERROR; } /* The test table. */ -struct svn_test_descriptor_t test_funcs[] = +static int max_threads = 4; + +static struct svn_test_descriptor_t test_funcs[] = { SVN_TEST_NULL, SVN_TEST_OPTS_PASS(dir_deltas, @@ -3569,9 +3894,17 @@ struct svn_test_descriptor_t test_funcs[] = "test svn_repos_delete"), SVN_TEST_OPTS_PASS(filename_with_control_chars, "test filenames with control characters"), - SVN_TEST_OPTS_PASS(test_dump_r0_mergeinfo, - "test dumping with r0 mergeinfo"), - SVN_TEST_OPTS_PASS(test_load_r0_mergeinfo, - "test loading with r0 mergeinfo"), + SVN_TEST_OPTS_PASS(test_repos_info, + "test svn_repos_info_*"), + SVN_TEST_OPTS_PASS(test_config_pool, + "test svn_repos__config_pool_*"), + SVN_TEST_OPTS_PASS(test_repos_fs_type, + "test test_repos_fs_type"), + SVN_TEST_OPTS_PASS(deprecated_access_context_api, + "test deprecated access context api"), + SVN_TEST_OPTS_PASS(trace_node_locations_authz, + "authz for svn_repos_trace_node_locations"), SVN_TEST_NULL }; + +SVN_TEST_MAIN |