/* utils.c --- wc/client test utilities * * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== */ #include "svn_error.h" #include "svn_client.h" #include "svn_cmdline.h" #include "svn_pools.h" #include "utils.h" #include "../svn_test_fs.h" #include "../../libsvn_wc/wc.h" #include "../../libsvn_wc/wc-queries.h" #define SVN_WC__I_AM_WC_DB #include "../../libsvn_wc/wc_db_private.h" #include "../../libsvn_wc/token-map.h" svn_error_t * svn_test__create_client_ctx(svn_client_ctx_t **ctx, svn_test__sandbox_t *sbox, apr_pool_t *result_pool) { SVN_ERR(svn_client_create_context2(ctx, NULL, result_pool)); SVN_ERR(svn_test__init_auth_baton(&(*ctx)->auth_baton, result_pool)); if (sbox) (*ctx)->wc_ctx = sbox->wc_ctx; return SVN_NO_ERROR; } /* Create an empty repository and WC for the test TEST_NAME. Set *REPOS_URL * to the URL of the new repository, *REPOS_DIR to its local path and * *WC_ABSPATH to the root path of the * new WC. * * Create the repository and WC in subdirectories called * REPOSITORIES_WORK_DIR/TEST_NAME and WCS_WORK_DIR/TEST_NAME respectively, * within the current working directory. * * Register the repo and WC to be cleaned up when the test suite exits. */ static svn_error_t * create_repos_and_wc(const char **repos_url, const char **repos_dir, const char **wc_abspath, const char *test_name, const svn_test_opts_t *opts, apr_pool_t *pool) { const char *repos_path = svn_relpath_join(REPOSITORIES_WORK_DIR, test_name, pool); const char *wc_path = svn_relpath_join(WCS_WORK_DIR, test_name, pool); /* Remove the repo and WC dirs if they already exist, to ensure the test * will run even if a previous failed attempt was not cleaned up. */ SVN_ERR(svn_io_remove_dir2(repos_path, TRUE, NULL, NULL, pool)); SVN_ERR(svn_io_remove_dir2(wc_path, TRUE, NULL, NULL, pool)); /* Create the parent dirs of the repo and WC if necessary. */ SVN_ERR(svn_io_make_dir_recursively(REPOSITORIES_WORK_DIR, pool)); SVN_ERR(svn_io_make_dir_recursively(WCS_WORK_DIR, pool)); /* Create a repos. Register it for clean-up. Set *REPOS_URL to its path. */ { /* Use a subpool to create the repository and then destroy the subpool so the repository's underlying filesystem is closed. If opts->fs_type is BDB this prevents any attempt to open a second environment handle within the same process when we checkout the WC below. BDB 4.4+ allows only a single environment handle to be open per process. */ apr_pool_t *subpool = svn_pool_create(pool); SVN_ERR(svn_test__create_repos2(NULL, repos_url, repos_dir, repos_path, opts, pool, subpool)); svn_pool_destroy(subpool); } /* Create a WC. Set *WC_ABSPATH to its path. */ { apr_pool_t *subpool = svn_pool_create(pool); /* To cleanup CTX */ svn_client_ctx_t *ctx; svn_opt_revision_t head_rev = { svn_opt_revision_head, {0} }; SVN_ERR(svn_test__create_client_ctx(&ctx, NULL, subpool)); SVN_ERR(svn_dirent_get_absolute(wc_abspath, wc_path, pool)); SVN_ERR(svn_client_checkout3(NULL, *repos_url, *wc_abspath, &head_rev, &head_rev, svn_depth_infinity, FALSE /* ignore_externals */, FALSE /* allow_unver_obstructions */, ctx, subpool)); svn_pool_destroy(subpool); } /* Register this WC for cleanup. */ svn_test_add_dir_cleanup(*wc_abspath); return SVN_NO_ERROR; } WC_QUERIES_SQL_DECLARE_STATEMENTS(statements); svn_error_t * svn_test__create_fake_wc(const char *wc_abspath, const char *extra_statements, const svn_test__nodes_data_t nodes[], const svn_test__actual_data_t actuals[], apr_pool_t *scratch_pool) { const char *dotsvn_abspath = svn_dirent_join(wc_abspath, ".svn", scratch_pool); svn_sqlite__db_t *sdb; const char **my_statements; int i; svn_sqlite__stmt_t *stmt; const apr_int64_t wc_id = 1; /* Allocate MY_STATEMENTS in RESULT_POOL because the SDB will continue to * refer to it over its lifetime. */ my_statements = apr_palloc(scratch_pool, 7 * sizeof(const char *)); my_statements[0] = statements[STMT_CREATE_SCHEMA]; my_statements[1] = statements[STMT_CREATE_NODES]; my_statements[2] = statements[STMT_CREATE_NODES_TRIGGERS]; my_statements[3] = statements[STMT_CREATE_EXTERNALS]; my_statements[4] = statements[STMT_INSTALL_SCHEMA_STATISTICS]; my_statements[5] = extra_statements; my_statements[6] = NULL; /* Create fake-wc/SUBDIR/.svn/ for placing the metadata. */ SVN_ERR(svn_io_make_dir_recursively(dotsvn_abspath, scratch_pool)); SVN_ERR(svn_wc__db_util_open_db(&sdb, wc_abspath, "wc.db", svn_sqlite__mode_rwcreate, FALSE /* exclusive */, 0 /* timeout */, my_statements, scratch_pool, scratch_pool)); for (i = 0; my_statements[i] != NULL; i++) SVN_ERR(svn_sqlite__exec_statements(sdb, /* my_statements[] */ i)); SVN_ERR(svn_sqlite__close(sdb)); if (!nodes && !actuals) return SVN_NO_ERROR; /* Re-open with normal set of statements */ SVN_ERR(svn_wc__db_util_open_db(&sdb, wc_abspath, "wc.db", svn_sqlite__mode_readwrite, FALSE /* exclusive */, 0 /* timeout */, statements, scratch_pool, scratch_pool)); if (nodes) { SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE)); for (i = 0; nodes[i].local_relpath; i++) { SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnns", wc_id, nodes[i].local_relpath, nodes[i].op_depth, nodes[i].local_relpath[0] ? svn_relpath_dirname(nodes[i].local_relpath, scratch_pool) : NULL, nodes[i].presence)); if (nodes[i].repos_relpath) { SVN_ERR(svn_sqlite__bind_int64(stmt, 5, nodes[i].repos_id)); SVN_ERR(svn_sqlite__bind_text(stmt, 6, nodes[i].repos_relpath)); SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, nodes[i].revision)); } if (nodes[i].depth) SVN_ERR(svn_sqlite__bind_text(stmt, 9, nodes[i].depth)); if (nodes[i].kind != 0) SVN_ERR(svn_sqlite__bind_token(stmt, 10, kind_map, nodes[i].kind)); if (nodes[i].last_author || nodes[i].last_date) { SVN_ERR(svn_sqlite__bind_revnum(stmt, 11, nodes[i].last_revision)); SVN_ERR(svn_sqlite__bind_int64(stmt, 12, nodes[i].last_date)); SVN_ERR(svn_sqlite__bind_text(stmt, 13, nodes[i].last_author)); } if (nodes[i].checksum) SVN_ERR(svn_sqlite__bind_text(stmt, 14, nodes[i].checksum)); if (nodes[i].properties) SVN_ERR(svn_sqlite__bind_text(stmt, 15, nodes[i].properties)); if (nodes[i].recorded_size || nodes[i].recorded_time) { SVN_ERR(svn_sqlite__bind_int64(stmt, 16, nodes[i].recorded_size)); SVN_ERR(svn_sqlite__bind_int64(stmt, 17, nodes[i].recorded_time)); } /* 18 is DAV cache */ if (nodes[i].symlink_target) SVN_ERR(svn_sqlite__bind_text(stmt, 19, nodes[i].symlink_target)); if (nodes[i].file_external) SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1)); if (nodes[i].moved_to) SVN_ERR(svn_sqlite__bind_text(stmt, 21, nodes[i].moved_to)); if (nodes[i].moved_here) SVN_ERR(svn_sqlite__bind_int(stmt, 22, 1)); if (nodes[i].inherited_props) SVN_ERR(svn_sqlite__bind_text(stmt, 23, nodes[i].inherited_props)); SVN_ERR(svn_sqlite__step_done(stmt)); } } if (actuals) { SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL_NODE)); for (i = 0; actuals[i].local_relpath; i++) { SVN_ERR(svn_sqlite__bindf(stmt, "isssss", wc_id, actuals[i].local_relpath, actuals[i].local_relpath[0] ? svn_relpath_dirname(actuals[i].local_relpath, scratch_pool) : NULL, actuals[i].properties, actuals[i].changelist, actuals[i].conflict_data)); SVN_ERR(svn_sqlite__step_done(stmt)); } } SVN_ERR(svn_sqlite__close(sdb)); return SVN_NO_ERROR; } svn_error_t * svn_test__sandbox_create(svn_test__sandbox_t *sandbox, const char *test_name, const svn_test_opts_t *opts, apr_pool_t *pool) { sandbox->pool = pool; SVN_ERR(create_repos_and_wc(&sandbox->repos_url, &sandbox->repos_dir, &sandbox->wc_abspath, test_name, opts, pool)); SVN_ERR(svn_wc_context_create(&sandbox->wc_ctx, NULL, pool, pool)); return SVN_NO_ERROR; } svn_error_t * sbox_file_write(svn_test__sandbox_t *b, const char *path, const char *text) { apr_file_t *f; SVN_ERR(svn_io_file_open(&f, sbox_wc_path(b, path), (APR_WRITE | APR_CREATE | APR_TRUNCATE), APR_OS_DEFAULT, b->pool)); SVN_ERR(svn_io_file_write_full(f, text, strlen(text), NULL, b->pool)); SVN_ERR(svn_io_file_close(f, b->pool)); return SVN_NO_ERROR; } svn_error_t * sbox_wc_add(svn_test__sandbox_t *b, const char *path) { const char *parent_abspath; path = sbox_wc_path(b, path); parent_abspath = svn_dirent_dirname(path, b->pool); SVN_ERR(svn_wc__acquire_write_lock(NULL, b->wc_ctx, parent_abspath, FALSE, b->pool, b->pool)); SVN_ERR(svn_wc_add_from_disk3(b->wc_ctx, path, NULL /*props*/, FALSE /* skip checks */, NULL, NULL, b->pool)); SVN_ERR(svn_wc__release_write_lock(b->wc_ctx, parent_abspath, b->pool)); return SVN_NO_ERROR; } svn_error_t * sbox_disk_mkdir(svn_test__sandbox_t *b, const char *path) { path = sbox_wc_path(b, path); SVN_ERR(svn_io_dir_make(path, APR_FPROT_OS_DEFAULT, b->pool)); return SVN_NO_ERROR; } svn_error_t * sbox_wc_mkdir(svn_test__sandbox_t *b, const char *path) { SVN_ERR(sbox_disk_mkdir(b, path)); SVN_ERR(sbox_wc_add(b, path)); return SVN_NO_ERROR; } #if 0 /* not used */ /* Copy the file or directory tree FROM_PATH to TO_PATH which must not exist * beforehand. */ svn_error_t * sbox_disk_copy(svn_test__sandbox_t *b, const char *from_path, const char *to_path) { const char *to_dir, *to_name; from_path = sbox_wc_path(b, from_path); to_path = sbox_wc_path(b, to_path); svn_dirent_split(&to_dir, &to_name, to_path, b->pool); return svn_io_copy_dir_recursively(from_path, to_dir, to_name, FALSE, NULL, NULL, b->pool); } #endif svn_error_t * sbox_wc_copy(svn_test__sandbox_t *b, const char *from_path, const char *to_path) { const char *parent_abspath; from_path = sbox_wc_path(b, from_path); to_path = sbox_wc_path(b, to_path); parent_abspath = svn_dirent_dirname(to_path, b->pool); SVN_ERR(svn_wc__acquire_write_lock(NULL, b->wc_ctx, parent_abspath, FALSE, b->pool, b->pool)); SVN_ERR(svn_wc_copy3(b->wc_ctx, from_path, to_path, FALSE, NULL, NULL, NULL, NULL, b->pool)); SVN_ERR(svn_wc__release_write_lock(b->wc_ctx, parent_abspath, b->pool)); return SVN_NO_ERROR; } svn_error_t * sbox_wc_copy_url(svn_test__sandbox_t *b, const char *from_url, svn_revnum_t revision, const char *to_path) { apr_pool_t *scratch_pool = b->pool; svn_client_ctx_t *ctx; svn_opt_revision_t rev = { svn_opt_revision_unspecified, {0} }; svn_client_copy_source_t* src; apr_array_header_t *sources = apr_array_make( scratch_pool, 1, sizeof(svn_client_copy_source_t *)); SVN_ERR(svn_test__create_client_ctx(&ctx, b, scratch_pool)); if (SVN_IS_VALID_REVNUM(revision)) { rev.kind = svn_opt_revision_number; rev.value.number = revision; } src = apr_pcalloc(scratch_pool, sizeof(*src)); src->path = from_url; src->revision = &rev; src->peg_revision = &rev; APR_ARRAY_PUSH(sources, svn_client_copy_source_t *) = src; SVN_ERR(svn_client_copy7(sources, sbox_wc_path(b, to_path), FALSE /* copy_as_child */, FALSE /* make_parents */, FALSE /* ignore_externals */, FALSE /* metadata_only */, FALSE, NULL /* pin_external */, NULL /* revprops */, NULL, NULL, /* commit_callback */ ctx, scratch_pool)); ctx->wc_ctx = NULL; return SVN_NO_ERROR; } svn_error_t * sbox_wc_revert(svn_test__sandbox_t *b, const char *path, svn_depth_t depth) { const char *abspath = sbox_wc_path(b, path); const char *dir_abspath; const char *lock_root_abspath; if (strcmp(abspath, b->wc_abspath)) dir_abspath = svn_dirent_dirname(abspath, b->pool); else dir_abspath = abspath; SVN_ERR(svn_wc__acquire_write_lock(&lock_root_abspath, b->wc_ctx, dir_abspath, FALSE /* lock_anchor */, b->pool, b->pool)); SVN_ERR(svn_wc_revert5(b->wc_ctx, abspath, depth, FALSE /* use_commit_times */, NULL /* changelist_filter */, FALSE /* clear_changelists */, FALSE /* metadata_only */, NULL, NULL, /* cancel baton + func */ NULL, NULL, /* notify baton + func */ b->pool)); SVN_ERR(svn_wc__release_write_lock(b->wc_ctx, lock_root_abspath, b->pool)); return SVN_NO_ERROR; } svn_error_t * sbox_wc_delete(svn_test__sandbox_t *b, const char *path) { const char *abspath = sbox_wc_path(b, path); const char *dir_abspath = svn_dirent_dirname(abspath, b->pool); const char *lock_root_abspath; SVN_ERR(svn_wc__acquire_write_lock(&lock_root_abspath, b->wc_ctx, dir_abspath, FALSE, b->pool, b->pool)); SVN_ERR(svn_wc_delete4(b->wc_ctx, abspath, FALSE, TRUE, NULL, NULL, /* cancel baton + func */ NULL, NULL, /* notify baton + func */ b->pool)); SVN_ERR(svn_wc__release_write_lock(b->wc_ctx, lock_root_abspath, b->pool)); return SVN_NO_ERROR; } svn_error_t * sbox_wc_exclude(svn_test__sandbox_t *b, const char *path) { const char *abspath = sbox_wc_path(b, path); const char *lock_root_abspath; SVN_ERR(svn_wc__acquire_write_lock(&lock_root_abspath, b->wc_ctx, abspath, TRUE, b->pool, b->pool)); SVN_ERR(svn_wc_exclude(b->wc_ctx, abspath, NULL, NULL, /* cancel baton + func */ NULL, NULL, /* notify baton + func */ b->pool)); SVN_ERR(svn_wc__release_write_lock(b->wc_ctx, lock_root_abspath, b->pool)); return SVN_NO_ERROR; } svn_error_t * sbox_wc_commit_ex(svn_test__sandbox_t *b, apr_array_header_t *targets, svn_depth_t depth) { svn_client_ctx_t *ctx; apr_pool_t *scratch_pool = svn_pool_create(b->pool); svn_error_t *err; SVN_ERR(svn_test__create_client_ctx(&ctx, b, scratch_pool)); /* A successfull commit doesn't close the ra session, but leaves that to the caller. This leaves the BDB handle open, which might cause problems in further test code. (op_depth_tests.c's repo_wc_copy) */ err = svn_client_commit6(targets, depth, FALSE /* keep_locks */, FALSE /* keep_changelist */, TRUE /* commit_as_operations */, TRUE /* include_file_externals */, FALSE /* include_dir_externals */, NULL, NULL, NULL, NULL, ctx, scratch_pool); svn_pool_destroy(scratch_pool); return svn_error_trace(err); } svn_error_t * sbox_wc_commit(svn_test__sandbox_t *b, const char *path) { apr_array_header_t *targets = apr_array_make(b->pool, 1, sizeof(const char *)); APR_ARRAY_PUSH(targets, const char *) = sbox_wc_path(b, path); return sbox_wc_commit_ex(b, targets, svn_depth_infinity); } svn_error_t * sbox_wc_update_depth(svn_test__sandbox_t *b, const char *path, svn_revnum_t revnum, svn_depth_t depth, svn_boolean_t sticky) { svn_client_ctx_t *ctx; apr_array_header_t *result_revs; apr_array_header_t *paths = apr_array_make(b->pool, 1, sizeof(const char *)); svn_opt_revision_t revision; if (SVN_IS_VALID_REVNUM(revnum)) { revision.kind = svn_opt_revision_number; revision.value.number = revnum; } else { revision.kind = svn_opt_revision_head; } APR_ARRAY_PUSH(paths, const char *) = sbox_wc_path(b, path); SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); return svn_client_update4(&result_revs, paths, &revision, depth, sticky, FALSE, FALSE, FALSE, FALSE, ctx, b->pool); } svn_error_t * sbox_wc_update(svn_test__sandbox_t *b, const char *path, svn_revnum_t revnum) { SVN_ERR(sbox_wc_update_depth(b, path, revnum, svn_depth_unknown, FALSE)); return SVN_NO_ERROR; } svn_error_t * sbox_wc_switch(svn_test__sandbox_t *b, const char *path, const char *url, svn_depth_t depth) { svn_client_ctx_t *ctx; svn_revnum_t result_rev; svn_opt_revision_t head_rev = { svn_opt_revision_head, {0} }; url = apr_pstrcat(b->pool, b->repos_url, url, SVN_VA_NULL); SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); return svn_client_switch3(&result_rev, sbox_wc_path(b, path), url, &head_rev, &head_rev, depth, FALSE /* depth_is_sticky */, TRUE /* ignore_externals */, FALSE /* allow_unver_obstructions */, TRUE /* ignore_ancestry */, ctx, b->pool); } svn_error_t * sbox_wc_resolved(svn_test__sandbox_t *b, const char *path) { return sbox_wc_resolve(b, path, svn_depth_infinity, svn_wc_conflict_choose_merged); } svn_error_t * sbox_wc_resolve(svn_test__sandbox_t *b, const char *path, svn_depth_t depth, svn_wc_conflict_choice_t conflict_choice) { const char *lock_abspath; svn_error_t *err; SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, b->wc_ctx, sbox_wc_path(b, path), b->pool, b->pool)); err = svn_wc__resolve_conflicts(b->wc_ctx, sbox_wc_path(b, path), depth, TRUE /* resolve_text */, "" /* resolve_prop (ALL props) */, TRUE /* resolve_tree */, conflict_choice, NULL, NULL, /* conflict func */ NULL, NULL, /* cancellation */ NULL, NULL, /* notification */ b->pool); err = svn_error_compose_create(err, svn_wc__release_write_lock(b->wc_ctx, lock_abspath, b->pool)); return err; } svn_error_t * sbox_wc_resolve_prop(svn_test__sandbox_t *b, const char *path, const char *propname, svn_wc_conflict_choice_t conflict_choice) { const char *lock_abspath; svn_error_t *err; SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, b->wc_ctx, sbox_wc_path(b, path), b->pool, b->pool)); err = svn_wc__resolve_conflicts(b->wc_ctx, sbox_wc_path(b, path), svn_depth_empty, FALSE, propname, FALSE, conflict_choice, NULL, NULL, /* conflict func */ NULL, NULL, /* cancellation */ NULL, NULL, /* notification */ b->pool); err = svn_error_compose_create(err, svn_wc__release_write_lock(b->wc_ctx, lock_abspath, b->pool)); return err; } svn_error_t * sbox_wc_move(svn_test__sandbox_t *b, const char *src, const char *dst) { svn_client_ctx_t *ctx; apr_array_header_t *paths = apr_array_make(b->pool, 1, sizeof(const char *)); SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); APR_ARRAY_PUSH(paths, const char *) = sbox_wc_path(b, src); return svn_client_move7(paths, sbox_wc_path(b, dst), FALSE /* move_as_child */, FALSE /* make_parents */, TRUE /* allow_mixed_revisions */, FALSE /* metadata_only */, NULL /* revprop_table */, NULL, NULL, /* commit callback */ ctx, b->pool); } svn_error_t * sbox_wc_propset(svn_test__sandbox_t *b, const char *name, const char *value, const char *path) { svn_client_ctx_t *ctx; apr_array_header_t *paths = apr_array_make(b->pool, 1, sizeof(const char *)); svn_string_t *pval = value ? svn_string_create(value, b->pool) : NULL; SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); APR_ARRAY_PUSH(paths, const char *) = sbox_wc_path(b, path); return svn_client_propset_local(name, pval, paths, svn_depth_empty, TRUE /* skip_checks */, NULL, ctx, b->pool); } svn_error_t * sbox_wc_relocate(svn_test__sandbox_t *b, const char *new_repos_url) { apr_pool_t *scratch_pool = b->pool; svn_client_ctx_t *ctx; SVN_ERR(svn_test__create_client_ctx(&ctx, b, scratch_pool)); SVN_ERR(svn_client_relocate2(b->wc_abspath, b->repos_url, new_repos_url, FALSE, ctx,scratch_pool)); b->repos_url = apr_pstrdup(b->pool, new_repos_url); return SVN_NO_ERROR; } svn_error_t * sbox_add_and_commit_greek_tree(svn_test__sandbox_t *b) { const struct svn_test__tree_entry_t *node; for (node = svn_test__greek_tree_nodes; node->path; node++) { if (node->contents) { SVN_ERR(sbox_file_write(b, node->path, node->contents)); SVN_ERR(sbox_wc_add(b, node->path)); } else { SVN_ERR(sbox_wc_mkdir(b, node->path)); } } SVN_ERR(sbox_wc_commit(b, "")); return SVN_NO_ERROR; }