diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2015-03-18 13:33:26 +0000 |
---|---|---|
committer | <> | 2015-07-08 14:41:01 +0000 |
commit | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (patch) | |
tree | 98bae10dde41c746c51ae97ec4f879e330415aa7 /subversion/tests/libsvn_wc | |
parent | 239dfafe71711b2f4c43d7b90a1228d7bdc5195e (diff) | |
download | subversion-tarball-subversion-1.8.13.tar.gz |
Imported from /home/lorry/working-area/delta_subversion-tarball/subversion-1.8.13.tar.gz.subversion-1.8.13
Diffstat (limited to 'subversion/tests/libsvn_wc')
-rw-r--r-- | subversion/tests/libsvn_wc/conflict-data-test.c | 831 | ||||
-rwxr-xr-x | subversion/tests/libsvn_wc/create_wc_for_upgrade.sh | 8 | ||||
-rw-r--r-- | subversion/tests/libsvn_wc/db-test.c | 363 | ||||
-rw-r--r-- | subversion/tests/libsvn_wc/entries-compat.c | 148 | ||||
-rw-r--r-- | subversion/tests/libsvn_wc/op-depth-test.c | 6654 | ||||
-rw-r--r-- | subversion/tests/libsvn_wc/pristine-store-test.c | 7 | ||||
-rw-r--r-- | subversion/tests/libsvn_wc/tree-conflict-data-test.c | 307 | ||||
-rw-r--r-- | subversion/tests/libsvn_wc/utils.c | 385 | ||||
-rw-r--r-- | subversion/tests/libsvn_wc/utils.h | 101 | ||||
-rw-r--r-- | subversion/tests/libsvn_wc/wc-incomplete-tester.c | 2 | ||||
-rw-r--r-- | subversion/tests/libsvn_wc/wc-queries-test.c | 897 | ||||
-rw-r--r-- | subversion/tests/libsvn_wc/wc-test.c | 323 |
12 files changed, 8955 insertions, 1071 deletions
diff --git a/subversion/tests/libsvn_wc/conflict-data-test.c b/subversion/tests/libsvn_wc/conflict-data-test.c new file mode 100644 index 0000000..97d22ea --- /dev/null +++ b/subversion/tests/libsvn_wc/conflict-data-test.c @@ -0,0 +1,831 @@ +/* + * + * 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. + * + */ + +/* + * conflict-data-test.c -- test the storage of tree conflict data + */ + +#include <stdio.h> +#include <string.h> +#include <apr_hash.h> +#include <apr_tables.h> + +#include "svn_props.h" +#include "svn_pools.h" +#include "svn_hash.h" +#include "svn_types.h" +#include "svn_wc.h" +#include "private/svn_wc_private.h" +#include "utils.h" +#include "../svn_test.h" +#include "../../libsvn_wc/tree_conflicts.h" +#include "../../libsvn_wc/wc.h" +#include "../../libsvn_wc/wc_db.h" +#include "../../libsvn_wc/conflicts.h" + +/* A quick way to create error messages. */ +static svn_error_t * +fail(apr_pool_t *pool, const char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + msg = apr_pvsprintf(pool, fmt, ap); + va_end(ap); + + return svn_error_create(SVN_ERR_TEST_FAILED, 0, msg); +} + +/* Assert that two integers are equal. Return an error if not. */ +#define ASSERT_INT_EQ(a, b) \ + do { \ + if ((a) != (b)) \ + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, \ + "failed: ASSERT_INT_EQ(" #a ", " #b ") " \ + "-> (%d == %d)", a, b); \ + } while (0) + +/* Assert that two strings are equal or both null. Return an error if not. */ +#define ASSERT_STR_EQ(a, b) \ + SVN_TEST_STRING_ASSERT(a, b) + +/* Assert that two version_t's are equal or both null. Return an error if not. */ +static svn_error_t * +compare_version(const svn_wc_conflict_version_t *actual, + const svn_wc_conflict_version_t *expected) +{ + if (actual == NULL && expected == NULL) + return SVN_NO_ERROR; + + SVN_TEST_ASSERT(actual && expected); + ASSERT_STR_EQ(actual->repos_url, expected->repos_url); + ASSERT_INT_EQ((int)actual->peg_rev, (int)expected->peg_rev); + ASSERT_STR_EQ(actual->path_in_repos, expected->path_in_repos); + ASSERT_INT_EQ(actual->node_kind, expected->node_kind); + return SVN_NO_ERROR; +} + +/* Assert that two conflict descriptions contain exactly the same data + * (including names of temporary files), or are both NULL. Return an + * error if not. */ +static svn_error_t * +compare_conflict(const svn_wc_conflict_description2_t *actual, + const svn_wc_conflict_description2_t *expected) +{ + if (actual == NULL && expected == NULL) + return SVN_NO_ERROR; + + SVN_TEST_ASSERT(actual && expected); + + ASSERT_INT_EQ(actual->kind, expected->kind); + ASSERT_STR_EQ(actual->local_abspath, expected->local_abspath); + ASSERT_INT_EQ(actual->node_kind, expected->node_kind); + ASSERT_STR_EQ(actual->property_name, expected->property_name); + ASSERT_INT_EQ(actual->is_binary, expected->is_binary); + ASSERT_STR_EQ(actual->mime_type, expected->mime_type); + ASSERT_INT_EQ(actual->action, expected->action); + ASSERT_INT_EQ(actual->reason, expected->reason); + ASSERT_STR_EQ(actual->base_abspath, expected->base_abspath); + ASSERT_STR_EQ(actual->their_abspath, expected->their_abspath); + ASSERT_STR_EQ(actual->my_abspath, expected->my_abspath); + ASSERT_STR_EQ(actual->merged_file, expected->merged_file); + ASSERT_INT_EQ(actual->operation, expected->operation); + SVN_ERR(compare_version(actual->src_left_version, + expected->src_left_version)); + SVN_ERR(compare_version(actual->src_right_version, + expected->src_right_version)); + return SVN_NO_ERROR; +} + +/* Assert that a file contains the expected data. Return an + * error if not. */ +static svn_error_t * +compare_file_content(const char *file_abspath, + const char *expected_val, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *actual_val; + + SVN_ERR(svn_stringbuf_from_file2(&actual_val, file_abspath, scratch_pool)); + ASSERT_STR_EQ(actual_val->data, expected_val); + return SVN_NO_ERROR; +} + +/* Assert that ACTUAL and EXPECTED both represent the same property + * conflict, or are both NULL. Return an error if not. + * + * Compare the property values found in files named by + * ACTUAL->base_abspath, ACTUAL->my_abspath, ACTUAL->merged_abspath + * with EXPECTED_BASE_VAL, EXPECTED_MY_VAL, EXPECTED_THEIR_VAL + * respectively, ignoring the corresponding fields in EXPECTED. */ +static svn_error_t * +compare_prop_conflict(const svn_wc_conflict_description2_t *actual, + const svn_wc_conflict_description2_t *expected, + const char *expected_base_val, + const char *expected_my_val, + const char *expected_their_val, + apr_pool_t *scratch_pool) +{ + if (actual == NULL && expected == NULL) + return SVN_NO_ERROR; + + SVN_TEST_ASSERT(actual && expected); + ASSERT_INT_EQ(actual->kind, svn_wc_conflict_kind_property); + ASSERT_INT_EQ(expected->kind, svn_wc_conflict_kind_property); + + ASSERT_STR_EQ(actual->local_abspath, expected->local_abspath); + ASSERT_INT_EQ(actual->node_kind, expected->node_kind); + ASSERT_STR_EQ(actual->property_name, expected->property_name); + ASSERT_INT_EQ(actual->action, expected->action); + ASSERT_INT_EQ(actual->reason, expected->reason); + ASSERT_INT_EQ(actual->operation, expected->operation); + SVN_ERR(compare_version(actual->src_left_version, + expected->src_left_version)); + SVN_ERR(compare_version(actual->src_right_version, + expected->src_right_version)); + + SVN_ERR(compare_file_content(actual->base_abspath, expected_base_val, + scratch_pool)); + SVN_ERR(compare_file_content(actual->my_abspath, expected_my_val, + scratch_pool)); + /* Historical wart: for a prop conflict, 'theirs' is in the 'merged_file' + * field, and the conflict artifact file is in the 'theirs_abspath' field. */ + SVN_ERR(compare_file_content(actual->merged_file, expected_their_val, + scratch_pool)); + /*ASSERT_STR_EQ(actual->theirs_abspath, conflict_artifact_file));*/ + + /* These are 'undefined' for a prop conflict */ + /*ASSERT_INT_EQ(actual->is_binary, expected->is_binary);*/ + /*ASSERT_STR_EQ(actual->mime_type, expected->mime_type);*/ + + return SVN_NO_ERROR; +} + +/* Create and return a tree conflict description */ +static svn_wc_conflict_description2_t * +tree_conflict_create(const char *local_abspath, + svn_node_kind_t node_kind, + svn_wc_operation_t operation, + svn_wc_conflict_action_t action, + svn_wc_conflict_reason_t reason, + const char *left_repo, + const char *left_path, + svn_revnum_t left_revnum, + svn_node_kind_t left_kind, + const char *right_repo, + const char *right_path, + svn_revnum_t right_revnum, + svn_node_kind_t right_kind, + apr_pool_t *result_pool) +{ + svn_wc_conflict_version_t *left, *right; + svn_wc_conflict_description2_t *conflict; + + left = svn_wc_conflict_version_create2(left_repo, NULL, left_path, + left_revnum, left_kind, result_pool); + right = svn_wc_conflict_version_create2(right_repo, NULL, right_path, + right_revnum, right_kind, + result_pool); + conflict = svn_wc_conflict_description_create_tree2( + local_abspath, node_kind, operation, + left, right, result_pool); + conflict->action = action; + conflict->reason = reason; + return conflict; +} + +static svn_error_t * +test_deserialize_tree_conflict(apr_pool_t *pool) +{ + const svn_wc_conflict_description2_t *conflict; + svn_wc_conflict_description2_t *exp_conflict; + const char *tree_conflict_data; + const char *local_abspath; + const svn_skel_t *skel; + + tree_conflict_data = "(conflict Foo.c file update deleted edited " + "(version 0 2 -1 0 0 ) (version 0 2 -1 0 0 ))"; + + SVN_ERR(svn_dirent_get_absolute(&local_abspath, "Foo.c", pool)); + exp_conflict = svn_wc_conflict_description_create_tree2( + local_abspath, svn_node_file, svn_wc_operation_update, + NULL, NULL, pool); + exp_conflict->action = svn_wc_conflict_action_delete; + exp_conflict->reason = svn_wc_conflict_reason_edited; + + skel = svn_skel__parse(tree_conflict_data, strlen(tree_conflict_data), pool); + SVN_ERR(svn_wc__deserialize_conflict(&conflict, skel, "", pool, pool)); + + if ((conflict->node_kind != exp_conflict->node_kind) || + (conflict->action != exp_conflict->action) || + (conflict->reason != exp_conflict->reason) || + (conflict->operation != exp_conflict->operation) || + (strcmp(conflict->local_abspath, exp_conflict->local_abspath) != 0)) + return fail(pool, "Unexpected tree conflict"); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_serialize_tree_conflict_data(apr_pool_t *pool) +{ + svn_wc_conflict_description2_t *conflict; + const char *tree_conflict_data; + const char *expected; + const char *local_abspath; + svn_skel_t *skel; + + SVN_ERR(svn_dirent_get_absolute(&local_abspath, "Foo.c", pool)); + + conflict = svn_wc_conflict_description_create_tree2( + local_abspath, svn_node_file, svn_wc_operation_update, + NULL, NULL, pool); + conflict->action = svn_wc_conflict_action_delete; + conflict->reason = svn_wc_conflict_reason_edited; + + SVN_ERR(svn_wc__serialize_conflict(&skel, conflict, pool, pool)); + tree_conflict_data = svn_skel__unparse(skel, pool)->data; + + expected = "(conflict Foo.c file update deleted edited " + "(version 0 2 -1 0 0 ) (version 0 2 -1 0 0 ))"; + + if (strcmp(expected, tree_conflict_data) != 0) + return fail(pool, "Unexpected text from tree conflict\n" + " Expected: %s\n" + " Actual: %s\n", expected, tree_conflict_data); + + return SVN_NO_ERROR; +} + +/* Test WC-DB-level conflict APIs. Especially tree conflicts. */ +static svn_error_t * +test_read_write_tree_conflicts(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t sbox; + + const char *parent_abspath; + const char *child1_abspath, *child2_abspath; + svn_wc_conflict_description2_t *conflict1, *conflict2; + + SVN_ERR(svn_test__sandbox_create(&sbox, "read_write_tree_conflicts", opts, pool)); + parent_abspath = svn_dirent_join(sbox.wc_abspath, "A", pool); + SVN_ERR(svn_wc__db_op_add_directory(sbox.wc_ctx->db, parent_abspath, + NULL /*props*/, NULL, pool)); + child1_abspath = svn_dirent_join(parent_abspath, "foo", pool); + child2_abspath = svn_dirent_join(parent_abspath, "bar", pool); + + conflict1 = tree_conflict_create(child1_abspath, svn_node_file, + svn_wc_operation_merge, + svn_wc_conflict_action_delete, + svn_wc_conflict_reason_edited, + "dummy://localhost", "path/to/foo", + 51, svn_node_file, + "dummy://localhost", "path/to/foo", + 52, svn_node_none, + pool); + + conflict2 = tree_conflict_create(child2_abspath, svn_node_dir, + svn_wc_operation_merge, + svn_wc_conflict_action_replace, + svn_wc_conflict_reason_edited, + "dummy://localhost", "path/to/bar", + 51, svn_node_dir, + "dummy://localhost", "path/to/bar", + 52, svn_node_file, + pool); + + /* Write */ + SVN_ERR(svn_wc__add_tree_conflict(sbox.wc_ctx, /*child1_abspath,*/ + conflict1, pool)); + SVN_ERR(svn_wc__add_tree_conflict(sbox.wc_ctx, /*child2_abspath,*/ + conflict2, pool)); + + /* Query (conflict1 through WC-DB API, conflict2 through WC API) */ + { + svn_boolean_t text_c, prop_c, tree_c; + + SVN_ERR(svn_wc__internal_conflicted_p(&text_c, &prop_c, &tree_c, + sbox.wc_ctx->db, child1_abspath, pool)); + SVN_TEST_ASSERT(tree_c); + SVN_TEST_ASSERT(! text_c && ! prop_c); + + SVN_ERR(svn_wc_conflicted_p3(&text_c, &prop_c, &tree_c, + sbox.wc_ctx, child2_abspath, pool)); + SVN_TEST_ASSERT(tree_c); + SVN_TEST_ASSERT(! text_c && ! prop_c); + } + + /* Read conflicts back */ + { + const svn_wc_conflict_description2_t *read_conflict; + + SVN_ERR(svn_wc__get_tree_conflict(&read_conflict, sbox.wc_ctx, + child1_abspath, pool, pool)); + SVN_ERR(compare_conflict(read_conflict, conflict1)); + + SVN_ERR(svn_wc__get_tree_conflict(&read_conflict, sbox.wc_ctx, + child2_abspath, pool, pool)); + SVN_ERR(compare_conflict(read_conflict, conflict2)); + } + + /* Read many */ + { + const apr_array_header_t *victims; + + SVN_ERR(svn_wc__db_read_conflict_victims(&victims, + sbox.wc_ctx->db, parent_abspath, + pool, pool)); + SVN_TEST_ASSERT(victims->nelts == 2); + } + + /* ### TODO: to test... + * svn_wc__db_read_conflicts + * svn_wc__node_get_conflict_info + * svn_wc__del_tree_conflict + */ + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_serialize_prop_conflict(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t sbox; + svn_skel_t *conflict_skel; + svn_boolean_t complete; + + SVN_ERR(svn_test__sandbox_create(&sbox, "test_serialize_prop_conflict", opts, pool)); + + conflict_skel = svn_wc__conflict_skel_create(pool); + + SVN_TEST_ASSERT(conflict_skel != NULL); + SVN_TEST_ASSERT(svn_skel__list_length(conflict_skel) == 2); + + SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel)); + SVN_TEST_ASSERT(!complete); /* Nothing set */ + + { + apr_hash_t *mine = apr_hash_make(pool); + apr_hash_t *their_old = apr_hash_make(pool); + apr_hash_t *theirs = apr_hash_make(pool); + apr_hash_t *conflicts = apr_hash_make(pool); + const char *marker_abspath; + + apr_hash_set(mine, "prop", APR_HASH_KEY_STRING, + svn_string_create("Mine", pool)); + + apr_hash_set(their_old, "prop", APR_HASH_KEY_STRING, + svn_string_create("Their-Old", pool)); + + apr_hash_set(theirs, "prop", APR_HASH_KEY_STRING, + svn_string_create("Theirs", pool)); + + apr_hash_set(conflicts, "prop", APR_HASH_KEY_STRING, ""); + + SVN_ERR(svn_io_open_unique_file3(NULL, &marker_abspath, sbox.wc_abspath, + svn_io_file_del_on_pool_cleanup, pool, + pool)); + + SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflict_skel, + sbox.wc_ctx->db, + sbox.wc_abspath, + marker_abspath, + mine, their_old, + theirs, conflicts, + pool, pool)); + } + + SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel)); + SVN_TEST_ASSERT(!complete); /* Misses operation */ + + SVN_ERR(svn_wc__conflict_skel_set_op_update( + conflict_skel, + svn_wc_conflict_version_create2("http://my-repos/svn", + "uuid", "trunk", 12, + svn_node_dir, pool), + NULL /* wc_only */, + pool, pool)); + + SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel)); + SVN_TEST_ASSERT(complete); /* Everything available */ + + { + apr_hash_t *mine; + apr_hash_t *their_old; + apr_hash_t *theirs; + apr_hash_t *conflicts; + const char *marker_abspath; + svn_string_t *v; + + SVN_ERR(svn_wc__conflict_read_prop_conflict(&marker_abspath, + &mine, + &their_old, + &theirs, + &conflicts, + sbox.wc_ctx->db, + sbox.wc_abspath, + conflict_skel, + pool, pool)); + + SVN_TEST_ASSERT(svn_dirent_is_ancestor(sbox.wc_abspath, marker_abspath)); + + v = apr_hash_get(mine, "prop", APR_HASH_KEY_STRING); + SVN_TEST_STRING_ASSERT(v->data, "Mine"); + + v = apr_hash_get(their_old, "prop", APR_HASH_KEY_STRING); + SVN_TEST_STRING_ASSERT(v->data, "Their-Old"); + + v = apr_hash_get(theirs, "prop", APR_HASH_KEY_STRING); + SVN_TEST_STRING_ASSERT(v->data, "Theirs"); + + SVN_TEST_ASSERT(apr_hash_count(conflicts) == 1); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_serialize_text_conflict(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t sbox; + svn_skel_t *conflict_skel; + svn_boolean_t complete; + + SVN_ERR(svn_test__sandbox_create(&sbox, "test_serialize_text_conflict", opts, pool)); + + conflict_skel = svn_wc__conflict_skel_create(pool); + + SVN_ERR(svn_wc__conflict_skel_add_text_conflict( + conflict_skel, + sbox.wc_ctx->db, sbox.wc_abspath, + svn_dirent_join(sbox.wc_abspath, "mine", pool), + svn_dirent_join(sbox.wc_abspath, "old-theirs", pool), + svn_dirent_join(sbox.wc_abspath, "theirs", pool), + pool, pool)); + + SVN_ERR(svn_wc__conflict_skel_set_op_merge( + conflict_skel, + svn_wc_conflict_version_create2("http://my-repos/svn", + "uuid", "trunk", 12, + svn_node_dir, pool), + svn_wc_conflict_version_create2("http://my-repos/svn", + "uuid", "branch/my", 8, + svn_node_dir, pool), + pool, pool)); + + SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel)); + SVN_TEST_ASSERT(complete); /* Everything available */ + + { + const char *mine_abspath; + const char *old_their_abspath; + const char *their_abspath; + + SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath, + &old_their_abspath, + &their_abspath, + sbox.wc_ctx->db, + sbox.wc_abspath, + conflict_skel, + pool, pool)); + + SVN_TEST_STRING_ASSERT( + svn_dirent_skip_ancestor(sbox.wc_abspath, mine_abspath), + "mine"); + + SVN_TEST_STRING_ASSERT( + svn_dirent_skip_ancestor(sbox.wc_abspath, old_their_abspath), + "old-theirs"); + + SVN_TEST_STRING_ASSERT( + svn_dirent_skip_ancestor(sbox.wc_abspath, their_abspath), + "theirs"); + } + + { + svn_wc_operation_t operation; + svn_boolean_t text_conflicted; + const apr_array_header_t *locs; + SVN_ERR(svn_wc__conflict_read_info(&operation, &locs, + &text_conflicted, NULL, NULL, + sbox.wc_ctx->db, sbox.wc_abspath, + conflict_skel, pool, pool)); + + SVN_TEST_ASSERT(text_conflicted); + SVN_TEST_ASSERT(operation == svn_wc_operation_merge); + + SVN_TEST_ASSERT(locs != NULL && locs->nelts == 2); + SVN_TEST_ASSERT(APR_ARRAY_IDX(locs, 0, svn_wc_conflict_version_t*) != NULL); + SVN_TEST_ASSERT(APR_ARRAY_IDX(locs, 1, svn_wc_conflict_version_t*) != NULL); + } + + { + const apr_array_header_t *markers; + const char *old_their_abspath; + const char *their_abspath; + const char *mine_abspath; + + SVN_ERR(svn_wc__conflict_read_markers(&markers, + sbox.wc_ctx->db, sbox.wc_abspath, + conflict_skel, pool, pool)); + + SVN_TEST_ASSERT(markers != NULL); + SVN_TEST_ASSERT(markers->nelts == 3); + + old_their_abspath = APR_ARRAY_IDX(markers, 0, const char *); + mine_abspath = APR_ARRAY_IDX(markers, 1, const char *); + their_abspath = APR_ARRAY_IDX(markers, 2, const char *); + + SVN_TEST_STRING_ASSERT( + svn_dirent_skip_ancestor(sbox.wc_abspath, mine_abspath), + "mine"); + + SVN_TEST_STRING_ASSERT( + svn_dirent_skip_ancestor(sbox.wc_abspath, old_their_abspath), + "old-theirs"); + + SVN_TEST_STRING_ASSERT( + svn_dirent_skip_ancestor(sbox.wc_abspath, their_abspath), + "theirs"); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_serialize_tree_conflict(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t sbox; + svn_skel_t *conflict_skel; + svn_boolean_t complete; + + SVN_ERR(svn_test__sandbox_create(&sbox, "test_serialize_tree_conflict", opts, pool)); + + conflict_skel = svn_wc__conflict_skel_create(pool); + + SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( + conflict_skel, + sbox.wc_ctx->db, sbox_wc_path(&sbox, "A/B"), + svn_wc_conflict_reason_moved_away, + svn_wc_conflict_action_delete, + sbox_wc_path(&sbox, "A/B"), + pool, pool)); + + SVN_ERR(svn_wc__conflict_skel_set_op_switch( + conflict_skel, + svn_wc_conflict_version_create2("http://my-repos/svn", + "uuid", "trunk", 12, + svn_node_dir, pool), + NULL /* wc_only */, + pool, pool)); + + SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel)); + SVN_TEST_ASSERT(complete); /* Everything available */ + + { + svn_wc_conflict_reason_t local_change; + svn_wc_conflict_action_t incoming_change; + const char *moved_away_op_root_abspath; + + SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, + &incoming_change, + &moved_away_op_root_abspath, + sbox.wc_ctx->db, + sbox.wc_abspath, + conflict_skel, + pool, pool)); + + SVN_TEST_ASSERT(local_change == svn_wc_conflict_reason_moved_away); + SVN_TEST_ASSERT(incoming_change == svn_wc_conflict_action_delete); + SVN_TEST_ASSERT(!strcmp(moved_away_op_root_abspath, + sbox_wc_path(&sbox, "A/B"))); + } + + return SVN_NO_ERROR; +} + +/* A conflict resolver callback baton for test_prop_conflicts(). */ +typedef struct test_prop_conflict_baton_t +{ + /* Sets of properties. */ + apr_hash_t *mine; + apr_hash_t *their_old; + apr_hash_t *theirs; + /* The set of prop names in conflict. */ + apr_hash_t *conflicts; + + /* We use all the fields of DESC except the base/theirs/mine/merged paths. */ + svn_wc_conflict_description2_t *desc; + + int conflicts_seen; +} test_prop_conflict_baton_t; + +/* Set *CONFLICT_SKEL_P to a new property conflict skel reflecting the + * conflict details given in B. */ +static svn_error_t * +create_prop_conflict_skel(svn_skel_t **conflict_skel_p, + svn_wc_context_t *wc_ctx, + const test_prop_conflict_baton_t *b, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_skel_t *conflict_skel = svn_wc__conflict_skel_create(result_pool); + const char *marker_abspath; + svn_boolean_t complete; + + SVN_ERR(svn_io_write_unique(&marker_abspath, + b->desc->local_abspath, + "conflict-artifact-file-content\n", 6, + svn_io_file_del_none, scratch_pool)); + + SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflict_skel, + wc_ctx->db, + b->desc->local_abspath, + marker_abspath, + b->mine, b->their_old, + b->theirs, b->conflicts, + result_pool, scratch_pool)); + + switch (b->desc->operation) + { + case svn_wc_operation_update: + SVN_ERR(svn_wc__conflict_skel_set_op_update( + conflict_skel, + b->desc->src_left_version, b->desc->src_right_version, + result_pool, scratch_pool)); + break; + case svn_wc_operation_switch: + SVN_ERR(svn_wc__conflict_skel_set_op_switch( + conflict_skel, + b->desc->src_left_version, b->desc->src_right_version, + result_pool, scratch_pool)); + break; + case svn_wc_operation_merge: + SVN_ERR(svn_wc__conflict_skel_set_op_merge( + conflict_skel, + b->desc->src_left_version, b->desc->src_right_version, + result_pool, scratch_pool)); + break; + default: + SVN_ERR_MALFUNCTION(); + } + + SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel)); + SVN_TEST_ASSERT(complete); + *conflict_skel_p = conflict_skel; + return SVN_NO_ERROR; +} + +/* A conflict resolver callback for test_prop_conflicts(), that checks + * that the conflict described to it matches the one described in BATON, + * and also counts the number of times it is called. */ +static svn_error_t * +prop_conflict_cb(svn_wc_conflict_result_t **result_p, + const svn_wc_conflict_description2_t *desc, + void *baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + test_prop_conflict_baton_t *b = baton; + + SVN_ERR(compare_prop_conflict( + desc, b->desc, + svn_prop_get_value(b->their_old, desc->property_name), + svn_prop_get_value(b->mine, desc->property_name), + svn_prop_get_value(b->theirs, desc->property_name), + scratch_pool)); + b->conflicts_seen++; + + *result_p = svn_wc_create_conflict_result(svn_wc_conflict_choose_postpone, + NULL /*merged_file*/, result_pool); + return SVN_NO_ERROR; +} + +/* Test for correct retrieval of property conflict descriptions from + * the WC DB. + * + * Presently it tests just one prop conflict, and only during the + * 'resolve' operation. We should also test during the 'update'/ + * 'switch'/'merge' operations. + */ +static svn_error_t * +test_prop_conflicts(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t sbox; + svn_skel_t *conflict_skel; + svn_error_t *err; + const char *lock_abspath; + test_prop_conflict_baton_t *b = apr_pcalloc(pool, sizeof(*b)); + svn_wc_conflict_description2_t *desc = apr_pcalloc(pool, sizeof(*desc)); + + SVN_ERR(svn_test__sandbox_create(&sbox, "test_prop_conflicts", opts, pool)); + + /* Describe a property conflict */ + b->mine = apr_hash_make(pool); + b->their_old = apr_hash_make(pool); + b->theirs = apr_hash_make(pool); + b->conflicts = apr_hash_make(pool); + svn_hash_sets(b->mine, "prop", svn_string_create("Mine", pool)); + svn_hash_sets(b->their_old, "prop", svn_string_create("Their-Old", pool)); + svn_hash_sets(b->theirs, "prop", svn_string_create("Theirs", pool)); + svn_hash_sets(b->conflicts, "prop", ""); + + b->desc = desc; + desc->local_abspath = sbox.wc_abspath; + desc->kind = svn_wc_conflict_kind_property; + desc->node_kind = svn_node_dir; + desc->operation = svn_wc_operation_update; + desc->action = svn_wc_conflict_action_edit; + desc->reason = svn_wc_conflict_reason_edited; + desc->mime_type = NULL; + desc->is_binary = FALSE; + desc->property_name = "prop"; + desc->src_left_version + = svn_wc_conflict_version_create2(sbox.repos_url, "uuid", + "trunk", 12, svn_node_dir, pool); + desc->src_right_version = NULL; /* WC only */ + + b->conflicts_seen = 0; + + /* Record a conflict */ + { + apr_pool_t *subpool = svn_pool_create(pool); + SVN_ERR(create_prop_conflict_skel(&conflict_skel, sbox.wc_ctx, b, + pool, subpool)); + svn_pool_clear(subpool); + SVN_ERR(svn_wc__db_op_mark_conflict(sbox.wc_ctx->db, + sbox.wc_abspath, + conflict_skel, NULL, subpool)); + svn_pool_destroy(subpool); + } + + /* Test the API for resolving the conflict: check that correct details + * of the conflict are returned. */ + SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, sbox.wc_ctx, + sbox.wc_abspath, pool, pool)); + err = svn_wc__resolve_conflicts(sbox.wc_ctx, sbox.wc_abspath, + svn_depth_empty, + FALSE /* resolve_text */, + "" /* resolve_prop (ALL props) */, + FALSE /* resolve_tree */, + svn_wc_conflict_choose_unspecified, + prop_conflict_cb, b, + NULL, NULL, /* cancellation */ + NULL, NULL, /* notification */ + pool); + + SVN_ERR(svn_error_compose_create(err, + svn_wc__release_write_lock(sbox.wc_ctx, + lock_abspath, + pool))); + + ASSERT_INT_EQ(b->conflicts_seen, 1); + return SVN_NO_ERROR; +} + +/* The test table. */ + +struct svn_test_descriptor_t test_funcs[] = + { + SVN_TEST_NULL, + SVN_TEST_PASS2(test_deserialize_tree_conflict, + "deserialize tree conflict"), + SVN_TEST_PASS2(test_serialize_tree_conflict_data, + "serialize tree conflict data"), + SVN_TEST_OPTS_PASS(test_read_write_tree_conflicts, + "read and write tree conflict data"), + SVN_TEST_OPTS_PASS(test_serialize_prop_conflict, + "read and write a property conflict"), + SVN_TEST_OPTS_PASS(test_serialize_text_conflict, + "read and write a text conflict"), + SVN_TEST_OPTS_PASS(test_serialize_tree_conflict, + "read and write a tree conflict"), + SVN_TEST_OPTS_PASS(test_prop_conflicts, + "test prop conflicts"), + SVN_TEST_NULL + }; + diff --git a/subversion/tests/libsvn_wc/create_wc_for_upgrade.sh b/subversion/tests/libsvn_wc/create_wc_for_upgrade.sh index 10ded2e..622595c 100755 --- a/subversion/tests/libsvn_wc/create_wc_for_upgrade.sh +++ b/subversion/tests/libsvn_wc/create_wc_for_upgrade.sh @@ -83,10 +83,10 @@ echo epsilon > epsilon ### row is created). # a file with just .working -### what comes after epsilon?? -echo lambda > lambda -"${SVN}" add lambda -"${SVN}" propset l-prop l-value lambda +# zeta = epsilon+1 +echo zeta > zeta +"${SVN}" add zeta +"${SVN}" propset z-prop z-value zeta # a file with .base and .working "${SVN}" propset b-more b-value2 beta diff --git a/subversion/tests/libsvn_wc/db-test.c b/subversion/tests/libsvn_wc/db-test.c index 462fcb7..45e9c4d 100644 --- a/subversion/tests/libsvn_wc/db-test.c +++ b/subversion/tests/libsvn_wc/db-test.c @@ -97,91 +97,91 @@ static const char * const TESTING_DATA = ( "insert into nodes values (" " 1, '', 0, null, 1, '', 1, 'normal'," " null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'A', 0, '', 1, 'A', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 10, null, null, null);" + " 10, null, null, null, null);" "insert into nodes values (" " 1, 'B', 0, '', 1, 'B', null, 'excluded'," " null, null, 'symlink', null, null, null, null, null, null, null," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" - " 1, 'C', 0, '', 1, 'C', null, 'absent'," + " 1, 'C', 0, '', 1, 'C', null, 'server-excluded'," " null, null, 'unknown', null, null, null, null, null, null, null," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'D', 0, '', 1, 'D', null, 'not-present'," " null, null, 'unknown', null, null, null, null, null, null, null," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'E', 0, '', 1, 'E', null, 'incomplete'," " null, null, 'unknown', null, null, null, null, null, null, null," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'F', 0, '', 1, 'F', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 15, null, null, null);" + " 15, null, null, null, null);" "insert into nodes values (" " 1, 'G', 0, '', 2, 'G-alt', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "'," - " 15, null, null, null);" + " 15, null, null, null, null);" "insert into nodes values (" " 1, 'H', 0, '', 1, 'H', 1, 'normal'," " null, null, 'symlink', '()', null, null, 'H-target', 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'I', 0, '', 1, 'I', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J', 0, '', 1, 'J', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e', 0, 'J', 1, 'J/J-e', 1, 'normal'," - " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, 'other/place', 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-a', 0, 'J/J-e', 1, 'J/J-e/J-e-a', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 15, null, null, null);" + " 15, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-b', 0, 'J/J-e', 1, 'J/J-e/J-e-b', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-b/Jeba', 0, 'J/J-e/J-e-b', 1, 'J/J-e/J-e-b/Jeba', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 15, null, null, null);" + " 15, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-f', 0, 'J', 1, 'J/J-f', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-f/J-f-a', 0, 'J/J-f', 1, 'J/J-f/J-f-a', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'K', 0, '', 1, 'K', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'K/K-a', 0, 'K', 1, 'K/K-a', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 15, null, null, null);" + " 15, null, null, null, null);" "insert into nodes values (" " 1, 'K/K-b', 0, 'K', 1, 'K/K-b', 1, 'normal'," - " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 15, null, null, null);" + " null, 'moved/away', 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," + " 15, null, null, null, null);" "" /* Load data into NODES table; ### op_depths have not been calculated by me yet; the value 1 is just 'good enough' to make the nodes WORKING nodes. */ "insert into nodes values (" " 1, 'I', 1, '', 2, 'some/dir', 2, 'normal'," - " 0, null, 'dir', '()', 'immediates', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," + " null, null, null, null, null);" /* I'm not sure what the working J is supposed to represent. It replaces the base J, but is it a copy or not? It has no @@ -189,112 +189,136 @@ static const char * const TESTING_DATA = ( implies they are children of a copied J. */ "insert into nodes values (" " 1, 'J', 1, '', null, null, null, 'normal'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-a', 1, 'J', null, null, null, 'normal'," - " 0, null, 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-b', 2, 'J', 2, 'some/dir', 2, 'normal'," - " 0, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," - " null, null, null, null);" + " null, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-b/J-b-a', 3, 'J/J-b', 2, 'another/dir', 2, 'normal'," - " 0, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," - " null, null, null, null);" + " null, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-b/J-b-b', 2, 'J/J-b', null, null, 2, 'normal'," - " 0, null, 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-c', 1, 'J', null, null, null, 'normal'," - " 0, null, 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-c/J-c-a', 1, 'J/J-c', null, null, null, 'normal'," - " 0, null, 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-c', 2, 'J', null, null, null, 'base-deleted'," - " 0, null, 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-c/J-c-a', 2, 'J/J-c', null, null, null, 'base-deleted'," - " 0, null, 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-d', 2, 'J', 2, 'moved/file', 2, 'normal'," " 1, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "'," - " 10, null, null, null);" + " 10, null, null, null, null);" + "insert into nodes values (" + " 1, 'moved/file', 0, 'moved', 2, 'moved/file', 2, 'normal'," + " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "'," + " 10, null, null, null, null);" + "insert into nodes values (" + " 1, 'moved/file', 2, 'moved', 2, 'moved/file', 2, 'base-deleted'," + " null, 'J/J-d', 'file', '()', null, null, null, null, null, null," + " 10, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e', 1, 'J', null, null, null, 'normal'," - " 0, 'other/place', 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-a', 1, 'J/J-e', null, null, null, 'normal'," - " 0, null, 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-b', 1, 'J/J-e', null, null, null, 'normal'," - " 0, null, 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e', 2, 'J', null, null, null, 'base-deleted'," - " 0, 'other/place', 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-a', 2, 'J/J-e', null, null, null, 'base-deleted'," - " 0, null, 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-b', 2, 'J/J-e', null, null, null, 'base-deleted'," - " 0, null, 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-b/Jeba', 1, 'J/J-e/J-e-b', null, null, null, 'base-deleted'," - " 0, null, 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-f', 1, 'J', null, null, null, 'normal'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-f/J-f-a', 1, 'J/J-f', null, null, null, 'base-deleted'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'K', 1, '', null, null, null, 'base-deleted'," - " 0, null, 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'K/K-a', 1, 'K', null, null, null, 'base-deleted'," - " 0, null, 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'K/K-b', 1, 'K', null, null, null, 'base-deleted'," - " 0, 'moved/away', 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'L', 1, '', null, null, null, 'normal'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'L/L-a', 1, 'L', null, null, null, 'normal'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'L/L-a/L-a-a', 1, 'L/L-a', null, null, null, 'normal'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'L/L-a', 2, 'L', null, null, null, 'base-deleted'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'L/L-a/L-a-a', 2, 'L/L-a', null, null, null, 'base-deleted'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" + "insert into nodes values (" + " 1, 'other/place', 2, 'other', null, null, null, 'normal'," + " 1, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" + "insert into nodes values (" + " 1, 'other/place/J-e-a', 2, 'other/place', null, null, null, 'normal'," + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" + "insert into nodes values (" + " 1, 'other/place/J-e-b', 2, 'other/place', null, null, null, 'normal'," + " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," + " null, null, null, null, null);" + "insert into nodes values (" + " 1, 'other/place/J-e-b/Jeba', 0, 'other/place/J-e-b', null, null, null, 'normal'," + " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," + " 15, null, null, null, null);" "insert into actual_node values (" " 1, 'I', '', null, null, null, null, null, 'changelist', null, " " null, null, null, null, null);" @@ -316,9 +340,11 @@ create_open(svn_wc__db_t **db, SVN_ERR(svn_dirent_get_absolute(local_abspath, svn_dirent_join("fake-wc", subdir, pool), pool)); - SVN_ERR(svn_wc__db_open(db, NULL, TRUE, TRUE, pool, pool)); + SVN_ERR(svn_wc__db_open(db, NULL, FALSE, TRUE, pool, pool)); SVN_ERR(svn_test__create_fake_wc(*local_abspath, TESTING_DATA, pool, pool)); + svn_test_add_dir_cleanup(*local_abspath); + return SVN_NO_ERROR; } @@ -354,7 +380,7 @@ static svn_error_t * test_getting_info(apr_pool_t *pool) { const char *local_abspath; - svn_wc__db_kind_t kind; + svn_node_kind_t kind; svn_wc__db_status_t status; svn_revnum_t revision; const char *repos_relpath; @@ -367,6 +393,7 @@ test_getting_info(apr_pool_t *pool) const svn_checksum_t *checksum; const char *target; svn_boolean_t had_props; + apr_hash_t *props; svn_boolean_t update_root; svn_wc__db_lock_t *lock; svn_wc__db_t *db; @@ -379,11 +406,11 @@ test_getting_info(apr_pool_t *pool) &status, &kind, &revision, &repos_relpath, &repos_root_url, &repos_uuid, &changed_rev, &changed_date, &changed_author,&depth, &checksum, - &target, &lock, &had_props, + &target, &lock, &had_props, &props, &update_root, db, local_abspath, pool, pool)); - SVN_TEST_ASSERT(kind == svn_wc__db_kind_dir); + SVN_TEST_ASSERT(kind == svn_node_dir); SVN_TEST_ASSERT(status == svn_wc__db_status_normal); SVN_TEST_ASSERT(revision == 1); SVN_TEST_STRING_ASSERT(repos_relpath, ""); @@ -396,6 +423,9 @@ test_getting_info(apr_pool_t *pool) SVN_TEST_ASSERT(checksum == NULL); SVN_TEST_ASSERT(target == NULL); SVN_TEST_ASSERT(lock == NULL); + SVN_TEST_ASSERT(!had_props); + SVN_TEST_ASSERT(apr_hash_count(props) == 0); + /* SVN_TEST_ASSERT(update_root == ???); */ /* Test: file-specific values. */ SVN_ERR(svn_wc__db_base_get_info( @@ -403,10 +433,10 @@ test_getting_info(apr_pool_t *pool) &repos_relpath, &repos_root_url, &repos_uuid, NULL, NULL, NULL, NULL, &checksum, NULL, NULL, - NULL, NULL, + NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "A", pool), pool, pool)); - SVN_TEST_ASSERT(kind == svn_wc__db_kind_file); + SVN_TEST_ASSERT(kind == svn_node_file); SVN_TEST_STRING_ASSERT(SHA1_1, svn_checksum_to_cstring(checksum, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "A"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); @@ -418,10 +448,10 @@ test_getting_info(apr_pool_t *pool) &repos_relpath, &repos_root_url, &repos_uuid, &changed_rev, &changed_date, &changed_author, &depth, &checksum, &target, &lock, - NULL, NULL, + NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "B", pool), pool, pool)); - SVN_TEST_ASSERT(kind == svn_wc__db_kind_symlink); + SVN_TEST_ASSERT(kind == svn_node_symlink); SVN_TEST_ASSERT(status == svn_wc__db_status_excluded); SVN_TEST_ASSERT(!SVN_IS_VALID_REVNUM(revision)); SVN_TEST_STRING_ASSERT(repos_relpath, "B"); @@ -435,16 +465,16 @@ test_getting_info(apr_pool_t *pool) SVN_TEST_ASSERT(target == NULL); SVN_TEST_ASSERT(lock == NULL); - /* Test: unknown kind, absent presence. */ + /* Test: unknown kind, server-excluded presence. */ SVN_ERR(svn_wc__db_base_get_info( &status, &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, + NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "C", pool), pool, pool)); - SVN_TEST_ASSERT(kind == svn_wc__db_kind_unknown); + SVN_TEST_ASSERT(kind == svn_node_unknown); SVN_TEST_ASSERT(status == svn_wc__db_status_server_excluded); /* Test: not-present presence. */ @@ -453,7 +483,7 @@ test_getting_info(apr_pool_t *pool) NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, + NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "D", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_not_present); @@ -464,7 +494,7 @@ test_getting_info(apr_pool_t *pool) NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, + NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "E", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_incomplete); @@ -475,7 +505,7 @@ test_getting_info(apr_pool_t *pool) NULL, NULL, NULL, NULL, NULL, NULL, NULL, &checksum, NULL, - NULL, NULL, NULL, + NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "F", pool), pool, pool)); SVN_TEST_STRING_ASSERT(SHA1_1, @@ -487,7 +517,7 @@ test_getting_info(apr_pool_t *pool) &repos_relpath, &repos_root_url, &repos_uuid, &changed_rev, &changed_date, &changed_author, NULL, NULL, NULL, - NULL, NULL, NULL, + NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "G", pool), pool, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "G-alt"); @@ -503,7 +533,7 @@ test_getting_info(apr_pool_t *pool) NULL, NULL, NULL, NULL, NULL, NULL, NULL, &checksum, &target, - NULL, NULL, NULL, + NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "H", pool), pool, pool)); SVN_TEST_ASSERT(checksum == NULL); @@ -515,11 +545,10 @@ test_getting_info(apr_pool_t *pool) NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, + NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "missing-file", pool), pool, pool); - SVN_TEST_ASSERT(err != NULL && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_PATH_NOT_FOUND); return SVN_NO_ERROR; } @@ -529,12 +558,12 @@ static svn_error_t * validate_node(svn_wc__db_t *db, const char *local_abspath, const char *relpath, - svn_wc__db_kind_t expected_kind, + svn_node_kind_t expected_kind, svn_wc__db_status_t expected_status, apr_pool_t *scratch_pool) { const char *path = svn_dirent_join(local_abspath, relpath, scratch_pool); - svn_wc__db_kind_t kind; + svn_node_kind_t kind; svn_wc__db_status_t status; apr_hash_t *props; const svn_string_t *value; @@ -544,14 +573,12 @@ validate_node(svn_wc__db_t *db, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, + NULL, NULL, NULL, NULL, db, path, scratch_pool, scratch_pool)); SVN_TEST_ASSERT(kind == expected_kind); SVN_TEST_ASSERT(status == expected_status); - SVN_ERR(svn_wc__db_base_get_props(&props, db, path, - scratch_pool, scratch_pool)); switch (status) { case svn_wc__db_status_server_excluded: @@ -559,13 +586,18 @@ validate_node(svn_wc__db_t *db, case svn_wc__db_status_incomplete: case svn_wc__db_status_not_present: /* Our tests aren't setting properties on these node types, so - short-circuit examination of name/value pairs. */ + short-circuit examination of name/value pairs, to avoid having + to handle the error from svn_wc__db_base_get_props(). */ return SVN_NO_ERROR; - default: - SVN_TEST_ASSERT(props != NULL); + break; /* Fall through */ } + SVN_ERR(svn_wc__db_base_get_props(&props, db, path, + scratch_pool, scratch_pool)); + + SVN_TEST_ASSERT(props != NULL); + value = apr_hash_get(props, "p1", APR_HASH_KEY_STRING); SVN_TEST_STRING_ASSERT(value->data, "v1"); @@ -628,7 +660,7 @@ test_inserting_nodes(apr_pool_t *pool) props, 1, TIME_1a, AUTHOR_1, children, svn_depth_infinity, - NULL, NULL, FALSE, NULL, NULL, + NULL, NULL, FALSE, NULL, NULL, NULL, pool)); /* Replace an incomplete node with a file node. */ @@ -640,7 +672,8 @@ test_inserting_nodes(apr_pool_t *pool) props, 1, TIME_1a, AUTHOR_1, checksum, - NULL, NULL, FALSE, NULL, FALSE, FALSE, NULL, + NULL, FALSE, FALSE, NULL, NULL, FALSE, FALSE, + NULL, NULL, pool)); /* Create a new symlink node. */ @@ -652,14 +685,15 @@ test_inserting_nodes(apr_pool_t *pool) props, 1, TIME_1a, AUTHOR_1, "O-target", - NULL, NULL, FALSE, NULL, NULL, + NULL, FALSE, FALSE, NULL, NULL, FALSE, FALSE, + NULL, NULL, pool)); - /* Replace an incomplete node with an absent file node. */ + /* Replace an incomplete node with an server-excluded file node. */ SVN_ERR(svn_wc__db_base_add_excluded_node( db, svn_dirent_join(local_abspath, "N/N-b", pool), "N/N-b", ROOT_ONE, UUID_ONE, 3, - svn_wc__db_kind_file, svn_wc__db_status_server_excluded, + svn_node_file, svn_wc__db_status_server_excluded, NULL, NULL, pool)); @@ -667,7 +701,7 @@ test_inserting_nodes(apr_pool_t *pool) SVN_ERR(svn_wc__db_base_add_excluded_node( db, svn_dirent_join(local_abspath, "P", pool), "P", ROOT_ONE, UUID_ONE, 3, - svn_wc__db_kind_dir, svn_wc__db_status_excluded, + svn_node_dir, svn_wc__db_status_excluded, NULL, NULL, pool)); @@ -675,44 +709,44 @@ test_inserting_nodes(apr_pool_t *pool) SVN_ERR(svn_wc__db_base_add_not_present_node( db, svn_dirent_join(local_abspath, "Q", pool), "Q", ROOT_ONE, UUID_ONE, 3, - svn_wc__db_kind_symlink, + svn_node_symlink, NULL, NULL, pool)); - /* Create a new absent unknown-kind node. */ + /* Create a new server-excluded unknown-kind node. */ SVN_ERR(svn_wc__db_base_add_excluded_node( db, svn_dirent_join(local_abspath, "R", pool), "R", ROOT_ONE, UUID_ONE, 3, - svn_wc__db_kind_unknown, svn_wc__db_status_server_excluded, + svn_node_unknown, svn_wc__db_status_server_excluded, NULL, NULL, pool)); /* Are all the nodes where we expect them to be? */ SVN_ERR(validate_node(db, local_abspath, "N", - svn_wc__db_kind_dir, svn_wc__db_status_normal, + svn_node_dir, svn_wc__db_status_normal, pool)); SVN_ERR(validate_node(db, local_abspath, "N/N-a", - svn_wc__db_kind_file, svn_wc__db_status_normal, + svn_node_file, svn_wc__db_status_normal, pool)); SVN_ERR(validate_node(db, local_abspath, "N/N-b", - svn_wc__db_kind_file, + svn_node_file, svn_wc__db_status_server_excluded, pool)); SVN_ERR(validate_node(db, local_abspath, "N/N-c", - svn_wc__db_kind_unknown, svn_wc__db_status_incomplete, + svn_node_unknown, svn_wc__db_status_incomplete, pool)); SVN_ERR(validate_node(db, local_abspath, "O", - svn_wc__db_kind_symlink, svn_wc__db_status_normal, + svn_node_symlink, svn_wc__db_status_normal, pool)); SVN_ERR(validate_node(db, local_abspath, "P", - svn_wc__db_kind_dir, svn_wc__db_status_excluded, + svn_node_dir, svn_wc__db_status_excluded, pool)); SVN_ERR(validate_node(db, local_abspath, "Q", - svn_wc__db_kind_symlink, svn_wc__db_status_not_present, + svn_node_symlink, svn_wc__db_status_not_present, pool)); SVN_ERR(validate_node(db, local_abspath, "R", - svn_wc__db_kind_unknown, + svn_node_unknown, svn_wc__db_status_server_excluded, pool)); @@ -768,7 +802,7 @@ static svn_error_t * test_working_info(apr_pool_t *pool) { const char *local_abspath; - svn_wc__db_kind_t kind; + svn_node_kind_t kind; svn_wc__db_status_t status; svn_revnum_t revision; const char *repos_relpath; @@ -777,10 +811,10 @@ test_working_info(apr_pool_t *pool) svn_revnum_t changed_rev; apr_time_t changed_date; const char *changed_author; - apr_time_t last_mod_time; + apr_time_t recorded_time; svn_depth_t depth; const svn_checksum_t *checksum; - svn_filesize_t translated_size; + svn_filesize_t recorded_size; const char *target; const char *changelist; const char *original_repos_relpath; @@ -806,13 +840,13 @@ test_working_info(apr_pool_t *pool) &changed_rev, &changed_date, &changed_author, &depth, &checksum, &target, &original_repos_relpath, &original_root_url, &original_uuid, &original_revnum, - &lock, &translated_size, &last_mod_time, &changelist, + &lock, &recorded_size, &recorded_time, &changelist, &conflicted, &op_root, &had_props, &props_mod, &have_base, &have_more_work, &have_work, db, svn_dirent_join(local_abspath, "I", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_added); - SVN_TEST_ASSERT(kind == svn_wc__db_kind_dir); + SVN_TEST_ASSERT(kind == svn_node_dir); SVN_TEST_ASSERT(revision == SVN_INVALID_REVNUM); SVN_TEST_ASSERT(repos_relpath == NULL); SVN_TEST_ASSERT(repos_root_url == NULL); @@ -822,18 +856,22 @@ test_working_info(apr_pool_t *pool) SVN_TEST_STRING_ASSERT(changed_author, AUTHOR_2); SVN_TEST_ASSERT(depth == svn_depth_immediates); SVN_TEST_ASSERT(checksum == NULL); - SVN_TEST_ASSERT(translated_size == SVN_INVALID_FILESIZE); + SVN_TEST_ASSERT(recorded_size == SVN_INVALID_FILESIZE); SVN_TEST_ASSERT(target == NULL); SVN_TEST_STRING_ASSERT(changelist, "changelist"); SVN_TEST_STRING_ASSERT(original_repos_relpath, "some/dir"); SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO); SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO); SVN_TEST_ASSERT(original_revnum == 2); - SVN_TEST_ASSERT(props_mod == FALSE); - SVN_TEST_ASSERT(have_base == TRUE); - SVN_TEST_ASSERT(have_work == TRUE); - SVN_TEST_ASSERT(conflicted == FALSE); + SVN_TEST_ASSERT(!had_props); + SVN_TEST_ASSERT(!props_mod); + SVN_TEST_ASSERT(have_base); + /* SVN_TEST_ASSERT(have_more_work...); */ + SVN_TEST_ASSERT(have_work); + SVN_TEST_ASSERT(!conflicted); SVN_TEST_ASSERT(lock == NULL); + /* SVN_TEST_ASSERT(last_mod_time...); */ + /* SVN_TEST_ASSERT(op_root...); */ /* ### we need a hojillion more tests in here. I just want to get this @@ -859,14 +897,14 @@ test_pdh(apr_pool_t *pool) SVN_ERR(svn_wc__db_base_add_excluded_node( db, svn_dirent_join(local_abspath, "sub", pool), "sub", ROOT_ONE, UUID_ONE, 1, - svn_wc__db_kind_file, svn_wc__db_status_server_excluded, + svn_node_file, svn_wc__db_status_server_excluded, NULL, NULL, pool)); SVN_ERR(svn_wc__db_base_add_excluded_node( db, svn_dirent_join(local_abspath, "sub/A", pool), "sub/A", ROOT_ONE, UUID_ONE, 1, - svn_wc__db_kind_file, svn_wc__db_status_server_excluded, + svn_node_file, svn_wc__db_status_server_excluded, NULL, NULL, pool)); @@ -888,6 +926,10 @@ test_scan_addition(apr_pool_t *pool) const char *original_root_url; const char *original_uuid; svn_revnum_t original_revision; + const char *moved_from_abspath; + const char *move_op_root_abspath; + const char *move_op_root_src; + const char *delete_op_root_abspath; SVN_ERR(create_open(&db, &local_abspath, "test_scan_addition", pool)); @@ -935,9 +977,24 @@ test_scan_addition(apr_pool_t *pool) &original_revision, db, svn_dirent_join(local_abspath, "J/J-d", pool), pool, pool)); + SVN_ERR(svn_wc__db_scan_moved( + &moved_from_abspath, + &move_op_root_abspath, + &move_op_root_src, + &delete_op_root_abspath, + db, svn_dirent_join(local_abspath, "J/J-d", pool), + pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_moved_here); SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-d", op_root_abspath, pool)); + SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/file", + moved_from_abspath, pool)); + SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-d", + move_op_root_abspath, pool)); + SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/file", + move_op_root_src, pool)); + SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/file", + delete_op_root_abspath, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-d"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); @@ -1015,6 +1072,7 @@ test_scan_deletion(apr_pool_t *pool) const char *base_del_abspath; const char *work_del_abspath; const char *moved_to_abspath; + const char *copy_op_root_abspath; SVN_ERR(create_open(&db, &local_abspath, "test_scan_deletion", pool)); @@ -1023,34 +1081,41 @@ test_scan_deletion(apr_pool_t *pool) &base_del_abspath, &moved_to_abspath, &work_del_abspath, + ©_op_root_abspath, db, svn_dirent_join(local_abspath, "J/J-e", pool), pool, pool)); - SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e", + SVN_TEST_ASSERT(validate_abspath(local_abspath, "J", base_del_abspath, pool)); SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place", moved_to_abspath, pool)); SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e", work_del_abspath, pool)); + SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place", + copy_op_root_abspath, pool)); /* Node was moved elsewhere (child of operation root). */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, + ©_op_root_abspath, db, svn_dirent_join(local_abspath, "J/J-e/J-e-a", pool), pool, pool)); - SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e", + SVN_TEST_ASSERT(validate_abspath(local_abspath, "J", base_del_abspath, pool)); - SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place", + SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place/J-e-a", moved_to_abspath, pool)); SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e", work_del_abspath, pool)); + SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place", + copy_op_root_abspath, pool)); /* Root of delete. Parent is a WORKING node. */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, + NULL, db, svn_dirent_join(local_abspath, "J/J-c", pool), pool, pool)); /* Implicit delete of "J" (via replacement). */ @@ -1065,6 +1130,7 @@ test_scan_deletion(apr_pool_t *pool) &base_del_abspath, &moved_to_abspath, &work_del_abspath, + NULL, db, svn_dirent_join(local_abspath, "J/J-c/J-c-a", pool), pool, pool)); /* Implicit delete of "J" (via replacement). */ @@ -1079,14 +1145,15 @@ test_scan_deletion(apr_pool_t *pool) &base_del_abspath, &moved_to_abspath, &work_del_abspath, + NULL, db, svn_dirent_join(local_abspath, "J/J-e/J-e-b/Jeba", pool), pool, pool)); /* ### I don't understand this. "J/J-e/J-e-b/Jeba" is a deleted base node that is not overlayed by the replacement rooted at "J". Why does base_del_abspath refer to "J-e"? */ - SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-e", + SVN_TEST_ASSERT(validate_abspath(local_abspath, "J", base_del_abspath, pool)); - SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place", + SVN_TEST_ASSERT(validate_abspath(local_abspath, "other/place/J-e-b/Jeba", moved_to_abspath, pool)); SVN_TEST_ASSERT(work_del_abspath == NULL); @@ -1095,6 +1162,7 @@ test_scan_deletion(apr_pool_t *pool) &base_del_abspath, &moved_to_abspath, &work_del_abspath, + NULL, db, svn_dirent_join(local_abspath, "J/J-f/J-f-a", pool), pool, pool)); /* Implicit delete of "J" (via replacement). */ @@ -1108,6 +1176,7 @@ test_scan_deletion(apr_pool_t *pool) &base_del_abspath, &moved_to_abspath, &work_del_abspath, + NULL, db, svn_dirent_join(local_abspath, "K", pool), pool, pool)); SVN_TEST_ASSERT(validate_abspath(local_abspath, "K", @@ -1120,6 +1189,7 @@ test_scan_deletion(apr_pool_t *pool) &base_del_abspath, &moved_to_abspath, &work_del_abspath, + NULL, db, svn_dirent_join(local_abspath, "K/K-a", pool), pool, pool)); SVN_TEST_ASSERT(validate_abspath(local_abspath, "K", @@ -1132,12 +1202,15 @@ test_scan_deletion(apr_pool_t *pool) &base_del_abspath, &moved_to_abspath, &work_del_abspath, + ©_op_root_abspath, db, svn_dirent_join(local_abspath, "K/K-b", pool), pool, pool)); - SVN_TEST_ASSERT(validate_abspath(local_abspath, "K/K-b", + SVN_TEST_ASSERT(validate_abspath(local_abspath, "K", base_del_abspath, pool)); SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/away", moved_to_abspath, pool)); + SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/away", + copy_op_root_abspath, pool)); SVN_TEST_ASSERT(work_del_abspath == NULL); /* Subtree deletion of added tree. Start at child. */ @@ -1145,6 +1218,7 @@ test_scan_deletion(apr_pool_t *pool) &base_del_abspath, &moved_to_abspath, &work_del_abspath, + NULL, db, svn_dirent_join(local_abspath, "L/L-a/L-a-a", pool), pool, pool)); SVN_TEST_ASSERT(base_del_abspath == NULL); @@ -1157,6 +1231,7 @@ test_scan_deletion(apr_pool_t *pool) &base_del_abspath, &moved_to_abspath, &work_del_abspath, + NULL, db, svn_dirent_join(local_abspath, "L/L-a", pool), pool, pool)); SVN_TEST_ASSERT(base_del_abspath == NULL); @@ -1356,6 +1431,7 @@ test_externals_store(apr_pool_t *pool) "not-a-uuid", 12, props, + NULL, 10, 987654, "somebody", @@ -1368,6 +1444,7 @@ test_externals_store(apr_pool_t *pool) FALSE, NULL, FALSE, NULL, + NULL, pool)); SVN_ERR(svn_wc__db_external_add_dir(db, @@ -1384,7 +1461,7 @@ test_externals_store(apr_pool_t *pool) { svn_wc__db_status_t status; - svn_wc__db_kind_t kind; + svn_node_kind_t kind; const char *repos_root_url; const char *repos_uuid; const char *defining_abspath; @@ -1401,7 +1478,7 @@ test_externals_store(apr_pool_t *pool) pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_normal); - SVN_TEST_ASSERT(kind == svn_wc__db_kind_file); + SVN_TEST_ASSERT(kind == svn_node_file); SVN_TEST_STRING_ASSERT(repos_root_url, "svn://some-repos/svn"); SVN_TEST_STRING_ASSERT(repos_uuid, "not-a-uuid"); SVN_TEST_STRING_ASSERT(defining_abspath, subdir); @@ -1432,7 +1509,7 @@ test_externals_store(apr_pool_t *pool) pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_normal); - SVN_TEST_ASSERT(kind == svn_wc__db_kind_dir); + SVN_TEST_ASSERT(kind == svn_node_dir); SVN_TEST_STRING_ASSERT(repos_root_url, "svn://other-repos/nsv"); SVN_TEST_STRING_ASSERT(repos_uuid, "no-uuid-either"); SVN_TEST_STRING_ASSERT(defining_abspath, subdir); diff --git a/subversion/tests/libsvn_wc/entries-compat.c b/subversion/tests/libsvn_wc/entries-compat.c index eed6bca..34ad425 100644 --- a/subversion/tests/libsvn_wc/entries-compat.c +++ b/subversion/tests/libsvn_wc/entries-compat.c @@ -96,171 +96,171 @@ static const char * const TESTING_DATA = ( "insert into nodes values (" " 1, '', 0, null, 1, '', 1, 'normal'," " null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'A', 0, '', 1, 'A', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 10, null, null, null);" + " 10, null, null, null, null);" "insert into nodes values (" " 1, 'B', 0, '', 1, 'B', null, 'excluded'," " null, null, 'symlink', null, null, null, null, null, null, null," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" - " 1, 'C', 0, '', 1, 'C', null, 'absent'," + " 1, 'C', 0, '', 1, 'C', null, 'server-excluded'," " null, null, 'unknown', null, null, null, null, null, null, null," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'D', 0, '', 1, 'D', null, 'not-present'," " null, null, 'unknown', null, null, null, null, null, null, null," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'E', 0, '', 1, 'E', null, 'incomplete'," " null, null, 'unknown', null, null, null, null, null, null, null," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'F', 0, '', 1, 'F', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 15, null, null, null);" + " 15, null, null, null, null);" "insert into nodes values (" " 1, 'G', 0, '', 2, 'G-alt', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "'," - " 15, null, null, null);" + " 15, null, null, null, null);" "insert into nodes values (" " 1, 'H', 0, '', 1, 'H', 1, 'normal'," " null, null, 'symlink', '()', null, null, 'H-target', 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'I', 0, '', 1, 'I', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J', 0, '', 1, 'J', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e', 0, 'J', 1, 'J/J-e', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-a', 0, 'J/J-e', 1, 'J/J-e/J-e-a', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 15, null, null, null);" + " 15, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-b', 0, 'J/J-e', 1, 'J/J-e/J-e-b', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-b/Jeba', 0, 'J/J-e/J-e-b', 1, 'J/J-e/J-e-b/Jeba', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 15, null, null, null);" + " 15, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-f', 0, 'J', 1, 'J/J-f', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-f/J-f-a', 0, 'J/J-f', 1, 'J/J-f/J-f-a', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'K', 0, '', 1, 'K', 1, 'normal'," " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'K/K-a', 0, 'K', 1, 'K/K-a', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 15, null, null, null);" + " 15, null, null, null, null);" "insert into nodes values (" " 1, 'K/K-b', 0, 'K', 1, 'K/K-b', 1, 'normal'," " null, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " 15, null, null, null);" + " 15, null, null, null, null);" "" /* Load data into NODES table; ### op_depths have not been calculated by me yet; the value 1 is just 'good enough' to make the nodes WORKING nodes. */ "insert into nodes values (" " 1, 'I', 1, '', 2, 'some/dir', 2, 'normal'," - " 0, null, 'dir', '()', 'immediates', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J', 1, '', null, null, null, 'normal'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-a', 1, 'J', null, null, null, 'normal'," - " 0, null, 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-b', 1, 'J', 2, 'some/dir', 2, 'normal'," - " 0, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," - " null, null, null, null);" + " null, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-b/J-b-a', 1, 'J/J-b', 2, 'another/dir', 2, 'normal'," - " 0, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," - " null, null, null, null);" + " null, null, 'dir', '()', 'infinity', null, null, 2, " TIME_2s ", '" AUTHOR_2 "'," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-b/J-b-b', 1, 'J/J-b', null, null, null, 'normal'," - " 0, null, 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-c', 1, 'J', null, null, null, 'not-present'," - " 0, null, 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-c/J-c-a', 1, 'J/J-c', null, null, null, 'not-present'," - " 0, null, 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-d', 1, 'J', 2, 'moved/file', 2, 'normal'," " 1, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "'," - " 10, null, null, null);" + " 10, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e', 1, 'J', null, null, null, 'not-present'," - " 0, 'other/place', 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, 'other/place', 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-a', 1, 'J/J-e', null, null, null, 'not-present'," - " 0, null, 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-b', 1, 'J/J-e', null, null, null, 'not-present'," - " 0, null, 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-e/J-e-b/Jeba', 1, 'J/J-e/J-e-b', null, null, null, 'base-deleted'," - " 0, null, 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-f', 1, 'J', null, null, null, 'normal'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'J/J-f/J-f-a', 1, 'J/J-f', null, null, null, 'base-deleted'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'K', 1, '', null, null, null, 'base-deleted'," - " 0, null, 'dir', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'K/K-a', 1, 'K', null, null, null, 'base-deleted'," - " 0, null, 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, null, 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'K/K-b', 1, 'K', null, null, null, 'base-deleted'," - " 0, 'moved/away', 'file', '()', null, null, null, null, null, null," - " null, null, null, null);" + " null, 'moved/away', 'file', '()', null, null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'L', 1, '', null, null, null, 'normal'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'L/L-a', 1, 'L', null, null, null, 'not-present'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into nodes values (" " 1, 'L/L-a/L-a-a', 1, 'L/L-a', null, null, null, 'not-present'," - " 0, null, 'dir', '()', 'immediates', null, null, null, null, null," - " null, null, null, null);" + " null, null, 'dir', '()', 'immediates', null, null, null, null, null," + " null, null, null, null, null);" "insert into actual_node values (" " 1, 'I', '', null, null, null, null, null, 'changelist', null, " " null, null, null, null, null);" @@ -274,11 +274,11 @@ static const char * const TESTING_DATA = ( "insert into nodes values (" " 1, 'M', 0, '', 1, 'M', 1, 'normal', " " null, null, 'dir', '()', null, null, null, 1, " TIME_1s ", '" AUTHOR_1 "', " - " null, null, null, null);" + " null, null, null, null, null);" "insert into nodes values (" " 1, 'M/M-a', 0, 'M', 1, 'M/M-a', 1, 'not-present', " " null, null, 'file', '()', null, null, null, 1, null, null, " - " null, null, null, null);" + " null, null, null, null, null);" ); @@ -295,7 +295,7 @@ static const char * const M_TESTING_DATA = ( "insert into nodes values (" " 1, '', 0, null, 1, 'M', 1, 'normal'," " null, null, 'dir', '()', 'infinity', null, null, 1, " TIME_1s ", '" AUTHOR_1 "'," - " null, null, null, null);" + " null, null, null, null, null);" ); @@ -332,10 +332,12 @@ create_open(svn_wc__db_t **db, pool)); SVN_ERR(svn_wc__db_open(db, NULL /* config */, - TRUE /* auto_upgrade */, + FALSE /* not_upgraded_ok */, TRUE /* enforce_empty_wq */, pool, pool)); + svn_test_add_dir_cleanup(*local_abspath); + return SVN_NO_ERROR; } @@ -601,12 +603,14 @@ test_access_baton_like_locking(apr_pool_t *pool) { const char *url, *repos_root_url, *repos_uuid; const char *subdir = svn_dirent_join(local_abspath, "sub-wc", pool); + const char *repos_relpath; svn_boolean_t is_root; - SVN_ERR(svn_wc__node_get_url(&url, wc_ctx, local_abspath, pool, pool)); - SVN_ERR(svn_wc__node_get_repos_info(&repos_root_url, &repos_uuid, + SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, + &repos_root_url, &repos_uuid, wc_ctx, local_abspath, pool, pool)); + url = svn_path_url_add_component2(repos_root_url, repos_relpath, pool); SVN_ERR(svn_io_make_dir_recursively(subdir, pool)); SVN_ERR(svn_wc_ensure_adm3(subdir, repos_uuid, @@ -614,13 +618,13 @@ test_access_baton_like_locking(apr_pool_t *pool) repos_root_url, 0, svn_depth_infinity, pool)); - SVN_ERR(svn_wc__check_wc_root(&is_root, NULL, NULL, wc_ctx->db, subdir, - pool)); + SVN_ERR(svn_wc__db_is_switched(&is_root, NULL, NULL, wc_ctx->db, subdir, + pool)); SVN_TEST_ASSERT(is_root); - SVN_ERR(svn_wc__check_wc_root(&is_root, NULL, NULL, wc_ctx2->db, subdir, - pool)); + SVN_ERR(svn_wc__db_is_switched(&is_root, NULL, NULL, wc_ctx2->db, subdir, + pool)); /* This test was added to show a regression where the next check failed, but the check above this succeeded */ diff --git a/subversion/tests/libsvn_wc/op-depth-test.c b/subversion/tests/libsvn_wc/op-depth-test.c index c385c39..39afcf4 100644 --- a/subversion/tests/libsvn_wc/op-depth-test.c +++ b/subversion/tests/libsvn_wc/op-depth-test.c @@ -35,6 +35,7 @@ #include "svn_wc.h" #include "svn_client.h" #include "svn_hash.h" +#include "svn_sorts.h" #include "utils.h" @@ -43,6 +44,8 @@ #include "private/svn_dep_compat.h" #include "../../libsvn_wc/wc.h" #include "../../libsvn_wc/wc_db.h" +#include "../../libsvn_wc/workqueue.h" +#include "../../libsvn_wc/conflicts.h" #define SVN_WC__I_AM_WC_DB #include "../../libsvn_wc/wc_db_private.h" @@ -52,6 +55,14 @@ #pragma warning(disable: 4221) /* nonstandard extension used */ #endif +/* This macro is not available in 1.8.x, but let's just use it here */ +#ifndef SVN_VA_NULL +struct svn_null_pointer_constant_stdarg_sentinel_t; + +/** Null pointer constant used as a sentinel in variable argument lists. */ +#define SVN_VA_NULL ((struct svn_null_pointer_constant_stdarg_sentinel_t*)0) +#endif + /* Compare strings, like strcmp but either or both may be NULL which * compares equal to NULL and not equal to any non-NULL string. */ static int @@ -77,260 +88,14 @@ open_wc_db(svn_sqlite__db_t **sdb, apr_pool_t *scratch_pool) { SVN_ERR(svn_wc__db_util_open_db(sdb, wc_root_abspath, "wc.db", - svn_sqlite__mode_readwrite, my_statements, + svn_sqlite__mode_readwrite, + FALSE /* exclusive */, my_statements, result_pool, scratch_pool)); return SVN_NO_ERROR; } /* ---------------------------------------------------------------------- */ -/* Functions for easy manipulation of a WC. Paths given to these functions - * can be relative to the WC root as stored in the WC baton. */ - -/* Return the abspath of PATH which is absolute or relative to the WC in B. */ -#define wc_path(b, path) (svn_dirent_join((b)->wc_abspath, (path), (b)->pool)) - -/* Create a file on disk at PATH, with TEXT as its content. */ -static void -file_write(svn_test__sandbox_t *b, const char *path, const char *text) -{ - FILE *f = fopen(wc_path(b, path), "w"); - fputs(text, f); - fclose(f); -} - -/* Schedule for addition the single node that exists on disk at PATH, - * non-recursively. */ -static svn_error_t * -wc_add(svn_test__sandbox_t *b, const char *path) -{ - const char *parent_abspath; - path = 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_disk(b->wc_ctx, path, NULL, NULL, b->pool)); - SVN_ERR(svn_wc__release_write_lock(b->wc_ctx, parent_abspath, b->pool)); - return SVN_NO_ERROR; -} - -/* Create a single directory on disk. */ -static svn_error_t * -disk_mkdir(svn_test__sandbox_t *b, const char *path) -{ - path = wc_path(b, path); - SVN_ERR(svn_io_dir_make(path, APR_FPROT_OS_DEFAULT, b->pool)); - return SVN_NO_ERROR; -} - -/* Create a single directory on disk and schedule it for addition. */ -static svn_error_t * -wc_mkdir(svn_test__sandbox_t *b, const char *path) -{ - SVN_ERR(disk_mkdir(b, path)); - SVN_ERR(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. */ -static svn_error_t * -disk_copy(svn_test__sandbox_t *b, const char *from_path, const char *to_path) -{ - const char *to_dir, *to_name; - from_path = wc_path(b, from_path); - to_path = 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 - -/* Copy the WC file or directory tree FROM_PATH to TO_PATH which must not - * exist beforehand. */ -static svn_error_t * -wc_copy(svn_test__sandbox_t *b, const char *from_path, const char *to_path) -{ - from_path = wc_path(b, from_path); - to_path = wc_path(b, to_path); - return svn_wc_copy3(b->wc_ctx, from_path, to_path, FALSE, - NULL, NULL, NULL, NULL, b->pool); -} - -/* Revert a WC file or directory tree at PATH */ -static svn_error_t * -wc_revert(svn_test__sandbox_t *b, const char *path, svn_depth_t depth) -{ - const char *abspath = 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 /* lock_anchor */, - b->pool, b->pool)); - SVN_ERR(svn_wc_revert4(b->wc_ctx, abspath, depth, FALSE, NULL, - 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; -} - -static svn_error_t * -wc_delete(svn_test__sandbox_t *b, const char *path) -{ - const char *abspath = 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; -} - -static svn_error_t * -wc_exclude(svn_test__sandbox_t *b, const char *path) -{ - const char *abspath = 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; -} - -static svn_error_t * -wc_commit(svn_test__sandbox_t *b, const char *path) -{ - svn_client_ctx_t *ctx; - apr_array_header_t *targets = apr_array_make(b->pool, 1, - sizeof(const char *)); - - APR_ARRAY_PUSH(targets, const char *) = wc_path(b, path); - SVN_ERR(svn_client_create_context(&ctx, b->pool)); - return svn_client_commit5(targets, svn_depth_infinity, - FALSE, FALSE, TRUE, /* keep locks/cl's/use_ops*/ - NULL, NULL, NULL, NULL, ctx, b->pool); -} - -static svn_error_t * -wc_update(svn_test__sandbox_t *b, const char *path, svn_revnum_t revnum) -{ - 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; - revision.kind = svn_opt_revision_number; - revision.value.number = revnum; - - APR_ARRAY_PUSH(paths, const char *) = wc_path(b, path); - SVN_ERR(svn_client_create_context(&ctx, b->pool)); - return svn_client_update4(&result_revs, paths, &revision, svn_depth_infinity, - TRUE, FALSE, FALSE, FALSE, FALSE, - ctx, b->pool); -} - -static svn_error_t * -wc_resolved(svn_test__sandbox_t *b, const char *path) -{ - svn_client_ctx_t *ctx; - - SVN_ERR(svn_client_create_context(&ctx, b->pool)); - return svn_client_resolved(wc_path(b, path), TRUE, ctx, b->pool); -} - -static svn_error_t * -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_client_create_context(&ctx, b->pool)); - APR_ARRAY_PUSH(paths, const char *) = wc_path(b, src); - return svn_client_move6(paths, wc_path(b, dst), - FALSE, FALSE, NULL, NULL, NULL, ctx, b->pool); -} - -static svn_error_t * -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_ERR(svn_client_create_context(&ctx, b->pool)); - APR_ARRAY_PUSH(paths, const char *) = wc_path(b, path); - return svn_client_propset_local(name, svn_string_create(value, b->pool), - paths, svn_depth_empty, TRUE, NULL, ctx, - b->pool); -} - -/* Create the Greek tree on disk in the WC, and commit it. */ -static svn_error_t * -add_and_commit_greek_tree(svn_test__sandbox_t *b) -{ - const char *greek_tree_dirs[8] = - { - "A", - "A/B", - "A/B/E", - "A/B/F", - "A/C", - "A/D", - "A/D/G", - "A/D/H" - }; - const char *greek_tree_files[12][2] = - { - { "iota", "This is the file 'iota'.\n" }, - { "A/mu", "This is the file 'mu'.\n" }, - { "A/B/lambda", "This is the file 'lambda'.\n" }, - { "A/B/E/alpha", "This is the file 'alpha'.\n" }, - { "A/B/E/beta", "This is the file 'beta'.\n" }, - { "A/D/gamma", "This is the file 'gamma'.\n" }, - { "A/D/G/pi", "This is the file 'pi'.\n" }, - { "A/D/G/rho", "This is the file 'rho'.\n" }, - { "A/D/G/tau", "This is the file 'tau'.\n" }, - { "A/D/H/chi", "This is the file 'chi'.\n" }, - { "A/D/H/psi", "This is the file 'psi'.\n" }, - { "A/D/H/omega", "This is the file 'omega'.\n" } - }; - int i; - - for (i = 0; i < 8; i++) - SVN_ERR(wc_mkdir(b, greek_tree_dirs[i])); - - for (i = 0; i < 12; i++) - { - file_write(b, greek_tree_files[i][0], greek_tree_files[i][1]); - SVN_ERR(wc_add(b, greek_tree_files[i][0])); - } - - SVN_ERR(wc_commit(b, "")); - - return SVN_NO_ERROR; -} - - -/* ---------------------------------------------------------------------- */ /* Functions for comparing expected and found WC DB data. */ /* Some of the fields from a NODES table row. */ @@ -341,36 +106,87 @@ typedef struct nodes_row_t { svn_revnum_t repo_revnum; const char *repo_relpath; svn_boolean_t file_external; + const char *moved_to; + svn_boolean_t moved_here; + const char *props; /* comma-separated list of prop names */ } nodes_row_t; /* Macro for filling in the REPO_* fields of a non-base NODES_ROW_T * that has no copy-from info. */ -#define NO_COPY_FROM SVN_INVALID_REVNUM, NULL +#define NO_COPY_FROM SVN_INVALID_REVNUM, NULL, FALSE +#define MOVED_HERE FALSE, NULL, TRUE +#define NOT_MOVED FALSE, NULL, FALSE + +/* Return a comma-separated list of the prop names in PROPS, in lexically + * ascending order, or NULL if PROPS is empty or NULL. (Here, we don't + * care about the difference between 'has no props' and 'can't have props', + * and we choose to represent both of those as NULL.) */ +static const char * +props_hash_to_text(apr_hash_t *props, apr_pool_t *pool) +{ + apr_array_header_t *props_sorted; + svn_stringbuf_t *str; + int i; + + if (! props) + return NULL; + + str = svn_stringbuf_create_empty(pool); + props_sorted = svn_sort__hash(props, svn_sort_compare_items_lexically, pool); + for (i = 0; i < props_sorted->nelts; i++) + { + const svn_sort__item_t *item + = &APR_ARRAY_IDX(props_sorted, i, svn_sort__item_t); + + if (str->len) + svn_stringbuf_appendbyte(str, ','); + svn_stringbuf_appendcstr(str, item->key); + } + return str->len ? str->data : NULL; +} /* Return a human-readable string representing ROW. */ static const char * print_row(const nodes_row_t *row, apr_pool_t *result_pool) { - const char *file_external_str; + const char *file_external_str, *moved_here_str, *moved_to_str, *props; if (row == NULL) return "(null)"; + if (row->moved_to) + moved_to_str = apr_psprintf(result_pool, ", moved-to %s", row->moved_to); + else + moved_to_str = ""; + + if (row->moved_here) + moved_here_str = ", moved-here"; + else + moved_here_str = ""; + if (row->file_external) file_external_str = ", file-external"; else file_external_str = ""; - + + if (row->props) + props = apr_psprintf(result_pool, ", p=(%s)", row->props); + else + props = ""; + if (row->repo_revnum == SVN_INVALID_REVNUM) - return apr_psprintf(result_pool, "%d, %s, %s%s", + return apr_psprintf(result_pool, "%d, \"%s\", \"%s\"%s%s%s%s", row->op_depth, row->local_relpath, row->presence, - file_external_str); + moved_here_str, moved_to_str, + file_external_str, props); else - return apr_psprintf(result_pool, "%d, %s, %s, from ^/%s@%d%s", + return apr_psprintf(result_pool, "%d, \"%s\", \"%s\", %s ^/%s@%d%s%s%s%s", row->op_depth, row->local_relpath, row->presence, + row->op_depth == 0 ? "base" : "copyfrom", row->repo_relpath, (int)row->repo_revnum, - file_external_str); + moved_here_str, moved_to_str, + file_external_str, props); } /* A baton to pass through svn_hash_diff() to compare_nodes_rows(). */ @@ -412,7 +228,14 @@ compare_nodes_rows(const void *key, apr_ssize_t klen, else if (expected->repo_revnum != found->repo_revnum || (strcmp_null(expected->repo_relpath, found->repo_relpath) != 0) || (strcmp_null(expected->presence, found->presence) != 0) - || (expected->file_external != found->file_external)) + || (expected->file_external != found->file_external) + || (expected->moved_here != found->moved_here) + || (expected->moved_to && !found->moved_to) + || (!expected->moved_to && found->moved_to) + || (expected->moved_to + && strcmp(expected->moved_to, found->moved_to)) + || (expected->props != NULL + && strcmp_null(expected->props, found->props) != 0)) { b->errors = svn_error_createf( SVN_ERR_TEST_FAILED, b->errors, @@ -441,10 +264,13 @@ check_db_rows(svn_test__sandbox_t *b, int i; svn_sqlite__stmt_t *stmt; static const char *const statements[] = { - "SELECT op_depth, presence, local_relpath, revision, repos_path, " - " file_external " - "FROM nodes " - "WHERE local_relpath = ?1 OR local_relpath LIKE ?2", + "SELECT op_depth, nodes.presence, nodes.local_relpath, revision," + " repos_path, file_external, def_local_relpath, moved_to, moved_here," + " properties" + " FROM nodes " + " LEFT OUTER JOIN externals" + " ON nodes.local_relpath = externals.local_relpath" + " WHERE nodes.local_relpath = ?1 OR nodes.local_relpath LIKE ?2", NULL }; #define STMT_SELECT_NODES_INFO 0 @@ -470,6 +296,7 @@ check_db_rows(svn_test__sandbox_t *b, { const char *key; nodes_row_t *row = apr_palloc(b->pool, sizeof(*row)); + apr_hash_t *props_hash; row->op_depth = svn_sqlite__column_int(stmt, 0); row->presence = svn_sqlite__column_text(stmt, 1, b->pool); @@ -477,6 +304,15 @@ check_db_rows(svn_test__sandbox_t *b, row->repo_revnum = svn_sqlite__column_revnum(stmt, 3); row->repo_relpath = svn_sqlite__column_text(stmt, 4, b->pool); row->file_external = !svn_sqlite__column_is_null(stmt, 5); + if (row->file_external && svn_sqlite__column_is_null(stmt, 6)) + comparison_baton.errors + = svn_error_createf(SVN_ERR_TEST_FAILED, comparison_baton.errors, + "incomplete {%s}", print_row(row, b->pool)); + row->moved_to = svn_sqlite__column_text(stmt, 7, b->pool); + row->moved_here = svn_sqlite__column_boolean(stmt, 8); + SVN_ERR(svn_sqlite__column_properties(&props_hash, stmt, 9, + b->pool, b->pool)); + row->props = props_hash_to_text(props_hash, b->pool); key = apr_psprintf(b->pool, "%d %s", row->op_depth, row->local_relpath); apr_hash_set(found_hash, key, APR_HASH_KEY_STRING, row); @@ -534,22 +370,22 @@ struct copy_subtest_t static svn_error_t * wc_wc_copies(svn_test__sandbox_t *b) { - SVN_ERR(add_and_commit_greek_tree(b)); + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* Create the various kinds of source node which will be copied */ - file_write(b, source_added_file, "New file"); - SVN_ERR(wc_add(b, source_added_file)); - SVN_ERR(wc_mkdir(b, source_added_dir)); - SVN_ERR(wc_mkdir(b, source_added_dir2)); + sbox_file_write(b, source_added_file, "New file"); + SVN_ERR(sbox_wc_add(b, source_added_file)); + SVN_ERR(sbox_wc_mkdir(b, source_added_dir)); + SVN_ERR(sbox_wc_mkdir(b, source_added_dir2)); - SVN_ERR(wc_copy(b, source_base_file, source_copied_file)); - SVN_ERR(wc_copy(b, source_base_dir, source_copied_dir)); + SVN_ERR(sbox_wc_copy(b, source_base_file, source_copied_file)); + SVN_ERR(sbox_wc_copy(b, source_base_dir, source_copied_dir)); /* Delete some nodes so that we can test copying onto these paths */ - SVN_ERR(wc_delete(b, "A/D/gamma")); - SVN_ERR(wc_delete(b, "A/D/G")); + SVN_ERR(sbox_wc_delete(b, "A/D/gamma")); + SVN_ERR(sbox_wc_delete(b, "A/D/G")); /* Test copying various things */ @@ -647,11 +483,7 @@ wc_wc_copies(svn_test__sandbox_t *b) /* Perform each subtest in turn. */ for (subtest = subtests; subtest->from_path; subtest++) { - SVN_ERR(svn_wc_copy3(b->wc_ctx, - wc_path(b, subtest->from_path), - wc_path(b, subtest->to_path), - FALSE /* metadata_only */, - NULL, NULL, NULL, NULL, b->pool)); + SVN_ERR(sbox_wc_copy(b, subtest->from_path, subtest->to_path)); SVN_ERR(check_db_rows(b, subtest->to_path, subtest->expected)); } } @@ -664,14 +496,14 @@ wc_wc_copies(svn_test__sandbox_t *b) static svn_error_t * repo_wc_copies(svn_test__sandbox_t *b) { - SVN_ERR(add_and_commit_greek_tree(b)); + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* Delete some nodes so that we can test copying onto these paths */ - SVN_ERR(wc_delete(b, "A/B/lambda")); - SVN_ERR(wc_delete(b, "A/D/gamma")); - SVN_ERR(wc_delete(b, "A/D/G")); - SVN_ERR(wc_delete(b, "A/D/H")); + SVN_ERR(sbox_wc_delete(b, "A/B/lambda")); + SVN_ERR(sbox_wc_delete(b, "A/D/gamma")); + SVN_ERR(sbox_wc_delete(b, "A/D/G")); + SVN_ERR(sbox_wc_delete(b, "A/D/H")); /* Test copying various things */ @@ -761,7 +593,7 @@ repo_wc_copies(svn_test__sandbox_t *b) source.peg_revision = &rev; APR_ARRAY_PUSH(sources, svn_client_copy_source_t *) = &source; SVN_ERR(svn_client_copy6(sources, - wc_path(b, subtest->to_path), + sbox_wc_path(b, subtest->to_path), FALSE, FALSE, FALSE, NULL, NULL, NULL, ctx, b->pool)); } @@ -801,7 +633,7 @@ test_reverts(const svn_test_opts_t *opts, apr_pool_t *pool) /* Implement revert tests below, now that we have a wc with lots of copy-changes */ - SVN_ERR(wc_revert(&b, "A/B/D-added", svn_depth_infinity)); + SVN_ERR(sbox_wc_revert(&b, "A/B/D-added", svn_depth_infinity)); SVN_ERR(check_db_rows(&b, "A/B/D-added", no_node_rows_expected)); return SVN_NO_ERROR; @@ -813,10 +645,10 @@ test_deletes(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "deletes", opts, pool)); - SVN_ERR(add_and_commit_greek_tree(&b)); + SVN_ERR(sbox_add_and_commit_greek_tree(&b)); - file_write(&b, "A/B/E/new-file", "New file"); - SVN_ERR(wc_add(&b, "A/B/E/new-file")); + sbox_file_write(&b, "A/B/E/new-file", "New file"); + SVN_ERR(sbox_wc_add(&b, "A/B/E/new-file")); { nodes_row_t rows[] = { { 4, "A/B/E/new-file", "normal", NO_COPY_FROM }, @@ -825,7 +657,7 @@ test_deletes(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A/B/E/new-file", rows)); } - SVN_ERR(wc_delete(&b, "A/B/E/alpha")); + SVN_ERR(sbox_wc_delete(&b, "A/B/E/alpha")); { nodes_row_t rows[] = { { 0, "A/B/E/alpha", "normal", 1, "A/B/E/alpha" }, @@ -835,7 +667,7 @@ test_deletes(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A/B/E/alpha", rows)); } - SVN_ERR(wc_delete(&b, "A/B/F")); + SVN_ERR(sbox_wc_delete(&b, "A/B/F")); { nodes_row_t rows[] = { { 0, "A/B/F", "normal", 1, "A/B/F" }, @@ -845,7 +677,7 @@ test_deletes(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A/B/F", rows)); } - SVN_ERR(wc_delete(&b, "A/B")); + SVN_ERR(sbox_wc_delete(&b, "A/B")); { nodes_row_t rows[] = { { 0, "A/B", "normal", 1, "A/B", }, @@ -874,11 +706,11 @@ test_adds(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "adds", opts, pool)); - SVN_ERR(add_and_commit_greek_tree(&b)); + SVN_ERR(sbox_add_and_commit_greek_tree(&b)); /* add file */ - file_write(&b, "new-file", "New file"); - SVN_ERR(wc_add(&b, "new-file")); + sbox_file_write(&b, "new-file", "New file"); + SVN_ERR(sbox_wc_add(&b, "new-file")); { nodes_row_t rows[] = { { 1, "new-file", "normal", NO_COPY_FROM }, @@ -887,8 +719,8 @@ test_adds(const svn_test_opts_t *opts, apr_pool_t *pool) } /* add dir */ - SVN_ERR(wc_mkdir(&b, "new-dir")); - SVN_ERR(wc_mkdir(&b, "new-dir/D2")); + SVN_ERR(sbox_wc_mkdir(&b, "new-dir")); + SVN_ERR(sbox_wc_mkdir(&b, "new-dir/D2")); { nodes_row_t rows[] = { { 1, "new-dir", "normal", NO_COPY_FROM }, @@ -898,9 +730,9 @@ test_adds(const svn_test_opts_t *opts, apr_pool_t *pool) } /* replace file */ - SVN_ERR(wc_delete(&b, "iota")); - file_write(&b, "iota", "New iota file"); - SVN_ERR(wc_add(&b, "iota")); + SVN_ERR(sbox_wc_delete(&b, "iota")); + sbox_file_write(&b, "iota", "New iota file"); + SVN_ERR(sbox_wc_add(&b, "iota")); { nodes_row_t rows[] = { { 0, "iota", "normal", 1, "iota" }, @@ -910,9 +742,9 @@ test_adds(const svn_test_opts_t *opts, apr_pool_t *pool) } /* replace dir */ - SVN_ERR(wc_delete(&b, "A/B/E")); - SVN_ERR(wc_mkdir(&b, "A/B/E")); - SVN_ERR(wc_mkdir(&b, "A/B/E/D2")); + SVN_ERR(sbox_wc_delete(&b, "A/B/E")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/E")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/E/D2")); { nodes_row_t rows[] = { { 0, "A/B/E", "normal", 1, "A/B/E" }, @@ -935,12 +767,12 @@ test_adds_change_kind(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "adds", opts, pool)); - SVN_ERR(add_and_commit_greek_tree(&b)); + SVN_ERR(sbox_add_and_commit_greek_tree(&b)); /* replace dir with file */ - SVN_ERR(wc_delete(&b, "A/B/E")); - file_write(&b, "A/B/E", "New E file"); - SVN_ERR(wc_add(&b, "A/B/E")); + SVN_ERR(sbox_wc_delete(&b, "A/B/E")); + sbox_file_write(&b, "A/B/E", "New E file"); + SVN_ERR(sbox_wc_add(&b, "A/B/E")); { nodes_row_t rows[] = { { 0, "A/B/E", "normal", 1, "A/B/E" }, @@ -954,9 +786,9 @@ test_adds_change_kind(const svn_test_opts_t *opts, apr_pool_t *pool) } /* replace file with dir */ - SVN_ERR(wc_delete(&b, "iota")); - SVN_ERR(wc_mkdir(&b, "iota")); - SVN_ERR(wc_mkdir(&b, "iota/D2")); + SVN_ERR(sbox_wc_delete(&b, "iota")); + SVN_ERR(sbox_wc_mkdir(&b, "iota")); + SVN_ERR(sbox_wc_mkdir(&b, "iota/D2")); { nodes_row_t rows[] = { { 0, "iota", "normal", 1, "iota" }, @@ -976,10 +808,10 @@ test_delete_of_copies(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "deletes_of_copies", opts, pool)); - SVN_ERR(add_and_commit_greek_tree(&b)); - SVN_ERR(wc_copy(&b, "A/B", "A/B-copied")); + SVN_ERR(sbox_add_and_commit_greek_tree(&b)); + SVN_ERR(sbox_wc_copy(&b, "A/B", "A/B-copied")); - SVN_ERR(wc_delete(&b, "A/B-copied/E")); + SVN_ERR(sbox_wc_delete(&b, "A/B-copied/E")); { nodes_row_t rows[] = { { 2, "A/B-copied/E", "normal", 1, "A/B/E" }, @@ -993,7 +825,7 @@ test_delete_of_copies(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A/B-copied/E", rows)); } - SVN_ERR(wc_copy(&b, "A/D/G", "A/B-copied/E")); + SVN_ERR(sbox_wc_copy(&b, "A/D/G", "A/B-copied/E")); { nodes_row_t rows[] = { { 2, "A/B-copied/E", "normal", 1, "A/B/E" }, @@ -1010,7 +842,7 @@ test_delete_of_copies(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A/B-copied/E", rows)); } - SVN_ERR(wc_delete(&b, "A/B-copied/E/rho")); + SVN_ERR(sbox_wc_delete(&b, "A/B-copied/E/rho")); { nodes_row_t rows[] = { { 2, "A/B-copied/E", "normal", 1, "A/B/E" }, @@ -1028,7 +860,7 @@ test_delete_of_copies(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A/B-copied/E", rows)); } - SVN_ERR(wc_delete(&b, "A/B-copied/E")); + SVN_ERR(sbox_wc_delete(&b, "A/B-copied/E")); { nodes_row_t rows[] = { { 2, "A/B-copied/E", "normal", 1, "A/B/E" }, @@ -1042,9 +874,9 @@ test_delete_of_copies(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A/B-copied/E", rows)); } - SVN_ERR(wc_copy(&b, "A/B", "A/B-copied/E")); + SVN_ERR(sbox_wc_copy(&b, "A/B", "A/B-copied/E")); - SVN_ERR(wc_delete(&b, "A/B-copied/E/F")); + SVN_ERR(sbox_wc_delete(&b, "A/B-copied/E/F")); { nodes_row_t rows[] = { { 3, "A/B-copied/E/F", "normal", 1, "A/B/F" }, @@ -1054,7 +886,7 @@ test_delete_of_copies(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A/B-copied/E/F", rows)); } - SVN_ERR(wc_delete(&b, "A/B-copied")); + SVN_ERR(sbox_wc_delete(&b, "A/B-copied")); { nodes_row_t rows[] = { { 0 } }; SVN_ERR(check_db_rows(&b, "A/B-copied", rows)); @@ -1070,11 +902,11 @@ test_delete_with_base(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "deletes_with_base", opts, pool)); - SVN_ERR(add_and_commit_greek_tree(&b)); - SVN_ERR(wc_delete(&b, "A/B/E/beta")); - SVN_ERR(wc_commit(&b, "")); + SVN_ERR(sbox_add_and_commit_greek_tree(&b)); + SVN_ERR(sbox_wc_delete(&b, "A/B/E/beta")); + SVN_ERR(sbox_wc_commit(&b, "")); - SVN_ERR(wc_delete(&b, "A/B/E")); + SVN_ERR(sbox_wc_delete(&b, "A/B/E")); { nodes_row_t rows[] = { { 0, "A/B/E", "normal", 1, "A/B/E"}, @@ -1087,9 +919,9 @@ test_delete_with_base(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A/B/E", rows)); } - SVN_ERR(wc_copy(&b, "A/B/F", "A/B/E")); - SVN_ERR(wc_copy(&b, "A/mu", "A/B/E/alpha")); - SVN_ERR(wc_copy(&b, "A/mu", "A/B/E/beta")); + SVN_ERR(sbox_wc_copy(&b, "A/B/F", "A/B/E")); + SVN_ERR(sbox_wc_copy(&b, "A/mu", "A/B/E/alpha")); + SVN_ERR(sbox_wc_copy(&b, "A/mu", "A/B/E/beta")); { nodes_row_t rows[] = { { 0, "A/B/E", "normal", 1, "A/B/E"}, @@ -1105,7 +937,7 @@ test_delete_with_base(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A/B/E", rows)); } - SVN_ERR(wc_delete(&b, "A/B/E")); + SVN_ERR(sbox_wc_delete(&b, "A/B/E")); { nodes_row_t rows[] = { { 0, "A/B/E", "normal", 1, "A/B/E"}, @@ -1137,16 +969,16 @@ test_delete_with_update(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "delete_with_update", opts, pool)); - SVN_ERR(wc_mkdir(&b, "A")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_mkdir(&b, "A/B")); - SVN_ERR(wc_mkdir(&b, "A/B/C")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_update(&b, "", 1)); - - SVN_ERR(wc_delete(&b, "A")); - SVN_ERR(wc_mkdir(&b, "A")); - SVN_ERR(wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_delete(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); { nodes_row_t rows[] = { { 0, "A", "normal", 1, "A"}, @@ -1156,7 +988,7 @@ test_delete_with_update(const svn_test_opts_t *opts, apr_pool_t *pool) }; SVN_ERR(check_db_rows(&b, "A", rows)); } - SVN_ERR(wc_update(&b, "", 2)); + SVN_ERR(sbox_wc_update(&b, "", 2)); { nodes_row_t rows[] = { { 0, "A", "normal", 2, "A"}, @@ -1170,8 +1002,8 @@ test_delete_with_update(const svn_test_opts_t *opts, apr_pool_t *pool) }; SVN_ERR(check_db_rows(&b, "A", rows)); } - SVN_ERR(wc_resolved(&b, "")); - SVN_ERR(wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_resolved(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); { nodes_row_t rows[] = { { 0, "A", "normal", 1, "A"}, @@ -1213,9 +1045,9 @@ insert_dirs(svn_test__sandbox_t *b, if (nodes->local_relpath[0]) { SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 2)); - SVN_ERR(svn_sqlite__bindf(stmt, "sissrs", + SVN_ERR(svn_sqlite__bindf(stmt, "sdssrs", nodes->local_relpath, - (apr_int64_t)nodes->op_depth, + nodes->op_depth, nodes->presence, nodes->repo_relpath, nodes->repo_revnum, @@ -1225,9 +1057,9 @@ insert_dirs(svn_test__sandbox_t *b, else { SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1)); - SVN_ERR(svn_sqlite__bindf(stmt, "sissr", + SVN_ERR(svn_sqlite__bindf(stmt, "sdssr", nodes->local_relpath, - (apr_int64_t)nodes->op_depth, + nodes->op_depth, nodes->presence, nodes->repo_relpath, nodes->repo_revnum)); @@ -1258,7 +1090,7 @@ base_dir_insert_remove(svn_test__sandbox_t *b, nodes_row_t *added) { nodes_row_t *after; - const char *dir_abspath = wc_path(b, local_relpath); + const char *dir_abspath = sbox_wc_path(b, local_relpath); int i; apr_int64_t num_before = count_rows(before), num_added = count_rows(added); @@ -1270,10 +1102,10 @@ base_dir_insert_remove(svn_test__sandbox_t *b, "not-even-a-uuid", revision, apr_hash_make(b->pool), revision, 0, NULL, NULL, svn_depth_infinity, - NULL, NULL, FALSE, NULL, NULL, + NULL, NULL, FALSE, NULL, NULL, NULL, b->pool)); - after = apr_palloc(b->pool, sizeof(*after) * (num_before + num_added + 1)); + after = apr_palloc(b->pool, sizeof(*after) * (apr_size_t)(num_before + num_added + 1)); for (i = 0; i < num_before; ++i) after[i] = before[i]; for (i = 0; i < num_added; ++i) @@ -1282,7 +1114,14 @@ base_dir_insert_remove(svn_test__sandbox_t *b, SVN_ERR(check_db_rows(b, "", after)); - SVN_ERR(svn_wc__db_base_remove(b->wc_ctx->db, dir_abspath, b->pool)); + SVN_ERR(svn_wc__db_base_remove(b->wc_ctx->db, dir_abspath, + FALSE /* keep_as_Working */, + FALSE /* queue_deletes */, + FALSE /* remove_locks */, + SVN_INVALID_REVNUM, + NULL, NULL, b->pool)); + SVN_ERR(svn_wc__wq_run(b->wc_ctx->db, dir_abspath, + NULL, NULL, b->pool)); SVN_ERR(check_db_rows(b, "", before)); @@ -1598,7 +1437,7 @@ temp_op_make_copy(svn_test__sandbox_t *b, SVN_ERR(insert_dirs(b, before)); - SVN_ERR(svn_wc__db_temp_op_make_copy(b->wc_ctx->db, dir_abspath, b->pool)); + SVN_ERR(svn_wc__db_op_make_copy(b->wc_ctx->db, dir_abspath, NULL, NULL, b->pool)); SVN_ERR(check_db_rows(b, "", after)); @@ -1698,39 +1537,39 @@ test_wc_move(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "wc_move", opts, pool)); - SVN_ERR(wc_mkdir(&b, "A")); - SVN_ERR(wc_mkdir(&b, "A/B")); - SVN_ERR(wc_mkdir(&b, "A/B/C")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); - SVN_ERR(wc_move(&b, "A/B/C", "A/B/C-move")); + SVN_ERR(sbox_wc_move(&b, "A/B/C", "A/B/C-move")); { nodes_row_t rows[] = { { 0, "", "normal", 1, "" }, { 0, "A", "normal", 1, "A" }, { 0, "A/B", "normal", 1, "A/B" }, - { 0, "A/B/C", "normal", 1, "A/B/C" }, - { 3, "A/B/C", "base-deleted", NO_COPY_FROM }, - { 3, "A/B/C-move", "normal", 1, "A/B/C" }, + { 0, "A/B/C", "normal", 1, "A/B/C"}, + { 3, "A/B/C", "base-deleted", NO_COPY_FROM, "A/B/C-move" }, + { 3, "A/B/C-move", "normal", 1, "A/B/C", MOVED_HERE }, { 0 } }; SVN_ERR(check_db_rows(&b, "", rows)); } - SVN_ERR(wc_move(&b, "A/B", "A/B-move")); + SVN_ERR(sbox_wc_move(&b, "A/B", "A/B-move")); { nodes_row_t rows[] = { { 0, "", "normal", 1, "" }, { 0, "A", "normal", 1, "A" }, - { 0, "A/B", "normal", 1, "A/B" }, - { 0, "A/B/C", "normal", 1, "A/B/C" }, - { 2, "A/B", "base-deleted", NO_COPY_FROM }, - { 2, "A/B/C", "base-deleted", NO_COPY_FROM }, - { 2, "A/B-move", "normal", 1, "A/B" }, - { 2, "A/B-move/C", "normal", 1, "A/B/C" }, - { 3, "A/B-move/C", "base-deleted", NO_COPY_FROM }, - { 3, "A/B-move/C-move", "normal", 1, "A/B/C" }, + { 0, "A/B", "normal", 1, "A/B"}, + { 0, "A/B/C", "normal", 1, "A/B/C"}, + { 2, "A/B", "base-deleted", NO_COPY_FROM, "A/B-move" }, + { 2, "A/B/C", "base-deleted", NO_COPY_FROM}, + { 2, "A/B-move", "normal", 1, "A/B", MOVED_HERE }, + { 2, "A/B-move/C", "normal", 1, "A/B/C", MOVED_HERE }, + { 3, "A/B-move/C", "base-deleted", NO_COPY_FROM, "A/B-move/C-move" }, + { 3, "A/B-move/C-move", "normal", 1, "A/B/C", MOVED_HERE }, { 0 } }; SVN_ERR(check_db_rows(&b, "", rows)); @@ -1745,14 +1584,14 @@ test_mixed_rev_copy(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "mixed_rev_copy", opts, pool)); - SVN_ERR(wc_mkdir(&b, "A")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_mkdir(&b, "A/B")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_mkdir(&b, "A/B/C")); - SVN_ERR(wc_commit(&b, "")); - - SVN_ERR(wc_copy(&b, "A", "X")); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_copy(&b, "A", "X")); { nodes_row_t rows[] = { { 1, "X", "normal", 1, "A" }, @@ -1765,7 +1604,7 @@ test_mixed_rev_copy(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "X", rows)); } - SVN_ERR(wc_copy(&b, "A/B", "X/Y")); + SVN_ERR(sbox_wc_copy(&b, "A/B", "X/Y")); { nodes_row_t rows[] = { { 1, "X", "normal", 1, "A" }, @@ -1781,7 +1620,7 @@ test_mixed_rev_copy(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "X", rows)); } - SVN_ERR(wc_delete(&b, "X/B/C")); + SVN_ERR(sbox_wc_delete(&b, "X/B/C")); { nodes_row_t rows[] = { { 1, "X", "normal", 1, "A" }, @@ -1796,8 +1635,8 @@ test_mixed_rev_copy(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "X", rows)); } - SVN_ERR(wc_delete(&b, "X")); - SVN_ERR(wc_update(&b, "A/B/C", 0)); + SVN_ERR(sbox_wc_delete(&b, "X")); + SVN_ERR(sbox_wc_update(&b, "A/B/C", 0)); { nodes_row_t rows[] = { { 0, "", "normal", 0, "" }, @@ -1809,7 +1648,7 @@ test_mixed_rev_copy(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "", rows)); } - SVN_ERR(wc_copy(&b, "A", "X")); + SVN_ERR(sbox_wc_copy(&b, "A", "X")); { nodes_row_t rows[] = { { 1, "X", "normal", 1, "A" }, @@ -1830,23 +1669,23 @@ test_delete_of_replace(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "delete_of_replace", opts, pool)); - SVN_ERR(wc_mkdir(&b, "A")); - SVN_ERR(wc_mkdir(&b, "A/B")); - SVN_ERR(wc_mkdir(&b, "A/B/C")); - SVN_ERR(wc_mkdir(&b, "A/B/C/F")); - SVN_ERR(wc_mkdir(&b, "A/B/C/F/K")); - SVN_ERR(wc_mkdir(&b, "A/B/C/G")); - SVN_ERR(wc_mkdir(&b, "A/B/C/G/K")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_update(&b, "", 1)); - - SVN_ERR(wc_copy(&b, "A", "X")); - SVN_ERR(wc_move(&b, "X/B/C/F", "X/B/C/H")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_update(&b, "", 2)); - - SVN_ERR(wc_delete(&b, "A/B")); - SVN_ERR(wc_copy(&b, "X/B", "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/F")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/F/K")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/G")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/G/K")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_copy(&b, "A", "X")); + SVN_ERR(sbox_wc_move(&b, "X/B/C/F", "X/B/C/H")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 2)); + + SVN_ERR(sbox_wc_delete(&b, "A/B")); + SVN_ERR(sbox_wc_copy(&b, "X/B", "A/B")); { nodes_row_t rows[] = { { 0, "A", "normal", 2, "A" }, @@ -1869,7 +1708,7 @@ test_delete_of_replace(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A", rows)); } - SVN_ERR(wc_delete(&b, "A/B")); + SVN_ERR(sbox_wc_delete(&b, "A/B")); { nodes_row_t rows[] = { { 0, "A", "normal", 2, "A" }, @@ -1899,25 +1738,25 @@ test_del_replace_not_present(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "del_replace_not_present", opts, pool)); - SVN_ERR(wc_mkdir(&b, "A")); - SVN_ERR(wc_mkdir(&b, "A/B")); - SVN_ERR(wc_mkdir(&b, "A/B/X")); - SVN_ERR(wc_mkdir(&b, "A/B/Y")); - SVN_ERR(wc_mkdir(&b, "A/B/Z")); - SVN_ERR(wc_commit(&b, "")); - - SVN_ERR(wc_copy(&b, "A", "X")); - SVN_ERR(wc_mkdir(&b, "X/B/W")); - SVN_ERR(wc_commit(&b, "")); - - SVN_ERR(wc_update(&b, "", 2)); - SVN_ERR(wc_update(&b, "A/B/X", 0)); - SVN_ERR(wc_update(&b, "A/B/Y", 0)); - SVN_ERR(wc_update(&b, "X/B/W", 0)); - SVN_ERR(wc_update(&b, "X/B/Y", 0)); - SVN_ERR(wc_update(&b, "X/B/Z", 0)); - - SVN_ERR(wc_delete(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/X")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/Y")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/Z")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_copy(&b, "A", "X")); + SVN_ERR(sbox_wc_mkdir(&b, "X/B/W")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_update(&b, "", 2)); + SVN_ERR(sbox_wc_update(&b, "A/B/X", 0)); + SVN_ERR(sbox_wc_update(&b, "A/B/Y", 0)); + SVN_ERR(sbox_wc_update(&b, "X/B/W", 0)); + SVN_ERR(sbox_wc_update(&b, "X/B/Y", 0)); + SVN_ERR(sbox_wc_update(&b, "X/B/Z", 0)); + + SVN_ERR(sbox_wc_delete(&b, "A")); { nodes_row_t rows[] = { { 0, "A", "normal", 2, "A" }, @@ -1933,7 +1772,7 @@ test_del_replace_not_present(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A", rows)); } - SVN_ERR(wc_copy(&b, "X", "A")); + SVN_ERR(sbox_wc_copy(&b, "X", "A")); { nodes_row_t rows[] = { { 0, "A", "normal", 2, "A" }, @@ -1952,7 +1791,7 @@ test_del_replace_not_present(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A", rows)); } - SVN_ERR(wc_delete(&b, "A")); + SVN_ERR(sbox_wc_delete(&b, "A")); { nodes_row_t rows[] = { { 0, "A", "normal", 2, "A" }, @@ -2091,7 +1930,7 @@ revert(svn_test__sandbox_t *b, actual_row_t *before_actual, actual_row_t *after_actual) { - const char *local_abspath = wc_path(b, local_relpath); + const char *local_abspath = sbox_wc_path(b, local_relpath); svn_error_t *err; if (!before_actual) @@ -2202,12 +2041,10 @@ test_op_revert(const svn_test_opts_t *opts, apr_pool_t *pool) before, before, before_actual4, after_actual4)); err = revert(&b, "A/B", svn_depth_empty, before, before, common_actual5, common_actual5); - SVN_TEST_ASSERT(err && err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH); err = revert(&b, "A/B/C", svn_depth_empty, before, before, common_actual6, common_actual6); - SVN_TEST_ASSERT(err && err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH); } { @@ -2260,30 +2097,24 @@ test_op_revert(const svn_test_opts_t *opts, apr_pool_t *pool) err = revert(&b, "A/B", svn_depth_empty, common, common, NULL, NULL); - SVN_TEST_ASSERT(err && err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH); err = revert(&b, "A/B", svn_depth_empty, common, common, common_actual, common_actual); - SVN_TEST_ASSERT(err && err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH); err = revert(&b, "P", svn_depth_empty, common, common, NULL, NULL); - SVN_TEST_ASSERT(err && err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH); err = revert(&b, "P", svn_depth_empty, common, common, common_actual, common_actual); - SVN_TEST_ASSERT(err && err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH); err = revert(&b, "X", svn_depth_empty, common, common, NULL, NULL); - SVN_TEST_ASSERT(err && err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH); err = revert(&b, "X", svn_depth_empty, common, common, common_actual, common_actual); - SVN_TEST_ASSERT(err && err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH); } { @@ -2484,8 +2315,7 @@ test_op_revert(const svn_test_opts_t *opts, apr_pool_t *pool) }; err = revert(&b, "A", svn_depth_empty, common, common, NULL, NULL); - SVN_TEST_ASSERT(err && err->apr_err == SVN_ERR_WC_INVALID_OPERATION_DEPTH); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_INVALID_OPERATION_DEPTH); } { @@ -2751,27 +2581,27 @@ test_children_of_replaced_dir(const svn_test_opts_t *opts, apr_pool_t *pool) A_abspath = svn_dirent_join(b.wc_abspath, "A", pool); /* Set up the base state as revision 1. */ - SVN_ERR(wc_mkdir(&b, "A")); - SVN_ERR(wc_mkdir(&b, "A/F")); - SVN_ERR(wc_mkdir(&b, "A/G")); - SVN_ERR(wc_mkdir(&b, "A/H")); - SVN_ERR(wc_mkdir(&b, "A/L")); - SVN_ERR(wc_mkdir(&b, "X")); - SVN_ERR(wc_mkdir(&b, "X/G")); - SVN_ERR(wc_mkdir(&b, "X/H")); - SVN_ERR(wc_mkdir(&b, "X/I")); - SVN_ERR(wc_mkdir(&b, "X/K")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/F")); + SVN_ERR(sbox_wc_mkdir(&b, "A/G")); + SVN_ERR(sbox_wc_mkdir(&b, "A/H")); + SVN_ERR(sbox_wc_mkdir(&b, "A/L")); + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_mkdir(&b, "X/G")); + SVN_ERR(sbox_wc_mkdir(&b, "X/H")); + SVN_ERR(sbox_wc_mkdir(&b, "X/I")); + SVN_ERR(sbox_wc_mkdir(&b, "X/K")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); /* Replace A with a copy of X. */ - SVN_ERR(wc_delete(&b, "A")); - SVN_ERR(wc_copy(&b, "X", "A")); + SVN_ERR(sbox_wc_delete(&b, "A")); + SVN_ERR(sbox_wc_copy(&b, "X", "A")); /* Make other local mods. */ - SVN_ERR(wc_delete(&b, "A/G")); - SVN_ERR(wc_mkdir(&b, "A/J")); - SVN_ERR(wc_mkdir(&b, "A/L")); + SVN_ERR(sbox_wc_delete(&b, "A/G")); + SVN_ERR(sbox_wc_mkdir(&b, "A/J")); + SVN_ERR(sbox_wc_mkdir(&b, "A/L")); /* Test several variants of "list the children of 'A'". */ @@ -2819,15 +2649,17 @@ do_delete(svn_test__sandbox_t *b, actual_row_t *actual_before, actual_row_t *actual_after) { - const char *local_abspath = wc_path(b, local_relpath); + const char *local_abspath = sbox_wc_path(b, local_relpath); SVN_ERR(insert_dirs(b, before)); SVN_ERR(insert_actual(b, actual_before)); SVN_ERR(check_db_rows(b, "", before)); SVN_ERR(check_db_actual(b, actual_before)); - SVN_ERR(svn_wc__db_op_delete(b->wc_ctx->db, local_abspath, - NULL, NULL /* notification */, + SVN_ERR(svn_wc__db_op_delete(b->wc_ctx->db, local_abspath, NULL, + TRUE /* delete_dir_externals */, + NULL /* conflict */, NULL /* work_item */, NULL, NULL /* cancellation */, + NULL, NULL /* notification */, b->pool)); SVN_ERR(check_db_rows(b, "", after)); SVN_ERR(check_db_actual(b, actual_after)); @@ -3069,13 +2901,13 @@ test_child_replace_with_same_origin(const svn_test_opts_t *opts, SVN_ERR(svn_test__sandbox_create(&b, "child_replace_with_same", opts, pool)); /* Set up the base state as revision 1. */ - SVN_ERR(wc_mkdir(&b, "A")); - SVN_ERR(wc_mkdir(&b, "A/B")); - SVN_ERR(wc_mkdir(&b, "A/B/C")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); - SVN_ERR(wc_copy(&b, "A", "X")); + SVN_ERR(sbox_wc_copy(&b, "A", "X")); { nodes_row_t rows[] = { @@ -3087,7 +2919,7 @@ test_child_replace_with_same_origin(const svn_test_opts_t *opts, SVN_ERR(check_db_rows(&b, "X", rows)); } - SVN_ERR(wc_delete(&b, "X/B")); + SVN_ERR(sbox_wc_delete(&b, "X/B")); { nodes_row_t rows[] = { {1, "X", "normal", 1, "A"}, @@ -3102,7 +2934,7 @@ test_child_replace_with_same_origin(const svn_test_opts_t *opts, SVN_ERR(check_db_rows(&b, "X", rows)); } - SVN_ERR(wc_copy(&b, "A/B", "X/B")); + SVN_ERR(sbox_wc_copy(&b, "A/B", "X/B")); { /* The revisions match what was here, so for an optimal commit this should have exactly the same behavior as reverting X/B. @@ -3138,38 +2970,38 @@ test_shadowed_update(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(svn_test__sandbox_create(&b, "shadowed_update", opts, pool)); /* Set up the base state as revision 1. */ - file_write(&b, "iota", "This is iota"); - SVN_ERR(wc_add(&b, "iota")); - SVN_ERR(wc_commit(&b, "")); + sbox_file_write(&b, "iota", "This is iota"); + SVN_ERR(sbox_wc_add(&b, "iota")); + SVN_ERR(sbox_wc_commit(&b, "")); /* And create two trees in r2 */ - SVN_ERR(wc_mkdir(&b, "A")); - SVN_ERR(wc_mkdir(&b, "A/B")); - SVN_ERR(wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); - SVN_ERR(wc_mkdir(&b, "K")); - SVN_ERR(wc_mkdir(&b, "K/L")); - SVN_ERR(wc_mkdir(&b, "K/L/M")); - SVN_ERR(wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "K")); + SVN_ERR(sbox_wc_mkdir(&b, "K/L")); + SVN_ERR(sbox_wc_mkdir(&b, "K/L/M")); + SVN_ERR(sbox_wc_commit(&b, "")); /* And change something in r3 */ - file_write(&b, "iota", "This is a new iota"); - SVN_ERR(wc_commit(&b, "")); + sbox_file_write(&b, "iota", "This is a new iota"); + SVN_ERR(sbox_wc_commit(&b, "")); /* And delete C & M */ - SVN_ERR(wc_delete(&b, "A/B/C")); - SVN_ERR(wc_delete(&b, "K/L/M")); - SVN_ERR(wc_commit(&b, "")); + SVN_ERR(sbox_wc_delete(&b, "A/B/C")); + SVN_ERR(sbox_wc_delete(&b, "K/L/M")); + SVN_ERR(sbox_wc_commit(&b, "")); /* And now create the shadowed situation */ - SVN_ERR(wc_update(&b, "", 2)); - SVN_ERR(wc_copy(&b, "A", "A_tmp")); - SVN_ERR(wc_update(&b, "", 1)); - SVN_ERR(wc_move(&b, "A_tmp", "A")); + SVN_ERR(sbox_wc_update(&b, "", 2)); + SVN_ERR(sbox_wc_copy(&b, "A", "A_tmp")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_move(&b, "A_tmp", "A")); - SVN_ERR(wc_mkdir(&b, "K")); - SVN_ERR(wc_mkdir(&b, "K/L")); - SVN_ERR(wc_mkdir(&b, "K/L/M")); + SVN_ERR(sbox_wc_mkdir(&b, "K")); + SVN_ERR(sbox_wc_mkdir(&b, "K/L")); + SVN_ERR(sbox_wc_mkdir(&b, "K/L/M")); /* Verify situation before update */ { @@ -3190,7 +3022,7 @@ test_shadowed_update(const svn_test_opts_t *opts, apr_pool_t *pool) } /* And now bring in A and K below the local information */ - SVN_ERR(wc_update(&b, "", 3)); + SVN_ERR(sbox_wc_update(&b, "", 3)); { nodes_row_t rows[] = { @@ -3223,9 +3055,9 @@ test_shadowed_update(const svn_test_opts_t *opts, apr_pool_t *pool) } /* Update again to remove C and M */ - SVN_ERR(wc_resolved(&b, "A")); - SVN_ERR(wc_resolved(&b, "K")); - SVN_ERR(wc_update(&b, "", 4)); + SVN_ERR(sbox_wc_resolved(&b, "A")); + SVN_ERR(sbox_wc_resolved(&b, "K")); + SVN_ERR(sbox_wc_update(&b, "", 4)); { nodes_row_t rows[] = { @@ -3255,11 +3087,11 @@ test_shadowed_update(const svn_test_opts_t *opts, apr_pool_t *pool) } /* Update again to bring C and M back */ - SVN_ERR(wc_resolved(&b, "A")); - SVN_ERR(wc_resolved(&b, "K")); - SVN_ERR(wc_update(&b, "", 3)); + SVN_ERR(sbox_wc_resolved(&b, "A")); + SVN_ERR(sbox_wc_resolved(&b, "K")); + SVN_ERR(sbox_wc_update(&b, "", 3)); - SVN_ERR(wc_delete(&b, "K/L/M")); + SVN_ERR(sbox_wc_delete(&b, "K/L/M")); { nodes_row_t rows[] = { {0, "", "normal", 3, ""}, @@ -3289,13 +3121,13 @@ test_shadowed_update(const svn_test_opts_t *opts, apr_pool_t *pool) } /* Resolve conflict on K and go back to r1 */ - SVN_ERR(wc_revert(&b, "K", svn_depth_infinity)); - SVN_ERR(wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_revert(&b, "K", svn_depth_infinity)); + SVN_ERR(sbox_wc_update(&b, "", 1)); - SVN_ERR(wc_mkdir(&b, "K")); - SVN_ERR(wc_mkdir(&b, "K/L")); + SVN_ERR(sbox_wc_mkdir(&b, "K")); + SVN_ERR(sbox_wc_mkdir(&b, "K/L")); - SVN_ERR(wc_update(&b, "", 3)); + SVN_ERR(sbox_wc_update(&b, "", 3)); { nodes_row_t rows[] = { @@ -3316,10 +3148,10 @@ test_shadowed_update(const svn_test_opts_t *opts, apr_pool_t *pool) } /* Update the shadowed K/L/M to r4 where they do not exit */ - SVN_ERR(wc_resolved(&b, "K")); - SVN_ERR(wc_update(&b, "K/L/M", 4)); - SVN_ERR(wc_resolved(&b, "A")); - SVN_ERR(wc_update(&b, "A/B/C", 4)); + SVN_ERR(sbox_wc_resolved(&b, "K")); + SVN_ERR(sbox_wc_update(&b, "K/L/M", 4)); + SVN_ERR(sbox_wc_resolved(&b, "A")); + SVN_ERR(sbox_wc_update(&b, "A/B/C", 4)); { nodes_row_t rows[] = { @@ -3361,24 +3193,24 @@ test_copy_of_deleted(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "copy_of_deleted", opts, pool)); - SVN_ERR(add_and_commit_greek_tree(&b)); + SVN_ERR(sbox_add_and_commit_greek_tree(&b)); - /* Recreate the test scenario from copy_tests.py copy_wc_url_with_absent */ + /* Recreate the test scenario from copy_tests.py copy_wc_url_with_server_excluded */ /* Delete A/B */ - SVN_ERR(wc_delete(&b, "A/B")); + SVN_ERR(sbox_wc_delete(&b, "A/B")); /* A/no not-present but in HEAD */ - SVN_ERR(wc_copy(&b, "A/mu", "A/no")); - SVN_ERR(wc_commit(&b, "A/no")); - SVN_ERR(wc_update(&b, "A/no", 1)); + SVN_ERR(sbox_wc_copy(&b, "A/mu", "A/no")); + SVN_ERR(sbox_wc_commit(&b, "A/no")); + SVN_ERR(sbox_wc_update(&b, "A/no", 1)); /* A/mu not-present and not in HEAD */ - SVN_ERR(wc_delete(&b, "A/mu")); - SVN_ERR(wc_commit(&b, "A/mu")); + SVN_ERR(sbox_wc_delete(&b, "A/mu")); + SVN_ERR(sbox_wc_commit(&b, "A/mu")); /* A/D excluded */ - SVN_ERR(wc_exclude(&b, "A/D")); + SVN_ERR(sbox_wc_exclude(&b, "A/D")); /* This should have created this structure */ { @@ -3408,7 +3240,7 @@ test_copy_of_deleted(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(check_db_rows(&b, "A", rows)); } - SVN_ERR(wc_copy(&b, "A", "A_copied")); + SVN_ERR(sbox_wc_copy(&b, "A", "A_copied")); /* I would expect this behavior, as this copies all layers where possible instead of just constructing a top level layer with not-present nodes @@ -3451,12 +3283,12 @@ test_case_rename(const svn_test_opts_t *opts, apr_pool_t *pool) apr_hash_t *dirents; SVN_ERR(svn_test__sandbox_create(&b, "case_rename", opts, pool)); - SVN_ERR(add_and_commit_greek_tree(&b)); + SVN_ERR(sbox_add_and_commit_greek_tree(&b)); - SVN_ERR(wc_move(&b, "A", "a")); - SVN_ERR(wc_move(&b, "iota", "iotA")); + SVN_ERR(sbox_wc_move(&b, "A", "a")); + SVN_ERR(sbox_wc_move(&b, "iota", "iotA")); - SVN_ERR(svn_io_get_dirents3(&dirents, wc_path(&b, ""), TRUE, pool, pool)); + SVN_ERR(svn_io_get_dirents3(&dirents, sbox_wc_path(&b, ""), TRUE, pool, pool)); /* A shouldn't be there, but a should */ SVN_TEST_ASSERT(apr_hash_get(dirents, "a", APR_HASH_KEY_STRING)); @@ -3474,14 +3306,14 @@ commit_file_external(const svn_test_opts_t *opts, apr_pool_t *pool) svn_test__sandbox_t b; SVN_ERR(svn_test__sandbox_create(&b, "commit_file_external", opts, pool)); - file_write(&b, "f", "this is f\n"); - SVN_ERR(wc_add(&b, "f")); - SVN_ERR(wc_propset(&b, "svn:externals", "^/f g", "")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_update(&b, "", 1)); - file_write(&b, "g", "this is f\nmodified via g\n"); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_update(&b, "", 2)); + sbox_file_write(&b, "f", "this is f\n"); + SVN_ERR(sbox_wc_add(&b, "f")); + SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f g", "")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + sbox_file_write(&b, "g", "this is f\nmodified via g\n"); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 2)); { nodes_row_t rows[] = { @@ -3496,6 +3328,196 @@ commit_file_external(const svn_test_opts_t *opts, apr_pool_t *pool) return SVN_NO_ERROR; } +static svn_error_t * +revert_file_externals(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "revert_file_externals", opts, pool)); + sbox_file_write(&b, "f", "this is f\n"); + SVN_ERR(sbox_wc_add(&b, "f")); + SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f g", "")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f h", "")); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f g", "A")); + { + nodes_row_t rows[] = { + { 0, "", "normal", 1, "" }, + { 0, "f", "normal", 1, "f" }, + { 0, "g", "normal", 1, "f", TRUE }, + { 1, "A", "normal", NO_COPY_FROM }, + { 0 } + }; + SVN_ERR(check_db_rows(&b, "", rows)); + } + + SVN_ERR(sbox_wc_update(&b, "", 1)); + { + nodes_row_t rows[] = { + { 0, "", "normal", 1, "" }, + { 0, "f", "normal", 1, "f" }, + { 1, "A", "normal", NO_COPY_FROM }, + { 0, "h", "normal", 1, "f", TRUE }, + { 0, "A/g", "normal", 1, "f", TRUE }, + { 0 } + }; + SVN_ERR(check_db_rows(&b, "", rows)); + } + + SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity)); + { + nodes_row_t rows[] = { + { 0, "", "normal", 1, "" }, + { 0, "f", "normal", 1, "f" }, + { 0, "h", "normal", 1, "f", TRUE }, + { 0, "A/g", "normal", 1, "f", TRUE }, + { 0 } + }; + SVN_ERR(check_db_rows(&b, "", rows)); + } + + SVN_ERR(sbox_wc_update(&b, "", 1)); + { + nodes_row_t rows[] = { + { 0, "", "normal", 1, "" }, + { 0, "f", "normal", 1, "f" }, + { 0, "g", "normal", 1, "f", TRUE }, + { 0 } + }; + SVN_ERR(check_db_rows(&b, "", rows)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +copy_file_externals(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "copy_file_externals", opts, pool)); + sbox_file_write(&b, "f", "this is f\n"); + SVN_ERR(sbox_wc_add(&b, "f")); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f g", "A")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f g", "A/B")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + { + nodes_row_t rows[] = { + { 0, "", "normal", 1, "" }, + { 0, "f", "normal", 1, "f" }, + { 0, "A", "normal", 1, "A" }, + { 2, "A/B", "normal", NO_COPY_FROM }, + { 0, "A/g", "normal", 1, "f", TRUE }, + { 0, "A/B/g", "normal", 1, "f", TRUE }, + { 0 } + }; + SVN_ERR(check_db_rows(&b, "", rows)); + } + + SVN_ERR(sbox_wc_copy(&b, "A", "X")); + { + nodes_row_t rows[] = { + { 0, "", "normal", 1, "" }, + { 0, "f", "normal", 1, "f" }, + { 0, "A", "normal", 1, "A" }, + { 2, "A/B", "normal", NO_COPY_FROM }, + { 0, "A/g", "normal", 1, "f", TRUE }, + { 0, "A/B/g", "normal", 1, "f", TRUE }, + { 1, "X", "normal", 1, "A" }, + { 2, "X/B", "normal", NO_COPY_FROM }, + { 0 } + }; + SVN_ERR(check_db_rows(&b, "", rows)); + } + + SVN_ERR(sbox_wc_update(&b, "", 1)); + { + nodes_row_t rows[] = { + { 0, "", "normal", 1, "" }, + { 0, "f", "normal", 1, "f" }, + { 0, "A", "normal", 1, "A" }, + { 2, "A/B", "normal", NO_COPY_FROM }, + { 0, "A/g", "normal", 1, "f", TRUE }, + { 0, "A/B/g", "normal", 1, "f", TRUE }, + { 1, "X", "normal", 1, "A" }, + { 2, "X/B", "normal", NO_COPY_FROM }, + { 0, "X/g", "normal", 1, "f", TRUE }, + { 0, "X/B/g", "normal", 1, "f", TRUE }, + { 0 } + }; + SVN_ERR(check_db_rows(&b, "", rows)); + } + + SVN_ERR(sbox_wc_delete(&b, "X")); + { + nodes_row_t rows[] = { + { 0, "", "normal", 1, "" }, + { 0, "f", "normal", 1, "f" }, + { 0, "A", "normal", 1, "A" }, + { 2, "A/B", "normal", NO_COPY_FROM }, + { 0, "A/g", "normal", 1, "f", TRUE }, + { 0, "A/B/g", "normal", 1, "f", TRUE }, + { 0 } + }; + SVN_ERR(check_db_rows(&b, "", rows)); + } + + SVN_ERR(sbox_wc_update(&b, "", 1)); + { + nodes_row_t rows[] = { + { 0, "", "normal", 1, "" }, + { 0, "f", "normal", 1, "f" }, + { 0, "A", "normal", 1, "A" }, + { 2, "A/B", "normal", NO_COPY_FROM }, + { 0, "A/g", "normal", 1, "f", TRUE }, + { 0, "A/B/g", "normal", 1, "f", TRUE }, + { 0 } + }; + SVN_ERR(check_db_rows(&b, "", rows)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +copy_wc_wc_server_excluded(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + nodes_row_t before[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/E", "server-excluded", 1, "A/B/E"}, + {0} + }; + nodes_row_t after[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/E", "server-excluded", 1, "A/B/E"}, + {1, "X", "normal", 1, "A"}, + {1, "X/B", "normal", 1, "A/B"}, + {1, "X/B/E", "incomplete", 1, "A/B/E"}, + {0} + }; + svn_error_t *err; + + SVN_ERR(svn_test__sandbox_create(&b, "copy_wc_wc_server_excluded", opts, pool)); + SVN_ERR(insert_dirs(&b, before)); + SVN_ERR(check_db_rows(&b, "", before)); + SVN_ERR(sbox_disk_mkdir(&b, "A")); + err = sbox_wc_copy(&b, "A", "X"); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_PATH_UNEXPECTED_STATUS); + SVN_ERR(check_db_rows(&b, "", after)); + + return SVN_NO_ERROR; +} + /* Issue 4040 */ static svn_error_t * incomplete_switch(const svn_test_opts_t *opts, apr_pool_t *pool) @@ -3504,17 +3526,17 @@ incomplete_switch(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(svn_test__sandbox_create(&b, "incomplete_switch", opts, pool)); - SVN_ERR(wc_mkdir(&b, "A")); - SVN_ERR(wc_mkdir(&b, "A/B")); - SVN_ERR(wc_mkdir(&b, "A/B/C")); - SVN_ERR(wc_mkdir(&b, "A/B/C/D")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_copy(&b, "A", "X")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_copy(&b, "A", "X/A")); - SVN_ERR(wc_commit(&b, "")); - SVN_ERR(wc_delete(&b, "X/A")); - SVN_ERR(wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_copy(&b, "A", "X")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_copy(&b, "A", "X/A")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_delete(&b, "X/A")); + SVN_ERR(sbox_wc_commit(&b, "")); { /* Interrupted switch from A@1 to X@3 */ @@ -3529,7 +3551,7 @@ incomplete_switch(const svn_test_opts_t *opts, apr_pool_t *pool) {0} }; - nodes_row_t after_update[] = { + nodes_row_t after_update[] = { {0, "", "normal", 4, "X"}, {0, "B", "normal", 4, "A/B"}, {0, "B/C", "normal", 4, "A/B/C"}, @@ -3539,13 +3561,5466 @@ incomplete_switch(const svn_test_opts_t *opts, apr_pool_t *pool) SVN_ERR(insert_dirs(&b, before)); SVN_ERR(check_db_rows(&b, "", before)); - SVN_ERR(wc_update(&b, "", 4)); + SVN_ERR(sbox_wc_update(&b, "", 4)); SVN_ERR(check_db_rows(&b, "", after_update)); } return SVN_NO_ERROR; } +static svn_error_t * +nested_moves_child_first(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "nested_moves_child_first", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_move(&b, "A/B/C", "A/B/C2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {3, "A/B/C", "base-deleted", NO_COPY_FROM, "A/B/C2"}, + {3, "A/B/C2", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 1, "A/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {3, "A/B2/C", "base-deleted", NO_COPY_FROM, "A/B2/C2"}, + {3, "A/B2/C2", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {2, "A2/B", "base-deleted", NO_COPY_FROM, "A2/B2"}, + {2, "A2/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A2/B2", "normal", 1, "A/B", MOVED_HERE}, + {2, "A2/B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {3, "A2/B2/C", "base-deleted", NO_COPY_FROM, "A2/B2/C2"}, + {3, "A2/B2/C2","normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Revert should leave the A to A2 move */ + SVN_ERR(sbox_wc_revert(&b, "A2/B2", svn_depth_infinity)); + SVN_ERR(sbox_wc_revert(&b, "A2/B", svn_depth_infinity)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +nested_moves_child_last(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "nested_moves_child_last", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_move(&b, "A2/B", "A2/B2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {2, "A2/B", "base-deleted", NO_COPY_FROM, "A2/B2"}, + {2, "A2/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A2/B2", "normal", 1, "A/B", MOVED_HERE}, + {2, "A2/B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_move(&b, "A2/B2/C", "A2/B2/C2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {2, "A2/B", "base-deleted", NO_COPY_FROM, "A2/B2"}, + {2, "A2/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A2/B2", "normal", 1, "A/B", MOVED_HERE}, + {2, "A2/B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {3, "A2/B2/C", "base-deleted", NO_COPY_FROM, "A2/B2/C2"}, + {3, "A2/B2/C2","normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Revert should leave the A to A2 move */ + SVN_ERR(sbox_wc_revert(&b, "A2/B2", svn_depth_infinity)); + SVN_ERR(sbox_wc_revert(&b, "A2/B", svn_depth_infinity)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_in_copy(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_in_copy", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_copy(&b, "A", "A2")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {1, "A2", "normal", 1, "A"}, + {1, "A2/B", "normal", 1, "A/B"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_move(&b, "A2/B", "A2/B2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {1, "A2", "normal", 1, "A"}, + {1, "A2/B", "normal", 1, "A/B"}, + {2, "A2/B", "base-deleted", NO_COPY_FROM, "A2/B2"}, + {2, "A2/B2", "normal", 1, "A/B", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_in_replace(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_in_replace", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_mkdir(&b, "X/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_delete(&b, "A")); + SVN_ERR(sbox_wc_copy(&b, "X", "A")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/B", "normal", 1, "X/B"}, + {1, "A", "normal", 1, "X"}, + {1, "A/B", "normal", 1, "X/B"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/B", "normal", 1, "X/B"}, + {1, "A", "normal", 1, "X"}, + {1, "A/B", "normal", 1, "X/B"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B2", "normal", 1, "X/B", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +copy_a_move(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "copy_a_move", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_move(&b, "A/B/C", "A/C2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {2, "A/C2", "normal", 1, "A/B/C", MOVED_HERE}, + {3, "A/B/C", "base-deleted", NO_COPY_FROM, "A/C2"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + /* Copying a move doesn't copy any moved-to/here artifacts, which + means that moving inside a copy is not the same as copying + something that contains a move? Is this behaviour correct? */ + SVN_ERR(sbox_wc_copy(&b, "A", "A2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {2, "A/C2", "normal", 1, "A/B/C", MOVED_HERE}, + {3, "A/B/C", "base-deleted", NO_COPY_FROM, "A/C2"}, + {1, "A2", "normal", 1, "A"}, + {1, "A2/B", "normal", 1, "A/B"}, + {1, "A2/B/C", "normal", 1, "A/B/C"}, + {2, "A2/C2", "normal", 1, "A/B/C"}, /* MOVED_HERE? */ + {3, "A2/B/C", "base-deleted", NO_COPY_FROM}, /* "A2/C2"? */ + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_to_swap(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_to_swap", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_mkdir(&b, "X/Y")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A/B", "X/B")); + SVN_ERR(sbox_wc_move(&b, "X/Y", "A/Y")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/Y", "normal", 1, "X/Y"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "X/B"}, + {2, "A/Y", "normal", 1, "X/Y", MOVED_HERE}, + {2, "X/Y", "base-deleted", NO_COPY_FROM, "A/Y"}, + {2, "X/B", "normal", 1, "A/B", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/Y", "normal", 1, "X/Y"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {2, "A2/B", "base-deleted", NO_COPY_FROM, "X/B"}, + {2, "A2/Y", "normal", 1, "X/Y", MOVED_HERE}, + {2, "X/Y", "base-deleted", NO_COPY_FROM, "A2/Y"}, + {2, "X/B", "normal", 1, "A/B", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_move(&b, "X", "A")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/Y", "normal", 1, "X/Y"}, + {1, "A", "normal", 1, "X", FALSE, "A2", TRUE}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/Y", "normal", 1, "X/Y", MOVED_HERE}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "X", "base-deleted", NO_COPY_FROM, "A"}, + {1, "X/Y", "base-deleted", NO_COPY_FROM}, + {2, "A/B", "normal", 1, "A/B", MOVED_HERE}, + {2, "A/Y", "base-deleted", NO_COPY_FROM, "A2/Y"}, + {2, "A2/B", "base-deleted", NO_COPY_FROM, "A/B"}, + {2, "A2/Y", "normal", 1, "X/Y", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_move(&b, "A2", "X")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/Y", "normal", 1, "X/Y"}, + {1, "A", "normal", 1, "X", FALSE, "X", TRUE}, + {1, "A/Y", "normal", 1, "X/Y", MOVED_HERE}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 1, "A", FALSE, "A", TRUE}, + {1, "X/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "X/Y", "base-deleted", NO_COPY_FROM}, + {2, "A/Y", "base-deleted", NO_COPY_FROM, "X/Y"}, + {2, "X/B", "base-deleted", NO_COPY_FROM, "A/B"}, + {2, "A/B", "normal", 1, "A/B", MOVED_HERE}, + {2, "X/Y", "normal", 1, "X/Y", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Revert and try in different order */ + SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity)); + + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + SVN_ERR(sbox_wc_move(&b, "X", "A")); + SVN_ERR(sbox_wc_move(&b, "A2", "X")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/Y", "normal", 1, "X/Y"}, + {1, "A", "normal", 1, "X", FALSE, "X", TRUE}, + {1, "A/Y", "normal", 1, "X/Y", MOVED_HERE}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 1, "A", FALSE, "A", TRUE}, + {1, "X/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "X/Y", "base-deleted", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_move(&b, "A/Y", "X/Y")); + SVN_ERR(sbox_wc_move(&b, "X/B", "A/B")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/Y", "normal", 1, "X/Y"}, + {1, "A", "normal", 1, "X", FALSE, "X", TRUE}, + {1, "A/Y", "normal", 1, "X/Y", MOVED_HERE}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 1, "A", FALSE, "A", TRUE}, + {1, "X/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "X/Y", "base-deleted", NO_COPY_FROM}, + {2, "A/Y", "base-deleted", NO_COPY_FROM, "X/Y"}, + {2, "X/B", "base-deleted", NO_COPY_FROM, "A/B"}, + {2, "A/B", "normal", 1, "A/B", MOVED_HERE}, + {2, "X/Y", "normal", 1, "X/Y", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* And move this last bit back and check if the db state is restored */ + SVN_ERR(sbox_wc_move(&b, "A/B", "X/B")); + SVN_ERR(sbox_wc_move(&b, "X/Y", "A/Y")); + + { + /* Exact the same as before the initial moves */ + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/Y", "normal", 1, "X/Y"}, + {1, "A", "normal", 1, "X", FALSE, "X", TRUE}, + {1, "A/Y", "normal", 1, "X/Y", MOVED_HERE}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 1, "A", FALSE, "A", TRUE}, + {1, "X/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "X/Y", "base-deleted", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* And try to undo the rest */ + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + SVN_ERR(sbox_wc_move(&b, "X", "A")); + SVN_ERR(sbox_wc_move(&b, "A2", "X")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/Y", "normal", 1, "X/Y"}, + + {0} + }; + + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +revert_nested_move(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + nodes_row_t nodes_A_moved[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + nodes_row_t nodes_AB_moved[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {2, "A2/B", "base-deleted", NO_COPY_FROM, "A2/B2"}, + {2, "A2/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A2/B2", "normal", 1, "A/B", MOVED_HERE}, + {2, "A2/B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + nodes_row_t nodes_ABC_moved[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {2, "A2/B", "base-deleted", NO_COPY_FROM, "A2/B2"}, + {2, "A2/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A2/B2", "normal", 1, "A/B", MOVED_HERE}, + {2, "A2/B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {3, "A2/B2/C", "base-deleted", NO_COPY_FROM, "A2/B2/C2"}, + {3, "A2/B2/C2", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + nodes_row_t nodes_AB_moved_C_copied[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {2, "A2/B", "base-deleted", NO_COPY_FROM, "A2/B2"}, + {2, "A2/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A2/B2", "normal", 1, "A/B", MOVED_HERE}, + {2, "A2/B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {3, "A2/B2/C2", "normal", 1, "A/B/C"}, + {0} + }; + nodes_row_t nodes_AC_moved_B_copied[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {2, "A2/B2", "normal", 1, "A/B"}, + {2, "A2/B2/C", "normal", 1, "A/B/C"}, + {3, "A2/B2/C", "base-deleted", NO_COPY_FROM, "A2/B2/C2"}, + {3, "A2/B2/C2", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + + SVN_ERR(svn_test__sandbox_create(&b, "revert_nested_move", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + SVN_ERR(check_db_rows(&b, "", nodes_A_moved)); + + SVN_ERR(sbox_wc_move(&b, "A2/B", "A2/B2")); + SVN_ERR(check_db_rows(&b, "", nodes_AB_moved)); + + SVN_ERR(sbox_wc_move(&b, "A2/B2/C", "A2/B2/C2")); + SVN_ERR(check_db_rows(&b, "", nodes_ABC_moved)); + + SVN_ERR(sbox_wc_revert(&b, "A2/B", svn_depth_infinity)); + SVN_ERR(sbox_wc_revert(&b, "A2/B2", svn_depth_infinity)); + SVN_ERR(check_db_rows(&b, "", nodes_A_moved)); + + SVN_ERR(sbox_wc_move(&b, "A2/B", "A2/B2")); + SVN_ERR(sbox_wc_move(&b, "A2/B2/C", "A2/B2/C2")); + SVN_ERR(check_db_rows(&b, "", nodes_ABC_moved)); + + SVN_ERR(sbox_wc_revert(&b, "A2/B2/C", svn_depth_empty)); + SVN_ERR(check_db_rows(&b, "", nodes_AB_moved_C_copied)); + SVN_ERR(sbox_wc_revert(&b, "A2/B2/C2", svn_depth_infinity)); + SVN_ERR(check_db_rows(&b, "", nodes_AB_moved)); + + SVN_ERR(sbox_wc_move(&b, "A2/B2/C", "A2/B2/C2")); + SVN_ERR(check_db_rows(&b, "", nodes_ABC_moved)); + + SVN_ERR(sbox_wc_revert(&b, "A2/B2/C", svn_depth_infinity)); + SVN_ERR(check_db_rows(&b, "", nodes_AB_moved_C_copied)); + SVN_ERR(sbox_wc_revert(&b, "A2/B2/C2", svn_depth_infinity)); + SVN_ERR(check_db_rows(&b, "", nodes_AB_moved)); + + SVN_ERR(sbox_wc_revert(&b, "A2/B", svn_depth_infinity)); + SVN_ERR(sbox_wc_revert(&b, "A2/B2", svn_depth_infinity)); + SVN_ERR(check_db_rows(&b, "", nodes_A_moved)); + + /* Check moves in reverse order */ + SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity)); + SVN_ERR(sbox_wc_move(&b, "A/B/C", "A/B/C2")); + SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2")); + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + SVN_ERR(check_db_rows(&b, "", nodes_ABC_moved)); + + SVN_ERR(sbox_wc_revert(&b, "A2/B", svn_depth_infinity)); + SVN_ERR(check_db_rows(&b, "", nodes_AC_moved_B_copied)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_on_move(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_on_move", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_mkdir(&b, "X/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A/B", "B2")); + SVN_ERR(sbox_wc_delete(&b, "A")); + SVN_ERR(sbox_wc_copy(&b, "X", "A")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/B", "normal", 1, "X/B"}, + {1, "B2", "normal", 1, "A/B", MOVED_HERE}, + {1, "A", "normal", 1, "X"}, + {1, "A/B", "normal", 1, "X/B", FALSE, "B2"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_move(&b, "A/B", "B3")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/B", "normal", 1, "X/B"}, + {1, "B2", "normal", 1, "A/B", MOVED_HERE}, + {1, "B3", "normal", 1, "X/B", MOVED_HERE}, + {1, "A", "normal", 1, "X"}, + {1, "A/B", "normal", 1, "X/B", FALSE, "B2"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "B3"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_on_move2(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_on_move2", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_mkdir(&b, "X/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + SVN_ERR(sbox_wc_delete(&b, "A")); + SVN_ERR(sbox_wc_copy(&b, "X", "A")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/B", "normal", 1, "X/B"}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A", "normal", 1, "X", FALSE, "A2"}, + {1, "A/B", "normal", 1, "X/B"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_move(&b, "A/B", "B3")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "X", "normal", 1, "X"}, + {0, "X/B", "normal", 1, "X/B"}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "B3", "normal", 1, "X/B", MOVED_HERE}, + {1, "A", "normal", 1, "X", FALSE, "A2"}, + {1, "A/B", "normal", 1, "X/B"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "B3"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_added(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_added", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + SVN_ERR(sbox_wc_mkdir(&b, "A2/B/C2")); + + /* Both A2/B/C and A2/B/C2 are simple adds inside the move. It + doesn't seem right for A2/B/C to be marked moved_here. */ + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {3, "A2/B/C", "normal", NO_COPY_FROM}, + {3, "A2/B/C2", "normal", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +/* Test the result of 'update' when the incoming changes are inside a + * directory that is locally moved. */ +static svn_error_t * +move_update(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_update", opts, pool)); + + /* r1: Create files 'f', 'h' */ + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + sbox_file_write(&b, "A/B/f", "r1 content\n"); + sbox_file_write(&b, "A/B/h", "r1 content\n"); + SVN_ERR(sbox_wc_add(&b, "A/B/f")); + SVN_ERR(sbox_wc_add(&b, "A/B/h")); + SVN_ERR(sbox_wc_commit(&b, "")); + + /* r2: Modify 'f' */ + sbox_file_write(&b, "A/B/f", "r1 content\nr2 content\n"); + SVN_ERR(sbox_wc_commit(&b, "")); + + /* r3: Delete 'h', add 'g' */ + sbox_file_write(&b, "A/B/g", "r3 content\n"); + SVN_ERR(sbox_wc_add(&b, "A/B/g")); + SVN_ERR(sbox_wc_delete(&b, "A/B/h")); + SVN_ERR(sbox_wc_commit(&b, "")); + + /* r4: Add a new subtree 'X' */ + SVN_ERR(sbox_wc_mkdir(&b, "X")); + sbox_file_write(&b, "X/f", "r4 content\n"); + sbox_file_write(&b, "X/g", "r4 content\n"); + sbox_file_write(&b, "X/h", "r4 content\n"); + SVN_ERR(sbox_wc_add(&b, "X/f")); + SVN_ERR(sbox_wc_add(&b, "X/g")); + SVN_ERR(sbox_wc_add(&b, "X/h")); + SVN_ERR(sbox_wc_commit(&b, "")); + + /* r5: Add a subtree 'A/B/C' */ + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_update(&b, "", 1)); + + /* A is single-revision so A2 is a single-revision copy */ + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/f", "normal", 1, "A/B/f"}, + {0, "A/B/h", "normal", 1, "A/B/h"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/h", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 1, "A/B/f", MOVED_HERE}, + {1, "A2/B/h", "normal", 1, "A/B/h", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Update causes a tree-conflict on A due to incoming text-change. */ + SVN_ERR(sbox_wc_update(&b, "", 2)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/f", "normal", 2, "A/B/f"}, + {0, "A/B/h", "normal", 2, "A/B/h"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/h", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 1, "A/B/f", MOVED_HERE}, + {1, "A2/B/h", "normal", 1, "A/B/h", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Resolve should update the move. */ + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/f", "normal", 2, "A/B/f"}, + {0, "A/B/h", "normal", 2, "A/B/h"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/h", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 2, "A", MOVED_HERE}, + {1, "A2/B", "normal", 2, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 2, "A/B/f", MOVED_HERE}, + {1, "A2/B/h", "normal", 2, "A/B/h", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Update causes a tree-conflict on due to incoming add. */ + SVN_ERR(sbox_wc_update(&b, "", 3)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 3, ""}, + {0, "A", "normal", 3, "A"}, + {0, "A/B", "normal", 3, "A/B"}, + {0, "A/B/f", "normal", 3, "A/B/f"}, + {0, "A/B/g", "normal", 3, "A/B/g"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/g", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 2, "A", MOVED_HERE}, + {1, "A2/B", "normal", 2, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 2, "A/B/f", MOVED_HERE}, + {1, "A2/B/h", "normal", 2, "A/B/h", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 3, ""}, + {0, "A", "normal", 3, "A"}, + {0, "A/B", "normal", 3, "A/B"}, + {0, "A/B/f", "normal", 3, "A/B/f"}, + {0, "A/B/g", "normal", 3, "A/B/g"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/g", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 3, "A", MOVED_HERE}, + {1, "A2/B", "normal", 3, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 3, "A/B/f", MOVED_HERE}, + {1, "A2/B/g", "normal", 3, "A/B/g", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_delete(&b, "A2/B")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 3, ""}, + {0, "A", "normal", 3, "A"}, + {0, "A/B", "normal", 3, "A/B"}, + {0, "A/B/f", "normal", 3, "A/B/f"}, + {0, "A/B/g", "normal", 3, "A/B/g"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/g", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 3, "A", MOVED_HERE}, + {1, "A2/B", "normal", 3, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 3, "A/B/f", MOVED_HERE}, + {1, "A2/B/g", "normal", 3, "A/B/g", MOVED_HERE}, + {2, "A2/B", "base-deleted", NO_COPY_FROM}, + {2, "A2/B/f", "base-deleted", NO_COPY_FROM}, + {2, "A2/B/g", "base-deleted", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "", 2)); + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/f", "normal", 2, "A/B/f"}, + {0, "A/B/h", "normal", 2, "A/B/h"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/h", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 2, "A", MOVED_HERE}, + {1, "A2/B", "normal", 2, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 2, "A/B/f", MOVED_HERE}, + {1, "A2/B/h", "normal", 2, "A/B/h", MOVED_HERE}, + {2, "A2/B", "base-deleted", NO_COPY_FROM}, + {2, "A2/B/f", "base-deleted", NO_COPY_FROM}, + {2, "A2/B/h", "base-deleted", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "X", 4)); + SVN_ERR(sbox_wc_copy(&b, "X", "A2/B")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/f", "normal", 2, "A/B/f"}, + {0, "A/B/h", "normal", 2, "A/B/h"}, + {0, "X", "normal", 4, "X"}, + {0, "X/f", "normal", 4, "X/f"}, + {0, "X/g", "normal", 4, "X/g"}, + {0, "X/h", "normal", 4, "X/h"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/h", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 2, "A", MOVED_HERE}, + {1, "A2/B", "normal", 2, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 2, "A/B/f", MOVED_HERE}, + {1, "A2/B/h", "normal", 2, "A/B/h", MOVED_HERE}, + {2, "A2/B", "normal", 4, "X"}, + {2, "A2/B/f", "normal", 4, "X/f"}, + {2, "A2/B/g", "normal", 4, "X/g"}, + {2, "A2/B/h", "normal", 4, "X/h"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "", 4)); + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 4, ""}, + {0, "A", "normal", 4, "A"}, + {0, "A/B", "normal", 4, "A/B"}, + {0, "A/B/f", "normal", 4, "A/B/f"}, + {0, "A/B/g", "normal", 4, "A/B/g"}, + {0, "X", "normal", 4, "X"}, + {0, "X/f", "normal", 4, "X/f"}, + {0, "X/g", "normal", 4, "X/g"}, + {0, "X/h", "normal", 4, "X/h"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/g", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 4, "A", MOVED_HERE}, + {1, "A2/B", "normal", 4, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 4, "A/B/f", MOVED_HERE}, + {1, "A2/B/g", "normal", 4, "A/B/g", MOVED_HERE}, + {2, "A2/B", "normal", 4, "X"}, + {2, "A2/B/f", "normal", 4, "X/f"}, + {2, "A2/B/g", "normal", 4, "X/g"}, + {2, "A2/B/h", "normal", 4, "X/h"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "", 5)); + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 5, ""}, + {0, "A", "normal", 5, "A"}, + {0, "A/B", "normal", 5, "A/B"}, + {0, "A/B/f", "normal", 5, "A/B/f"}, + {0, "A/B/g", "normal", 5, "A/B/g"}, + {0, "A/B/C", "normal", 5, "A/B/C"}, + {0, "X", "normal", 5, "X"}, + {0, "X/f", "normal", 5, "X/f"}, + {0, "X/g", "normal", 5, "X/g"}, + {0, "X/h", "normal", 5, "X/h"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/g", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 5, "A", MOVED_HERE}, + {1, "A2/B", "normal", 5, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 5, "A/B/f", MOVED_HERE}, + {1, "A2/B/g", "normal", 5, "A/B/g", MOVED_HERE}, + {1, "A2/B/C", "normal", 5, "A/B/C", MOVED_HERE}, + {2, "A2/B", "normal", 4, "X"}, + {2, "A2/B/f", "normal", 4, "X/f"}, + {2, "A2/B/g", "normal", 4, "X/g"}, + {2, "A2/B/h", "normal", 4, "X/h"}, + {2, "A2/B/C", "base-deleted", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + + return SVN_NO_ERROR; +} + +static svn_error_t * +check_moved_to(apr_array_header_t *moved_tos, + int i, + int op_depth, + const char *local_relpath) +{ + struct svn_wc__db_moved_to_t *moved_to; + + if (i >= moved_tos->nelts) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "moved-to %d not found", i); + + moved_to = APR_ARRAY_IDX(moved_tos, i, struct svn_wc__db_moved_to_t *); + + if (moved_to->op_depth != op_depth + || strcmp(moved_to->local_relpath, local_relpath)) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "expected: {%d '%s'} found[%d]: {%d '%s'}", + op_depth, local_relpath, i, + moved_to->op_depth, moved_to->local_relpath); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_scan_delete(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + const char *moved_to_abspath, *moved_to_op_root_abspath; + apr_array_header_t *moved_tos; + + SVN_ERR(svn_test__sandbox_create(&b, "scan_delete", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A2")); + SVN_ERR(sbox_wc_mkdir(&b, "A2/B")); + SVN_ERR(sbox_wc_mkdir(&b, "C2")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A2", "X")); + SVN_ERR(sbox_wc_move(&b, "X/B", "Z")); + SVN_ERR(sbox_wc_move(&b, "A/B", "X/B")); + SVN_ERR(sbox_wc_move(&b, "X/B/C", "Y")); + SVN_ERR(sbox_wc_move(&b, "C2", "X/B/C")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {0, "A2", "normal", 1, "A2"}, + {0, "A2/B", "normal", 1, "A2/B"}, + {0, "C2", "normal", 1, "C2"}, + {1, "A2", "base-deleted", NO_COPY_FROM, "X"}, + {1, "A2/B", "base-deleted", NO_COPY_FROM}, + {1, "Z", "normal", 1, "A2/B", MOVED_HERE}, + {1, "X", "normal", 1, "A2", MOVED_HERE}, + {1, "X/B", "normal", 1, "A2/B", MOVED_HERE}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "X/B"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "X/B", "normal", 1, "A/B", FALSE, "Z", TRUE}, + {2, "X/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {1, "Y", "normal", 1, "A/B/C", MOVED_HERE}, + {1, "C2", "base-deleted", NO_COPY_FROM, "X/B/C"}, + {3, "X/B/C", "normal", 1, "C2", FALSE, "Y", TRUE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath, + NULL, &moved_to_op_root_abspath, + b.wc_ctx->db, sbox_wc_path(&b, "C2"), + pool, pool)); + SVN_TEST_STRING_ASSERT(moved_to_abspath, sbox_wc_path(&b, "X/B/C")); + SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, sbox_wc_path(&b, "X/B/C")); + + SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath, + NULL, &moved_to_op_root_abspath, + b.wc_ctx->db, sbox_wc_path(&b, "A/B"), + pool, pool)); + SVN_TEST_STRING_ASSERT(moved_to_abspath, sbox_wc_path(&b, "X/B")); + SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, sbox_wc_path(&b, "X/B")); + + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A/B/C"), pool, pool)); + SVN_ERR(check_moved_to(moved_tos, 0, 2, "X/B/C")); + SVN_ERR(check_moved_to(moved_tos, 1, 3, "Y")); + SVN_TEST_ASSERT(moved_tos->nelts == 2); + + SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath, + NULL, &moved_to_op_root_abspath, + b.wc_ctx->db, sbox_wc_path(&b, "A/B/C"), + pool, pool)); + SVN_TEST_STRING_ASSERT(moved_to_abspath, sbox_wc_path(&b, "X/B/C")); + SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, sbox_wc_path(&b, "X/B")); + + SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath, + NULL, &moved_to_op_root_abspath, + b.wc_ctx->db, sbox_wc_path(&b, "A2"), + pool, pool)); + SVN_TEST_STRING_ASSERT(moved_to_abspath, sbox_wc_path(&b, "X")); + SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, sbox_wc_path(&b, "X")); + + SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath, + NULL, &moved_to_op_root_abspath, + b.wc_ctx->db, sbox_wc_path(&b, "A2/B"), + pool, pool)); + SVN_TEST_STRING_ASSERT(moved_to_abspath, sbox_wc_path(&b, "X/B")); + SVN_TEST_STRING_ASSERT(moved_to_op_root_abspath, sbox_wc_path(&b, "X")); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_follow_moved_to(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + apr_array_header_t *moved_tos; + + SVN_ERR(svn_test__sandbox_create(&b, "follow_moved_to", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A1")); + SVN_ERR(sbox_wc_mkdir(&b, "A1/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A1/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A1/B/C/D")); + SVN_ERR(sbox_wc_mkdir(&b, "A1/B/C/D/E")); + SVN_ERR(sbox_wc_mkdir(&b, "A2")); + SVN_ERR(sbox_wc_mkdir(&b, "A2/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A2/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A2/B/C/D")); + SVN_ERR(sbox_wc_mkdir(&b, "A2/B/C/D/E")); + SVN_ERR(sbox_wc_mkdir(&b, "A3")); + SVN_ERR(sbox_wc_mkdir(&b, "A3/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A3/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A3/B/C/D")); + SVN_ERR(sbox_wc_mkdir(&b, "A3/B/C/D/E")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A1", "normal", 1, "A1"}, + {0, "A1/B", "normal", 1, "A1/B"}, + {0, "A1/B/C", "normal", 1, "A1/B/C"}, + {0, "A1/B/C/D", "normal", 1, "A1/B/C/D"}, + {0, "A1/B/C/D/E", "normal", 1, "A1/B/C/D/E"}, + {0, "A2", "normal", 1, "A2"}, + {0, "A2/B", "normal", 1, "A2/B"}, + {0, "A2/B/C", "normal", 1, "A2/B/C"}, + {0, "A2/B/C/D", "normal", 1, "A2/B/C/D"}, + {0, "A2/B/C/D/E", "normal", 1, "A2/B/C/D/E"}, + {0, "A3", "normal", 1, "A3"}, + {0, "A3/B", "normal", 1, "A3/B"}, + {0, "A3/B/C", "normal", 1, "A3/B/C"}, + {0, "A3/B/C/D", "normal", 1, "A3/B/C/D"}, + {0, "A3/B/C/D/E", "normal", 1, "A3/B/C/D/E"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_move(&b, "A1", "X")); + SVN_ERR(sbox_wc_move(&b, "A2", "A1")); + SVN_ERR(sbox_wc_move(&b, "A3", "A2")); + SVN_ERR(sbox_wc_move(&b, "X", "A3")); + SVN_ERR(sbox_wc_move(&b, "A1/B", "X")); + SVN_ERR(sbox_wc_move(&b, "A2/B", "A1/B")); + SVN_ERR(sbox_wc_move(&b, "A3/B", "A2/B")); + SVN_ERR(sbox_wc_move(&b, "X", "A3/B")); + SVN_ERR(sbox_wc_move(&b, "A1/B/C/D", "X")); + SVN_ERR(sbox_wc_move(&b, "A2/B/C/D", "A1/B/C/D")); + SVN_ERR(sbox_wc_move(&b, "A3/B/C/D", "A2/B/C/D")); + SVN_ERR(sbox_wc_move(&b, "X", "A3/B/C/D")); + SVN_ERR(sbox_wc_move(&b, "A1/B/C/D/E", "X")); + SVN_ERR(sbox_wc_move(&b, "A2/B/C/D/E", "A1/B/C/D/E")); + SVN_ERR(sbox_wc_move(&b, "A3/B/C/D/E", "A2/B/C/D/E")); + SVN_ERR(sbox_wc_move(&b, "X", "A3/B/C/D/E")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A1", "normal", 1, "A1"}, + {0, "A1/B", "normal", 1, "A1/B"}, + {0, "A1/B/C", "normal", 1, "A1/B/C"}, + {0, "A1/B/C/D", "normal", 1, "A1/B/C/D"}, + {0, "A1/B/C/D/E", "normal", 1, "A1/B/C/D/E"}, + {0, "A2", "normal", 1, "A2"}, + {0, "A2/B", "normal", 1, "A2/B"}, + {0, "A2/B/C", "normal", 1, "A2/B/C"}, + {0, "A2/B/C/D", "normal", 1, "A2/B/C/D"}, + {0, "A2/B/C/D/E", "normal", 1, "A2/B/C/D/E"}, + {0, "A3", "normal", 1, "A3"}, + {0, "A3/B", "normal", 1, "A3/B"}, + {0, "A3/B/C", "normal", 1, "A3/B/C"}, + {0, "A3/B/C/D", "normal", 1, "A3/B/C/D"}, + {0, "A3/B/C/D/E", "normal", 1, "A3/B/C/D/E"}, + + {1, "A1", "normal", 1, "A2", FALSE, "A3", TRUE}, + {1, "A1/B", "normal", 1, "A2/B", MOVED_HERE}, + {1, "A1/B/C", "normal", 1, "A2/B/C", MOVED_HERE}, + {1, "A1/B/C/D", "normal", 1, "A2/B/C/D", MOVED_HERE}, + {1, "A1/B/C/D/E", "normal", 1, "A2/B/C/D/E", MOVED_HERE}, + + {1, "A2", "normal", 1, "A3", FALSE, "A1", TRUE}, + {1, "A2/B", "normal", 1, "A3/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A3/B/C", MOVED_HERE}, + {1, "A2/B/C/D", "normal", 1, "A3/B/C/D", MOVED_HERE}, + {1, "A2/B/C/D/E", "normal", 1, "A3/B/C/D/E", MOVED_HERE}, + + {1, "A3", "normal", 1, "A1", FALSE, "A2", TRUE}, + {1, "A3/B", "normal", 1, "A1/B", MOVED_HERE}, + {1, "A3/B/C", "normal", 1, "A1/B/C", MOVED_HERE}, + {1, "A3/B/C/D", "normal", 1, "A1/B/C/D", MOVED_HERE}, + {1, "A3/B/C/D/E", "normal", 1, "A1/B/C/D/E", MOVED_HERE}, + + {2, "A1/B", "normal", 1, "A3/B", FALSE, "A3/B", TRUE}, + {2, "A1/B/C", "normal", 1, "A3/B/C", MOVED_HERE}, + {2, "A1/B/C/D", "normal", 1, "A3/B/C/D", MOVED_HERE}, + {2, "A1/B/C/D/E", "normal", 1, "A3/B/C/D/E", MOVED_HERE}, + + {2, "A2/B", "normal", 1, "A1/B", FALSE, "A1/B", TRUE}, + {2, "A2/B/C", "normal", 1, "A1/B/C", MOVED_HERE}, + {2, "A2/B/C/D", "normal", 1, "A1/B/C/D", MOVED_HERE}, + {2, "A2/B/C/D/E", "normal", 1, "A1/B/C/D/E", MOVED_HERE}, + + {2, "A3/B", "normal", 1, "A2/B", FALSE, "A2/B", TRUE}, + {2, "A3/B/C", "normal", 1, "A2/B/C", MOVED_HERE}, + {2, "A3/B/C/D", "normal", 1, "A2/B/C/D", MOVED_HERE}, + {2, "A3/B/C/D/E", "normal", 1, "A2/B/C/D/E", MOVED_HERE}, + + {4, "A1/B/C/D", "normal", 1, "A1/B/C/D", FALSE, "A3/B/C/D", TRUE}, + {4, "A1/B/C/D/E", "normal", 1, "A1/B/C/D/E", MOVED_HERE}, + + {4, "A2/B/C/D", "normal", 1, "A2/B/C/D", FALSE, "A1/B/C/D", TRUE}, + {4, "A2/B/C/D/E", "normal", 1, "A2/B/C/D/E", MOVED_HERE}, + + {4, "A3/B/C/D", "normal", 1, "A3/B/C/D", FALSE, "A2/B/C/D", TRUE}, + {4, "A3/B/C/D/E", "normal", 1, "A3/B/C/D/E", MOVED_HERE}, + + {5, "A1/B/C/D/E", "normal", 1, "A2/B/C/D/E", FALSE, "A3/B/C/D/E", TRUE}, + {5, "A2/B/C/D/E", "normal", 1, "A3/B/C/D/E", FALSE, "A1/B/C/D/E", TRUE}, + {5, "A3/B/C/D/E", "normal", 1, "A1/B/C/D/E", FALSE, "A2/B/C/D/E", TRUE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* A1->A3, A3/B->A2/B, A2/B/C/D->A1/B/C/D, A1/B/C/D/E->A3/B/C/D/E */ + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A1"), pool, pool)); + SVN_ERR(check_moved_to(moved_tos, 0, 1, "A3")); + SVN_TEST_ASSERT(moved_tos->nelts == 1); + + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A1/B"), pool, pool)); + SVN_ERR(check_moved_to(moved_tos, 0, 1, "A3/B")); + SVN_ERR(check_moved_to(moved_tos, 1, 2, "A2/B")); + SVN_TEST_ASSERT(moved_tos->nelts == 2); + + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A1/B/C"), pool, pool)); + SVN_ERR(check_moved_to(moved_tos, 0, 1, "A3/B/C")); + SVN_ERR(check_moved_to(moved_tos, 1, 2, "A2/B/C")); + SVN_TEST_ASSERT(moved_tos->nelts == 2); + + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A1/B/C/D"), pool, pool)); + SVN_ERR(check_moved_to(moved_tos, 0, 1, "A3/B/C/D")); + SVN_ERR(check_moved_to(moved_tos, 1, 2, "A2/B/C/D")); + SVN_ERR(check_moved_to(moved_tos, 2, 4, "A1/B/C/D")); + SVN_TEST_ASSERT(moved_tos->nelts == 3); + + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A1/B/C/D/E"), pool, pool)); + SVN_ERR(check_moved_to(moved_tos, 0, 1, "A3/B/C/D/E")); + SVN_ERR(check_moved_to(moved_tos, 1, 2, "A2/B/C/D/E")); + SVN_ERR(check_moved_to(moved_tos, 2, 4, "A1/B/C/D/E")); + SVN_ERR(check_moved_to(moved_tos, 3, 5, "A3/B/C/D/E")); + SVN_TEST_ASSERT(moved_tos->nelts == 4); + + SVN_ERR(sbox_wc_delete(&b, "A3/B/C/D/E")); + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A1/B/C/D/E"), pool, pool)); + SVN_ERR(check_moved_to(moved_tos, 0, 1, "A3/B/C/D/E")); + SVN_ERR(check_moved_to(moved_tos, 1, 2, "A2/B/C/D/E")); + SVN_ERR(check_moved_to(moved_tos, 2, 4, "A1/B/C/D/E")); + SVN_TEST_ASSERT(moved_tos->nelts == 3); + + return SVN_NO_ERROR; +} + +static svn_error_t * +mixed_rev_move(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + apr_array_header_t *moved_tos; + + SVN_ERR(svn_test__sandbox_create(&b, "mixed_rev_move", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 0, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 3, "A/B/C"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* We don't allow mixed-rev move in 1.8 and the command line client + will return an error, but for compatibility with 1.7 move has an + allow_mixed_revisions=TRUE flag which is being used here so the + move transforms automatically into copy+delete. This test was + written before that transforming was implemented so still expects + some move information. */ + SVN_ERR(sbox_wc_move(&b, "A", "X")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 0, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 3, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "X"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 1, "A", MOVED_HERE}, + {1, "X/B", "not-present", 2, "A/B"}, + {2, "X/B", "normal", 2, "A/B"}, + {2, "X/B/C", "not-present", 3, "A/B/C"}, + {3, "X/B/C", "normal", 3, "A/B/C"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* ### These values PASS but I'm not sure they are correct. */ + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A/B/C"), pool, pool)); + SVN_ERR(check_moved_to(moved_tos, 0, 1, "X/B/C")); + SVN_TEST_ASSERT(moved_tos->nelts == 1); + + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A/B"), pool, pool)); + SVN_ERR(check_moved_to(moved_tos, 0, 1, "X/B")); + SVN_TEST_ASSERT(moved_tos->nelts == 1); + + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A"), pool, pool)); + SVN_ERR(check_moved_to(moved_tos, 0, 1, "X")); + SVN_TEST_ASSERT(moved_tos->nelts == 1); + + + /* This move doesn't record moved-to */ + SVN_ERR(sbox_wc_move(&b, "X/B", "X/Y")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 0, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 3, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "X"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 1, "A", MOVED_HERE}, + {1, "X/B", "not-present", 2, "A/B"}, + {2, "X/Y", "normal", 2, "A/B"}, + {2, "X/Y/C", "not-present", NO_COPY_FROM}, + {3, "X/Y/C", "normal", 3, "A/B/C"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A/B/C"), pool, pool)); + SVN_TEST_ASSERT(moved_tos->nelts == 0); + + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A/B"), pool, pool)); + SVN_TEST_ASSERT(moved_tos->nelts == 0); + + SVN_ERR(svn_wc__db_follow_moved_to(&moved_tos, b.wc_ctx->db, + sbox_wc_path(&b, "A"), pool, pool)); + SVN_ERR(check_moved_to(moved_tos, 0, 1, "X")); + SVN_TEST_ASSERT(moved_tos->nelts == 1); + + return SVN_NO_ERROR; +} + +/* Test the result of 'update' when the incoming changes are inside a + * directory that is locally moved. */ +static svn_error_t * +update_prop_mod_into_moved(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "update_prop_mod_into_moved", opts, pool)); + + /* r1: Create files 'f', 'h' */ + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + sbox_file_write(&b, "A/B/f", "r1 content\n"); + sbox_file_write(&b, "A/B/h", "r1 content\n"); + SVN_ERR(sbox_wc_add(&b, "A/B/f")); + SVN_ERR(sbox_wc_add(&b, "A/B/h")); + SVN_ERR(sbox_wc_propset(&b, "pd", "f1", "A/B/f")); + SVN_ERR(sbox_wc_propset(&b, "pn", "f1", "A/B/f")); + SVN_ERR(sbox_wc_propset(&b, "pm", "f1", "A/B/f")); + SVN_ERR(sbox_wc_propset(&b, "p", "h1", "A/B/h")); + SVN_ERR(sbox_wc_commit(&b, "")); + + /* r2: Modify 'f'. Delete prop 'pd', modify prop 'pm', add prop 'pa', + * leave prop 'pn' unchanged. */ + sbox_file_write(&b, "A/B/f", "r1 content\nr2 content\n"); + SVN_ERR(sbox_wc_propset(&b, "pd", NULL, "A/B/f")); + SVN_ERR(sbox_wc_propset(&b, "pm", "f2", "A/B/f")); + SVN_ERR(sbox_wc_propset(&b, "pa", "f2", "A/B/f")); + SVN_ERR(sbox_wc_commit(&b, "")); + + /* r3: Delete 'h', add 'g' */ + sbox_file_write(&b, "A/B/g", "r3 content\n"); + SVN_ERR(sbox_wc_add(&b, "A/B/g")); + SVN_ERR(sbox_wc_propset(&b, "p", "g3", "A/B/g")); + SVN_ERR(sbox_wc_delete(&b, "A/B/h")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_update(&b, "", 1)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/f", "normal", 1, "A/B/f", NOT_MOVED, "pd,pm,pn"}, + {0, "A/B/h", "normal", 1, "A/B/h", NOT_MOVED, "p"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* A is single-revision so A2 is a single-revision copy */ + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/f", "normal", 1, "A/B/f", NOT_MOVED, "pd,pm,pn"}, + {0, "A/B/h", "normal", 1, "A/B/h", NOT_MOVED, "p"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/h", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 1, "A/B/f", MOVED_HERE, "pd,pm,pn"}, + {1, "A2/B/h", "normal", 1, "A/B/h", MOVED_HERE, "p"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Update causes a tree-conflict on A due to incoming text-change. */ + SVN_ERR(sbox_wc_update(&b, "", 2)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/f", "normal", 2, "A/B/f", NOT_MOVED, "pa,pm,pn"}, + {0, "A/B/h", "normal", 2, "A/B/h", NOT_MOVED, "p"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/h", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 1, "A/B/f", MOVED_HERE, "pd,pm,pn"}, + {1, "A2/B/h", "normal", 1, "A/B/h", MOVED_HERE, "p"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Resolve should update the move. */ + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/f", "normal", 2, "A/B/f", NOT_MOVED, "pa,pm,pn"}, + {0, "A/B/h", "normal", 2, "A/B/h", NOT_MOVED, "p"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/f", "base-deleted", NO_COPY_FROM}, + {1, "A/B/h", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 2, "A", MOVED_HERE}, + {1, "A2/B", "normal", 2, "A/B", MOVED_HERE}, + {1, "A2/B/f", "normal", 2, "A/B/f", MOVED_HERE, "pa,pm,pn"}, + {1, "A2/B/h", "normal", 2, "A/B/h", MOVED_HERE, "p"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +nested_move_update(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "nested_move_update", opts, pool)); + + /* r1: Create file 'f' */ + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + sbox_file_write(&b, "A/B/C/f", "r1 content\n"); + SVN_ERR(sbox_wc_add(&b, "A/B/C/f")); + SVN_ERR(sbox_wc_commit(&b, "")); + + /* r2: Modify 'f' */ + sbox_file_write(&b, "A/B/C/f", "r1 content\nr2 content\n"); + SVN_ERR(sbox_wc_commit(&b, "")); + + /* r3: Create 'X' */ + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + SVN_ERR(sbox_wc_move(&b, "A2/B/C", "A2/B/C2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {0, "A/B/C/f", "normal", 1, "A/B/C/f"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/f", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {1, "A2/B/C/f", "normal", 1, "A/B/C/f", MOVED_HERE}, + {3, "A2/B/C", "base-deleted", NO_COPY_FROM, "A2/B/C2"}, + {3, "A2/B/C/f", "base-deleted", NO_COPY_FROM}, + {3, "A2/B/C2", "normal", 1, "A/B/C", MOVED_HERE}, + {3, "A2/B/C2/f", "normal", 1, "A/B/C/f", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "", 2)); + + /* Following the A->A2 move should raise a tree-conflict on A2/B/C, + resolving that may require an explicit resolve. */ + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + SVN_ERR(sbox_wc_resolve(&b, "A2/B/C", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/f", "normal", 2, "A/B/C/f"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/f", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 2, "A", MOVED_HERE}, + {1, "A2/B", "normal", 2, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 2, "A/B/C", MOVED_HERE}, + {1, "A2/B/C/f", "normal", 2, "A/B/C/f", MOVED_HERE}, + {3, "A2/B/C", "base-deleted", NO_COPY_FROM, "A2/B/C2"}, + {3, "A2/B/C/f", "base-deleted", NO_COPY_FROM}, + {3, "A2/B/C2", "normal", 2, "A/B/C", MOVED_HERE}, + {3, "A2/B/C2/f", "normal", 2, "A/B/C/f", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Update A to r3 brings no changes but updates the revisions. */ + SVN_ERR(sbox_wc_update(&b, "A", 3)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 3, "A"}, + {0, "A/B", "normal", 3, "A/B"}, + {0, "A/B/C", "normal", 3, "A/B/C"}, + {0, "A/B/C/f", "normal", 3, "A/B/C/f"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/f", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 3, "A", MOVED_HERE}, + {1, "A2/B", "normal", 3, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 3, "A/B/C", MOVED_HERE}, + {1, "A2/B/C/f", "normal", 3, "A/B/C/f", MOVED_HERE}, + {3, "A2/B/C", "base-deleted", NO_COPY_FROM, "A2/B/C2"}, + {3, "A2/B/C/f", "base-deleted", NO_COPY_FROM}, + {3, "A2/B/C2", "normal", 3, "A/B/C", MOVED_HERE}, + {3, "A2/B/C2/f", "normal", 3, "A/B/C/f", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +nested_move_commit(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "nested_move_commit", opts, pool)); + + /* r1: Create file 'f' */ + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + sbox_file_write(&b, "A/B/C/f", "r1 content\n"); + SVN_ERR(sbox_wc_add(&b, "A/B/C/f")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A/B/C", "C2")); + + { + const char *moved_to; + const char *expected_to; + SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, NULL, &moved_to, + b.wc_ctx->db, sbox_wc_path(&b, "A/B/C"), + pool, pool)); + + expected_to = sbox_wc_path(&b, "C2"); + + if (strcmp(moved_to, expected_to) != 0) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Expected moved to %s, but was %s", + expected_to, moved_to); + } + { + const char *moved_from; + const char *expected_from; + SVN_ERR(svn_wc__db_scan_moved(&moved_from, NULL, NULL, NULL, + b.wc_ctx->db, sbox_wc_path(&b, "C2"), + pool, pool)); + + expected_from = sbox_wc_path(&b, "A/B/C"); + + if (strcmp(moved_from, expected_from) != 0) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Expected moved from %s, but was %s", + expected_from, moved_from); + } + + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {0, "A/B/C/f", "normal", 1, "A/B/C/f"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/f", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {1, "A2/B/C/f", "normal", 1, "A/B/C/f", MOVED_HERE}, + {3, "A2/B/C", "base-deleted", NO_COPY_FROM, "C2"}, + {3, "A2/B/C/f", "base-deleted", NO_COPY_FROM}, + {1, "C2", "normal", 1, "A/B/C", MOVED_HERE}, + {1, "C2/f", "normal", 1, "A/B/C/f", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + { + const char *moved_to; + const char *expected_to; + SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, NULL, &moved_to, + b.wc_ctx->db, sbox_wc_path(&b, "A/B/C"), + pool, pool)); + + /* A/B/C is part of the A->A2 move. */ + expected_to = sbox_wc_path(&b, "A2"); + if (strcmp(moved_to, expected_to) != 0) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Expected moved to %s, but was %s", + expected_to, moved_to); + + SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, NULL, &moved_to, + b.wc_ctx->db, sbox_wc_path(&b, "A2/B/C"), + pool, pool)); + + /* A2/B/C is the A2/B/C->C2 move. */ + expected_to = sbox_wc_path(&b, "C2"); + if (strcmp(moved_to, expected_to) != 0) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Expected moved to %s, but was %s", + expected_to, moved_to); + } + { + const char *moved_from; + const char *expected_from; + SVN_ERR(svn_wc__db_scan_moved(&moved_from, NULL, NULL, NULL, + b.wc_ctx->db, sbox_wc_path(&b, "C2"), + pool, pool)); + + /* C2 is the A2/B/C->C2 move. */ + expected_from = sbox_wc_path(&b, "A2/B/C"); + if (strcmp(moved_from, expected_from) != 0) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Expected moved from %s, but was %s", + expected_from, moved_from); + } + + { + apr_array_header_t *targets = apr_array_make(pool, 2, sizeof(const char *)); + + APR_ARRAY_PUSH(targets, const char *) = sbox_wc_path(&b, "A"); + APR_ARRAY_PUSH(targets, const char *) = sbox_wc_path(&b, "A2"); + + SVN_ERR(sbox_wc_commit_ex(&b, targets, svn_depth_empty)); + } + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "not-present", 2, "A"}, + {0, "A2", "normal", 2, "A2"}, + {0, "A2/B", "normal", 2, "A2/B"}, + {0, "A2/B/C", "normal", 2, "A2/B/C"}, + {0, "A2/B/C/f", "normal", 2, "A2/B/C/f"}, + {3, "A2/B/C", "base-deleted", NO_COPY_FROM, "C2"}, + {3, "A2/B/C/f", "base-deleted", NO_COPY_FROM}, + + /* These need to have their copyfrom information updated */ + {1, "C2", "normal", 2, "A2/B/C", MOVED_HERE}, + {1, "C2/f", "normal", 2, "A2/B/C/f", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + { + const char *moved_to; + const char *expected_to; + SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, NULL, &moved_to, + b.wc_ctx->db, sbox_wc_path(&b, "A2/B/C"), + pool, pool)); + + expected_to = sbox_wc_path(&b, "C2"); + + if (strcmp(moved_to, expected_to) != 0) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Expected moved to %s, but was %s", + expected_to, moved_to); + } + + { + const char *moved_from; + const char *expected_from; + SVN_ERR(svn_wc__db_scan_moved(&moved_from, NULL, NULL, NULL, + b.wc_ctx->db, sbox_wc_path(&b, "C2"), + pool, pool)); + + expected_from = sbox_wc_path(&b, "A2/B/C"); + + if (strcmp(moved_from, expected_from) != 0) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Expected moved from %s, but was %s", + expected_from, moved_from); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +nested_move_update2(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "nested_move_update2", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "P")); + SVN_ERR(sbox_wc_mkdir(&b, "P/Q")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + SVN_ERR(sbox_wc_move(&b, "P", "A")); + SVN_ERR(sbox_wc_move(&b, "A2", "P")); + SVN_ERR(sbox_wc_move(&b, "A/Q", "A/Q2")); + SVN_ERR(sbox_wc_move(&b, "P/B", "P/B2")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "P", "normal", 1, "P"}, + {0, "P/Q", "normal", 1, "P/Q"}, + {1, "A", "normal", 1, "P", FALSE, "P", TRUE}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/Q", "normal", 1, "P/Q", MOVED_HERE}, + {1, "P", "normal", 1, "A", FALSE, "A", TRUE}, + {1, "P/Q", "base-deleted", NO_COPY_FROM}, + {1, "P/B", "normal", 1, "A/B", MOVED_HERE}, + {2, "A/Q", "base-deleted", NO_COPY_FROM, "A/Q2"}, + {2, "A/Q2", "normal", 1, "P/Q", MOVED_HERE}, + {2, "P/B", "base-deleted", NO_COPY_FROM, "P/B2"}, + {2, "P/B2", "normal", 1, "A/B", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Update A bumps revisions but only for moves originating in A. In + particular A/Q to A/Q2 does not get bumped. */ + SVN_ERR(sbox_wc_update(&b, "A", 2)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "P", "normal", 1, "P"}, + {0, "P/Q", "normal", 1, "P/Q"}, + {1, "A", "normal", 1, "P", FALSE, "P", TRUE}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/Q", "normal", 1, "P/Q", MOVED_HERE}, + {1, "P", "normal", 2, "A", FALSE, "A", TRUE}, + {1, "P/Q", "base-deleted", NO_COPY_FROM}, + {1, "P/B", "normal", 2, "A/B", MOVED_HERE}, + {2, "A/Q", "base-deleted", NO_COPY_FROM, "A/Q2"}, + {2, "A/Q2", "normal", 1, "P/Q", MOVED_HERE}, + {2, "P/B", "base-deleted", NO_COPY_FROM, "P/B2"}, + {2, "P/B2", "normal", 2, "A/B", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +check_tree_conflict_repos_path(svn_test__sandbox_t *b, + const char *wc_path, + const char *repos_path1, + const char *repos_path2) +{ + svn_skel_t *conflict; + svn_wc_operation_t operation; + const apr_array_header_t *locations; + svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted; + + SVN_ERR(svn_wc__db_read_conflict(&conflict, b->wc_ctx->db, + sbox_wc_path(b, wc_path), + b->pool, b->pool)); + + SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, + &text_conflicted, &prop_conflicted, + &tree_conflicted, + b->wc_ctx->db, b->wc_abspath, + conflict, b->pool, b->pool)); + + SVN_ERR_ASSERT(tree_conflicted); + + if (repos_path1) + { + svn_wc_conflict_version_t *version + = APR_ARRAY_IDX(locations, 0, svn_wc_conflict_version_t *); + + SVN_ERR_ASSERT(!strcmp(version->path_in_repos, repos_path1)); + } + + if (repos_path2) + { + svn_wc_conflict_version_t *version + = APR_ARRAY_IDX(locations, 1, svn_wc_conflict_version_t *); + + SVN_ERR_ASSERT(!strcmp(version->path_in_repos, repos_path2)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_update_conflicts(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_update_conflicts", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_mkdir(&b, "X/A")); + SVN_ERR(sbox_wc_mkdir(&b, "X/A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/C/D")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/C/D/E")); + SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/F")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_switch(&b, "", "/X", svn_depth_infinity)); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + SVN_ERR(sbox_wc_move(&b, "A2/B/C", "A2/B/C2")); + sbox_file_write(&b, "A2/B/F", "obstruction\n"); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, "X"}, + {0, "A", "normal", 1, "X/A"}, + {0, "A/B", "normal", 1, "X/A/B"}, + {0, "A/B/C", "normal", 1, "X/A/B/C"}, + {0, "A/B/C/D", "normal", 1, "X/A/B/C/D"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "X/A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "X/A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 1, "X/A/B/C", MOVED_HERE}, + {1, "A2/B/C/D", "normal", 1, "X/A/B/C/D", MOVED_HERE}, + {3, "A2/B/C", "base-deleted", NO_COPY_FROM, "A2/B/C2"}, + {3, "A2/B/C/D", "base-deleted", NO_COPY_FROM}, + {3, "A2/B/C2", "normal", 1, "X/A/B/C", MOVED_HERE}, + {3, "A2/B/C2/D", "normal", 1, "X/A/B/C/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "A", 2)); + SVN_ERR(check_tree_conflict_repos_path(&b, "A", "X/A", "X/A")); + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, "X"}, + {0, "A", "normal", 2, "X/A"}, + {0, "A/B", "normal", 2, "X/A/B"}, + {0, "A/B/C", "normal", 2, "X/A/B/C"}, + {0, "A/B/C/D", "normal", 2, "X/A/B/C/D"}, + {0, "A/B/C/D/E", "normal", 2, "X/A/B/C/D/E"}, + {0, "A/B/F", "normal", 2, "X/A/B/F"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "A/B/F", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 2, "X/A", MOVED_HERE}, + {1, "A2/B", "normal", 2, "X/A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 2, "X/A/B/C", MOVED_HERE}, + {1, "A2/B/C/D", "normal", 2, "X/A/B/C/D", MOVED_HERE}, + {1, "A2/B/C/D/E", "normal", 2, "X/A/B/C/D/E", MOVED_HERE}, + {1, "A2/B/F", "normal", 2, "X/A/B/F", MOVED_HERE}, + {3, "A2/B/C", "base-deleted", NO_COPY_FROM, "A2/B/C2"}, + {3, "A2/B/C/D", "base-deleted", NO_COPY_FROM}, + {3, "A2/B/C/D/E", "base-deleted", NO_COPY_FROM}, + {3, "A2/B/C2", "normal", 1, "X/A/B/C", MOVED_HERE}, + {3, "A2/B/C2/D", "normal", 1, "X/A/B/C/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(check_tree_conflict_repos_path(&b, "A2/B/C", "X/A/B/C", "X/A/B/C")); + SVN_ERR(check_tree_conflict_repos_path(&b, "A2/B/F", NULL, "X/A/B/F")); + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_update_delete_mods(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_update_delete_mods", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/D")); + sbox_file_write(&b, "A/B/C/f", "r1 content\n"); + SVN_ERR(sbox_wc_add(&b, "A/B/C/f")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_delete(&b, "A/B/C")); + SVN_ERR(sbox_wc_delete(&b, "A/B/D")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A/B", "B2")); + sbox_file_write(&b, "B2/C/f", "modified content\n"); + SVN_ERR(sbox_wc_delete(&b, "B2/D")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {0, "A/B/C/f", "normal", 1, "A/B/C/f"}, + {0, "A/B/D", "normal", 1, "A/B/D"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C/f", "base-deleted", NO_COPY_FROM}, + {2, "A/B/D", "base-deleted", NO_COPY_FROM}, + {1, "B2", "normal", 1, "A/B", MOVED_HERE}, + {1, "B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {1, "B2/C/f", "normal", 1, "A/B/C/f", MOVED_HERE}, + {1, "B2/D", "normal", 1, "A/B/D", MOVED_HERE}, + {2, "B2/D", "base-deleted", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "A", 2)); + SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "B2"}, + {1, "B2", "normal", 2, "A/B", MOVED_HERE}, + {2, "B2/C", "normal", 1, "A/B/C"}, + {2, "B2/C/f", "normal", 1, "A/B/C/f"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(check_tree_conflict_repos_path(&b, "B2/C", "A/B/C", "A/B/C")); + SVN_ERR(check_tree_conflict_repos_path(&b, "B2/D", "A/B/D", "A/B/D")); + + return SVN_NO_ERROR; +} + +static svn_error_t * +nested_moves2(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "nested_moves2", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A/A")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_move(&b, "A/A/A/A/A/A", "C")); + SVN_ERR(sbox_wc_move(&b, "A/A/A/A", "D")); + SVN_ERR(sbox_wc_move(&b, "A/A", "E")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 0, ""}, + {0, "X", "normal", 2, "X"}, + {0, "A", "normal", 1, "A"}, + {0, "A/A", "normal", 1, "A/A"}, + {0, "A/A/A", "normal", 1, "A/A/A"}, + {0, "A/A/A/A", "normal", 1, "A/A/A/A"}, + {0, "A/A/A/A/A", "normal", 1, "A/A/A/A/A"}, + {0, "A/A/A/A/A/A", "normal", 1, "A/A/A/A/A/A"}, + {2, "A/A", "base-deleted", NO_COPY_FROM, "E"}, + {2, "A/A/A", "base-deleted", NO_COPY_FROM}, + {2, "A/A/A/A", "base-deleted", NO_COPY_FROM}, + {2, "A/A/A/A/A", "base-deleted", NO_COPY_FROM}, + {2, "A/A/A/A/A/A", "base-deleted", NO_COPY_FROM}, + {1, "E", "normal", 1, "A/A", MOVED_HERE}, + {1, "E/A", "normal", 1, "A/A/A", MOVED_HERE}, + {1, "E/A/A", "normal", 1, "A/A/A/A", MOVED_HERE}, + {1, "E/A/A/A", "normal", 1, "A/A/A/A/A", MOVED_HERE}, + {1, "E/A/A/A/A", "normal", 1, "A/A/A/A/A/A", MOVED_HERE}, + {3, "E/A/A", "base-deleted", NO_COPY_FROM, "D"}, + {3, "E/A/A/A", "base-deleted", NO_COPY_FROM}, + {3, "E/A/A/A/A", "base-deleted", NO_COPY_FROM}, + {1, "D", "normal", 1, "A/A/A/A", MOVED_HERE}, + {1, "D/A", "normal", 1, "A/A/A/A/A", MOVED_HERE}, + {1, "D/A/A", "normal", 1, "A/A/A/A/A/A", MOVED_HERE}, + {3, "D/A/A", "base-deleted", NO_COPY_FROM, "C"}, + {1, "C", "normal", 1, "A/A/A/A/A/A", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "A", 2)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 0, ""}, + {0, "X", "normal", 2, "X"}, + {0, "A", "normal", 2, "A"}, + {0, "A/A", "normal", 2, "A/A"}, + {0, "A/A/A", "normal", 2, "A/A/A"}, + {0, "A/A/A/A", "normal", 2, "A/A/A/A"}, + {0, "A/A/A/A/A", "normal", 2, "A/A/A/A/A"}, + {0, "A/A/A/A/A/A", "normal", 2, "A/A/A/A/A/A"}, + {2, "A/A", "base-deleted", NO_COPY_FROM, "E"}, + {2, "A/A/A", "base-deleted", NO_COPY_FROM}, + {2, "A/A/A/A", "base-deleted", NO_COPY_FROM}, + {2, "A/A/A/A/A", "base-deleted", NO_COPY_FROM}, + {2, "A/A/A/A/A/A", "base-deleted", NO_COPY_FROM}, + {1, "E", "normal", 2, "A/A", MOVED_HERE}, + {1, "E/A", "normal", 2, "A/A/A", MOVED_HERE}, + {1, "E/A/A", "normal", 2, "A/A/A/A", MOVED_HERE}, + {1, "E/A/A/A", "normal", 2, "A/A/A/A/A", MOVED_HERE}, + {1, "E/A/A/A/A", "normal", 2, "A/A/A/A/A/A", MOVED_HERE}, + {3, "E/A/A", "base-deleted", NO_COPY_FROM, "D"}, + {3, "E/A/A/A", "base-deleted", NO_COPY_FROM}, + {3, "E/A/A/A/A", "base-deleted", NO_COPY_FROM}, + {1, "D", "normal", 2, "A/A/A/A", MOVED_HERE}, + {1, "D/A", "normal", 2, "A/A/A/A/A", MOVED_HERE}, + {1, "D/A/A", "normal", 2, "A/A/A/A/A/A", MOVED_HERE}, + {3, "D/A/A", "base-deleted", NO_COPY_FROM, "C"}, + {1, "C", "normal", 2, "A/A/A/A/A/A", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_in_delete(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_in_delete", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D/E")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A/B/C", "C2")); + SVN_ERR(sbox_wc_delete(&b, "A/B")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {2, "A/B", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM, "C2"}, + {1, "C2", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "", 2)); + SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + SVN_ERR(sbox_wc_resolve(&b, "A/B/C", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {2, "A/B", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM, "C2"}, + {2, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "C2", "normal", 2, "A/B/C", MOVED_HERE}, + {1, "C2/D", "normal", 2, "A/B/C/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "", 3)); + SVN_ERR(sbox_wc_revert(&b, "A/B", svn_depth_empty)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 3, ""}, + {0, "A", "normal", 3, "A"}, + {0, "A/B", "normal", 3, "A/B"}, + {0, "A/B/C", "normal", 3, "A/B/C"}, + {0, "A/B/C/D", "normal", 3, "A/B/C/D"}, + {0, "A/B/C/D/E", "normal", 3, "A/B/C/D/E"}, + {3, "A/B/C", "base-deleted", NO_COPY_FROM, "C2"}, + {3, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {3, "A/B/C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "C2", "normal", 2, "A/B/C", MOVED_HERE}, + {1, "C2/D", "normal", 2, "A/B/C/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Revert should have left a tree-conflict (or broken the move). */ + SVN_ERR(sbox_wc_resolve(&b, "A/B/C", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 3, ""}, + {0, "A", "normal", 3, "A"}, + {0, "A/B", "normal", 3, "A/B"}, + {0, "A/B/C", "normal", 3, "A/B/C"}, + {0, "A/B/C/D", "normal", 3, "A/B/C/D"}, + {0, "A/B/C/D/E", "normal", 3, "A/B/C/D/E"}, + {3, "A/B/C", "base-deleted", NO_COPY_FROM, "C2"}, + {3, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {3, "A/B/C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "C2", "normal", 3, "A/B/C", MOVED_HERE}, + {1, "C2/D", "normal", 3, "A/B/C/D", MOVED_HERE}, + {1, "C2/D/E", "normal", 3, "A/B/C/D/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +switch_move(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "switch_move", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/D")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/D/E")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_copy(&b, "A", "X")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "X/B/D/E/F")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_switch(&b, "", "/A", svn_depth_infinity)); + SVN_ERR(sbox_wc_update(&b, "", 2)); + + SVN_ERR(sbox_wc_move(&b, "B/C", "C2")); + SVN_ERR(sbox_wc_move(&b, "B/D", "D2")); + SVN_ERR(sbox_wc_move(&b, "D2/E", "D2/E2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, "A"}, + {0, "B", "normal", 2, "A/B"}, + {0, "B/C", "normal", 2, "A/B/C"}, + {0, "B/D", "normal", 2, "A/B/D"}, + {0, "B/D/E", "normal", 2, "A/B/D/E"}, + {2, "B/C", "base-deleted", NO_COPY_FROM, "C2"}, + {2, "B/D", "base-deleted", NO_COPY_FROM, "D2"}, + {2, "B/D/E", "base-deleted", NO_COPY_FROM}, + {1, "C2", "normal", 2, "A/B/C", MOVED_HERE}, + {1, "D2", "normal", 2, "A/B/D", MOVED_HERE}, + {1, "D2/E", "normal", 2, "A/B/D/E", MOVED_HERE}, + {2, "D2/E", "base-deleted", NO_COPY_FROM, "D2/E2"}, + {2, "D2/E2", "normal", 2, "A/B/D/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Switch "bumps" revisions and paths and raises conflicts just like + update. */ + SVN_ERR(sbox_wc_switch(&b, "", "/X", svn_depth_infinity)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 3, "X"}, + {0, "B", "normal", 3, "X/B"}, + {0, "B/C", "normal", 3, "X/B/C"}, + {0, "B/D", "normal", 3, "X/B/D"}, + {0, "B/D/E", "normal", 3, "X/B/D/E"}, + {0, "B/D/E/F", "normal", 3, "X/B/D/E/F"}, + {2, "B/C", "base-deleted", NO_COPY_FROM, "C2"}, + {2, "B/D", "base-deleted", NO_COPY_FROM, "D2"}, + {2, "B/D/E", "base-deleted", NO_COPY_FROM}, + {2, "B/D/E/F", "base-deleted", NO_COPY_FROM}, + {1, "C2", "normal", 3, "X/B/C", MOVED_HERE}, + {1, "D2", "normal", 2, "A/B/D", MOVED_HERE}, + {1, "D2/E", "normal", 2, "A/B/D/E", MOVED_HERE}, + {2, "D2/E", "base-deleted", NO_COPY_FROM, "D2/E2"}, + {2, "D2/E2", "normal", 2, "A/B/D/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Conflicts from switch are resolved just like those from update. */ + SVN_ERR(sbox_wc_resolve(&b, "B/D", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 3, "X"}, + {0, "B", "normal", 3, "X/B"}, + {0, "B/C", "normal", 3, "X/B/C"}, + {0, "B/D", "normal", 3, "X/B/D"}, + {0, "B/D/E", "normal", 3, "X/B/D/E"}, + {0, "B/D/E/F", "normal", 3, "X/B/D/E/F"}, + {2, "B/C", "base-deleted", NO_COPY_FROM, "C2"}, + {2, "B/D", "base-deleted", NO_COPY_FROM, "D2"}, + {2, "B/D/E", "base-deleted", NO_COPY_FROM}, + {2, "B/D/E/F", "base-deleted", NO_COPY_FROM}, + {1, "C2", "normal", 3, "X/B/C", MOVED_HERE}, + {1, "D2", "normal", 3, "X/B/D", MOVED_HERE}, + {1, "D2/E", "normal", 3, "X/B/D/E", MOVED_HERE}, + {1, "D2/E/F", "normal", 3, "X/B/D/E/F", MOVED_HERE}, + {2, "D2/E", "base-deleted", NO_COPY_FROM, "D2/E2"}, + {2, "D2/E/F", "base-deleted", NO_COPY_FROM}, + {2, "D2/E2", "normal", 2, "A/B/D/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_resolve(&b, "D2/E", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 3, "X"}, + {0, "B", "normal", 3, "X/B"}, + {0, "B/C", "normal", 3, "X/B/C"}, + {0, "B/D", "normal", 3, "X/B/D"}, + {0, "B/D/E", "normal", 3, "X/B/D/E"}, + {0, "B/D/E/F", "normal", 3, "X/B/D/E/F"}, + {2, "B/C", "base-deleted", NO_COPY_FROM, "C2"}, + {2, "B/D", "base-deleted", NO_COPY_FROM, "D2"}, + {2, "B/D/E", "base-deleted", NO_COPY_FROM}, + {2, "B/D/E/F", "base-deleted", NO_COPY_FROM}, + {1, "C2", "normal", 3, "X/B/C", MOVED_HERE}, + {1, "D2", "normal", 3, "X/B/D", MOVED_HERE}, + {1, "D2/E", "normal", 3, "X/B/D/E", MOVED_HERE}, + {1, "D2/E/F", "normal", 3, "X/B/D/E/F", MOVED_HERE}, + {2, "D2/E", "base-deleted", NO_COPY_FROM, "D2/E2"}, + {2, "D2/E/F", "base-deleted", NO_COPY_FROM}, + {2, "D2/E2", "normal", 3, "X/B/D/E", MOVED_HERE}, + {2, "D2/E2/F", "normal", 3, "X/B/D/E/F", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_replace(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_replace", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "B/X")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A", "X")); + SVN_ERR(sbox_wc_move(&b, "B", "A")); + SVN_ERR(sbox_wc_move(&b, "X", "B")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "B", "normal", 1, "B"}, + {1, "A", "normal", 1, "B", FALSE, "B", TRUE}, + {1, "B", "normal", 1, "A", FALSE, "A", TRUE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "", 2)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "B", "normal", 2, "B"}, + {0, "B/X", "normal", 2, "B/X"}, + {1, "A", "normal", 1, "B", FALSE, "B", TRUE}, + {1, "B", "normal", 2, "A", FALSE, "A", TRUE}, + {1, "B/X", "base-deleted", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_resolve(&b, "B", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "B", "normal", 2, "B"}, + {0, "B/X", "normal", 2, "B/X"}, + {1, "A", "normal", 2, "B", FALSE, "B", TRUE}, + {1, "A/X", "normal", 2, "B/X", MOVED_HERE}, + {1, "B", "normal", 2, "A", FALSE, "A", TRUE}, + {1, "B/X", "base-deleted", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +layered_moved_to(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + svn_error_t *err; + + SVN_ERR(svn_test__sandbox_create(&b, "layered_moved_to", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D/E")); + SVN_ERR(sbox_wc_mkdir(&b, "C")); + SVN_ERR(sbox_wc_mkdir(&b, "C/D")); + SVN_ERR(sbox_wc_mkdir(&b, "C/D/E")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_propset(&b, "property", "value", "A/B/C/D/E")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_propset(&b, "property", "value", "C/D/E")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "P")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_propset(&b, "property2", "value", "A/B/C/D/E")); + SVN_ERR(sbox_wc_propset(&b, "property2", "value", "C/D/E")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A", "X")); + SVN_ERR(sbox_wc_move(&b, "X/B/C/D/E", "E2")); + SVN_ERR(sbox_wc_delete(&b, "X/B/C")); + SVN_ERR(sbox_wc_move(&b, "C", "X/B/C")); + SVN_ERR(sbox_wc_move(&b, "X/B/C/D/E", "E3")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {0, "A/B/C/D", "normal", 1, "A/B/C/D"}, + {0, "A/B/C/D/E", "normal", 1, "A/B/C/D/E"}, + {0, "C", "normal", 1, "C"}, + {0, "C/D", "normal", 1, "C/D"}, + {0, "C/D/E", "normal", 1, "C/D/E"}, + {1, "A", "base-deleted", NO_COPY_FROM, "X"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "C", "base-deleted", NO_COPY_FROM, "X/B/C"}, + {1, "C/D", "base-deleted", NO_COPY_FROM}, + {1, "C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 1, "A", MOVED_HERE}, + {1, "X/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "X/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {1, "X/B/C/D", "normal", 1, "A/B/C/D", MOVED_HERE}, + {1, "X/B/C/D/E", "normal", 1, "A/B/C/D/E", MOVED_HERE}, + {3, "X/B/C", "normal", 1, "C", MOVED_HERE}, + {3, "X/B/C/D", "normal", 1, "C/D", MOVED_HERE}, + {3, "X/B/C/D/E", "normal", 1, "C/D/E", FALSE, "E2", TRUE}, + {5, "X/B/C/D/E", "base-deleted", NO_COPY_FROM, "E3"}, + {1, "E2", "normal", 1, "A/B/C/D/E", MOVED_HERE}, + {1, "E3", "normal", 1, "C/D/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "A", 2)); + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + SVN_ERR(sbox_wc_resolve(&b, "X/B/C", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + SVN_ERR(sbox_wc_resolve(&b, "X/B/C/D/E", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {0, "A/B/C/D/E", "normal", 2, "A/B/C/D/E"}, + {0, "C", "normal", 1, "C"}, + {0, "C/D", "normal", 1, "C/D"}, + {0, "C/D/E", "normal", 1, "C/D/E"}, + {1, "A", "base-deleted", NO_COPY_FROM, "X"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "C", "base-deleted", NO_COPY_FROM, "X/B/C"}, + {1, "C/D", "base-deleted", NO_COPY_FROM}, + {1, "C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 2, "A", MOVED_HERE}, + {1, "X/B", "normal", 2, "A/B", MOVED_HERE}, + {1, "X/B/C", "normal", 2, "A/B/C", MOVED_HERE}, + {1, "X/B/C/D", "normal", 2, "A/B/C/D", MOVED_HERE}, + {1, "X/B/C/D/E", "normal", 2, "A/B/C/D/E", MOVED_HERE}, + {3, "X/B/C", "normal", 1, "C", MOVED_HERE}, + {3, "X/B/C/D", "normal", 1, "C/D", MOVED_HERE}, + {3, "X/B/C/D/E", "normal", 1, "C/D/E", FALSE, "E2", TRUE}, + {5, "X/B/C/D/E", "base-deleted", NO_COPY_FROM, "E3"}, + {1, "E2", "normal", 2, "A/B/C/D/E", MOVED_HERE}, + {1, "E3", "normal", 1, "C/D/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "C", 3)); + SVN_ERR(sbox_wc_resolve(&b, "C", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + SVN_ERR(sbox_wc_resolve(&b, "X/B/C/D/E", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {0, "A/B/C/D/E", "normal", 2, "A/B/C/D/E"}, + {0, "C", "normal", 3, "C"}, + {0, "C/D", "normal", 3, "C/D"}, + {0, "C/D/E", "normal", 3, "C/D/E"}, + {1, "A", "base-deleted", NO_COPY_FROM, "X"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "C", "base-deleted", NO_COPY_FROM, "X/B/C"}, + {1, "C/D", "base-deleted", NO_COPY_FROM}, + {1, "C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 2, "A", MOVED_HERE}, + {1, "X/B", "normal", 2, "A/B", MOVED_HERE}, + {1, "X/B/C", "normal", 2, "A/B/C", MOVED_HERE}, + {1, "X/B/C/D", "normal", 2, "A/B/C/D", MOVED_HERE}, + {1, "X/B/C/D/E", "normal", 2, "A/B/C/D/E", MOVED_HERE}, + {3, "X/B/C", "normal", 3, "C", MOVED_HERE}, + {3, "X/B/C/D", "normal", 3, "C/D", MOVED_HERE}, + {3, "X/B/C/D/E", "normal", 3, "C/D/E", FALSE, "E2", TRUE}, + {5, "X/B/C/D/E", "base-deleted", NO_COPY_FROM, "E3"}, + {1, "E2", "normal", 2, "A/B/C/D/E", MOVED_HERE}, + {1, "E3", "normal", 3, "C/D/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* An update with no text/property/tree changes in A, just a revision bump. */ + SVN_ERR(sbox_wc_update(&b, "A", 4)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 4, "A"}, + {0, "A/B", "normal", 4, "A/B"}, + {0, "A/B/C", "normal", 4, "A/B/C"}, + {0, "A/B/C/D", "normal", 4, "A/B/C/D"}, + {0, "A/B/C/D/E", "normal", 4, "A/B/C/D/E"}, + {0, "C", "normal", 3, "C"}, + {0, "C/D", "normal", 3, "C/D"}, + {0, "C/D/E", "normal", 3, "C/D/E"}, + {1, "A", "base-deleted", NO_COPY_FROM, "X"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "C", "base-deleted", NO_COPY_FROM, "X/B/C"}, + {1, "C/D", "base-deleted", NO_COPY_FROM}, + {1, "C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 4, "A", MOVED_HERE}, + {1, "X/B", "normal", 4, "A/B", MOVED_HERE}, + {1, "X/B/C", "normal", 4, "A/B/C", MOVED_HERE}, + {1, "X/B/C/D", "normal", 4, "A/B/C/D", MOVED_HERE}, + {1, "X/B/C/D/E", "normal", 4, "A/B/C/D/E", MOVED_HERE}, + {3, "X/B/C", "normal", 3, "C", MOVED_HERE}, + {3, "X/B/C/D", "normal", 3, "C/D", MOVED_HERE}, + {3, "X/B/C/D/E", "normal", 3, "C/D/E", FALSE, "E2", TRUE}, + {5, "X/B/C/D/E", "base-deleted", NO_COPY_FROM, "E3"}, + {1, "E2", "normal", 4, "A/B/C/D/E", MOVED_HERE}, + {1, "E3", "normal", 3, "C/D/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Update for conflicts on A and C */ + SVN_ERR(sbox_wc_update(&b, "", 5)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 5, ""}, + {0, "A", "normal", 5, "A"}, + {0, "A/B", "normal", 5, "A/B"}, + {0, "A/B/C", "normal", 5, "A/B/C"}, + {0, "A/B/C/D", "normal", 5, "A/B/C/D"}, + {0, "A/B/C/D/E", "normal", 5, "A/B/C/D/E"}, + {0, "P", "normal", 5, "P"}, + {0, "C", "normal", 5, "C"}, + {0, "C/D", "normal", 5, "C/D"}, + {0, "C/D/E", "normal", 5, "C/D/E"}, + {1, "A", "base-deleted", NO_COPY_FROM, "X"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "C", "base-deleted", NO_COPY_FROM, "X/B/C"}, + {1, "C/D", "base-deleted", NO_COPY_FROM}, + {1, "C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 4, "A", MOVED_HERE}, + {1, "X/B", "normal", 4, "A/B", MOVED_HERE}, + {1, "X/B/C", "normal", 4, "A/B/C", MOVED_HERE}, + {1, "X/B/C/D", "normal", 4, "A/B/C/D", MOVED_HERE}, + {1, "X/B/C/D/E", "normal", 4, "A/B/C/D/E", MOVED_HERE}, + {3, "X/B/C", "normal", 3, "C", MOVED_HERE}, + {3, "X/B/C/D", "normal", 3, "C/D", MOVED_HERE}, + {3, "X/B/C/D/E", "normal", 3, "C/D/E", FALSE, "E2", TRUE}, + {5, "X/B/C/D/E", "base-deleted", NO_COPY_FROM, "E3"}, + {1, "E2", "normal", 4, "A/B/C/D/E", MOVED_HERE}, + {1, "E3", "normal", 3, "C/D/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Partially resolve A */ + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + SVN_ERR(sbox_wc_resolve(&b, "X/B/C", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + + /* Cannot resolve C */ + err = sbox_wc_resolve(&b, "C", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE); + + /* Complete resolving A and then resolve C */ + SVN_ERR(sbox_wc_resolve(&b, "X/B/C/D/E", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + SVN_ERR(sbox_wc_resolve(&b, "C", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 5, ""}, + {0, "A", "normal", 5, "A"}, + {0, "A/B", "normal", 5, "A/B"}, + {0, "A/B/C", "normal", 5, "A/B/C"}, + {0, "A/B/C/D", "normal", 5, "A/B/C/D"}, + {0, "A/B/C/D/E", "normal", 5, "A/B/C/D/E"}, + {0, "P", "normal", 5, "P"}, + {0, "C", "normal", 5, "C"}, + {0, "C/D", "normal", 5, "C/D"}, + {0, "C/D/E", "normal", 5, "C/D/E"}, + {1, "A", "base-deleted", NO_COPY_FROM, "X"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "C", "base-deleted", NO_COPY_FROM, "X/B/C"}, + {1, "C/D", "base-deleted", NO_COPY_FROM}, + {1, "C/D/E", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 5, "A", MOVED_HERE}, + {1, "X/B", "normal", 5, "A/B", MOVED_HERE}, + {1, "X/B/C", "normal", 5, "A/B/C", MOVED_HERE}, + {1, "X/B/C/D", "normal", 5, "A/B/C/D", MOVED_HERE}, + {1, "X/B/C/D/E", "normal", 5, "A/B/C/D/E", MOVED_HERE}, + {3, "X/B/C", "normal", 5, "C", MOVED_HERE}, + {3, "X/B/C/D", "normal", 5, "C/D", MOVED_HERE}, + {3, "X/B/C/D/E", "normal", 5, "C/D/E", FALSE, "E2", TRUE}, + {5, "X/B/C/D/E", "base-deleted", NO_COPY_FROM, "E3"}, + {1, "E2", "normal", 5, "A/B/C/D/E", MOVED_HERE}, + {1, "E3", "normal", 3, "C/D/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +update_within_move(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + svn_error_t *err; + + SVN_ERR(svn_test__sandbox_create(&b, "update_within_move", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A", "X")); + SVN_ERR(sbox_wc_update(&b, "A/B/C", 2)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {1, "A", "base-deleted", NO_COPY_FROM, "X"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 1, "A", MOVED_HERE}, + {1, "X/B", "normal", 1, "A/B", MOVED_HERE}, + {1, "X/B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Can't resolve mixed-revision source to mine-conflict. */ + err = sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE); + + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_merged)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {1, "A", "base-deleted", NO_COPY_FROM}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {1, "X", "normal", 1, "A"}, + {1, "X/B", "normal", 1, "A/B"}, + {1, "X/B/C", "normal", 1, "A/B/C"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + + return SVN_NO_ERROR; +} + +static svn_error_t * +commit_moved_descendant(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + SVN_ERR(svn_test__sandbox_create(&b, "commit_moved_descendant", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A/A")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_copy(&b, "A", "A_copied")); + SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_moved")); + SVN_ERR(sbox_wc_delete(&b, "A/A")); + SVN_ERR(sbox_wc_copy(&b, "A_copied/A", "A/A")); + + /* And now I want to commit AAA_moved (the entire move), but not + the replacement of A/A */ + + /* For now, just start committing directly */ + /* ### This fails, because A/A/A is not collected by the commit + harvester (it doesn't need committing, but our move filter + blocks on it) */ + SVN_ERR(sbox_wc_commit(&b, "")); + + /* It would be nicer if we could just do a: */ + /* SVN_ERR(sbox_wc_commit(&b, "AAA_moved")); */ + /* Which then includes the delete half of the move, when it is + shadowed, like in this case. The commit processing doesn't + support this yet though*/ + + return SVN_NO_ERROR; +} + +static svn_error_t * +commit_moved_away_descendant(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + SVN_ERR(svn_test__sandbox_create(&b, "commit_moved_away_descendant", + opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A/A/A")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_copy(&b, "A", "A_copied")); + SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_moved")); + SVN_ERR(sbox_wc_delete(&b, "A/A")); + SVN_ERR(sbox_wc_copy(&b, "A_copied/A", "A/A")); + + /* And now I want to make sure that I can't commit A, without also + committing AAA_moved, as that would break the move*/ + SVN_ERR(sbox_wc_commit(&b, "A")); + + return svn_error_create(SVN_ERR_TEST_FAILED, NULL, + "The commit should have failed"); + + /*return SVN_NO_ERROR;*/ +} + +static svn_error_t * +finite_move_update_bump(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + svn_error_t *err; + SVN_ERR(svn_test__sandbox_create(&b, "finite_move_update_bump", + opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "P")); + SVN_ERR(sbox_wc_mkdir(&b, "P/Q")); + sbox_file_write(&b, "P/Q/f", "r1 content\n"); + SVN_ERR(sbox_wc_add(&b, "P/Q/f")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A/B", "B2")); + SVN_ERR(sbox_wc_update(&b, "A/B/C", 2)); + SVN_ERR(check_tree_conflict_repos_path(&b, "A/B", NULL, NULL)); + err = sbox_wc_resolve(&b, "A/B", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "P", "normal", 1, "P"}, + {0, "P/Q", "normal", 1, "P/Q"}, + {0, "P/Q/f", "normal", 1, "P/Q/f"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "B2", "normal", 1, "A/B", MOVED_HERE}, + {1, "B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity)); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_move(&b, "A/B", "B2")); + SVN_ERR(sbox_wc_move(&b, "P/Q", "Q2")); + SVN_ERR(sbox_wc_update_depth(&b, "A/B", 2, svn_depth_files, FALSE)); + SVN_ERR(sbox_wc_update_depth(&b, "P/Q", 2, svn_depth_files, FALSE)); + SVN_ERR(check_tree_conflict_repos_path(&b, "A/B", NULL, NULL)); + err = sbox_wc_resolve(&b, "A/B", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {0, "P", "normal", 1, "P"}, + {0, "P/Q", "normal", 2, "P/Q"}, + {0, "P/Q/f", "normal", 2, "P/Q/f"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "P/Q", "base-deleted", NO_COPY_FROM, "Q2"}, + {2, "P/Q/f", "base-deleted", NO_COPY_FROM}, + {1, "B2", "normal", 1, "A/B", MOVED_HERE}, + {1, "B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {1, "Q2", "normal", 2, "P/Q", MOVED_HERE}, + {1, "Q2/f", "normal", 2, "P/Q/f", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity)); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_move(&b, "A/B", "B2")); + SVN_ERR(sbox_wc_move(&b, "P", "P2")); + SVN_ERR(sbox_wc_update_depth(&b, "A/B", 2, svn_depth_immediates, FALSE)); + SVN_ERR(sbox_wc_update_depth(&b, "P", 2, svn_depth_immediates, FALSE)); + SVN_ERR(check_tree_conflict_repos_path(&b, "P", NULL, NULL)); + err = sbox_wc_resolve(&b, "P", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "P", "normal", 2, "P"}, + {0, "P/Q", "normal", 2, "P/Q"}, + {0, "P/Q/f", "normal", 1, "P/Q/f"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "P", "base-deleted", NO_COPY_FROM, "P2"}, + {1, "P/Q", "base-deleted", NO_COPY_FROM}, + {1, "P/Q/f", "base-deleted", NO_COPY_FROM}, + {1, "B2", "normal", 2, "A/B", MOVED_HERE}, + {1, "B2/C", "normal", 2, "A/B/C", MOVED_HERE}, + {1, "P2", "normal", 1, "P", MOVED_HERE}, + {1, "P2/Q", "normal", 1, "P/Q", MOVED_HERE}, + {1, "P2/Q/f", "normal", 1, "P/Q/f", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity)); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_move(&b, "A/B/C", "C2")); + SVN_ERR(sbox_wc_move(&b, "P/Q", "Q2")); + SVN_ERR(sbox_wc_update_depth(&b, "A/B/C", 2, svn_depth_empty, FALSE)); + SVN_ERR(sbox_wc_update_depth(&b, "P/Q", 2, svn_depth_empty, FALSE)); + SVN_ERR(check_tree_conflict_repos_path(&b, "P/Q", NULL, NULL)); + err = sbox_wc_resolve(&b, "P/Q", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "P", "normal", 1, "P"}, + {0, "P/Q", "normal", 2, "P/Q"}, + {0, "P/Q/f", "normal", 1, "P/Q/f"}, + {3, "A/B/C", "base-deleted", NO_COPY_FROM, "C2"}, + {2, "P/Q", "base-deleted", NO_COPY_FROM, "Q2"}, + {2, "P/Q/f", "base-deleted", NO_COPY_FROM}, + {1, "C2", "normal", 2, "A/B/C", MOVED_HERE}, + {1, "Q2", "normal", 1, "P/Q", MOVED_HERE}, + {1, "Q2/f", "normal", 1, "P/Q/f", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_away_delete_update(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + SVN_ERR(svn_test__sandbox_create(&b, "move_away_delete_update", + opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "P")); + SVN_ERR(sbox_wc_mkdir(&b, "P/Q")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_delete(&b, "A/B")); + SVN_ERR(sbox_wc_delete(&b, "P/Q")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_move(&b, "A/B/C", "C2")); + SVN_ERR(sbox_wc_move(&b, "P/Q", "Q2")); + + /* Update to r2 removes the move sources and clears moved_here from + the move destinations. */ + SVN_ERR(sbox_wc_update(&b, "", 2)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "P", "normal", 2, "P"}, + {1, "C2", "normal", 1, "A/B/C"}, + {1, "Q2", "normal", 1, "P/Q"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_not_present_variants(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + SVN_ERR(svn_test__sandbox_create(&b, "move_not_present_variants", + opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "B")); + SVN_ERR(sbox_wc_mkdir(&b, "B/B")); + SVN_ERR(sbox_wc_mkdir(&b, "C")); + SVN_ERR(sbox_wc_mkdir(&b, "C/B")); + SVN_ERR(sbox_wc_mkdir(&b, "D")); + SVN_ERR(sbox_wc_mkdir(&b, "D/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_delete(&b, "A/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_delete(&b, "B/B")); + SVN_ERR(sbox_wc_update(&b, "C/B", 0)); + SVN_ERR(sbox_wc_exclude(&b, "D/B")); + + SVN_ERR(sbox_wc_copy(&b, "A", "cA")); + SVN_ERR(sbox_wc_copy(&b, "B", "cB")); + SVN_ERR(sbox_wc_copy(&b, "C", "cC")); + SVN_ERR(sbox_wc_copy(&b, "D", "cD")); + + SVN_ERR(sbox_wc_copy(&b, "cA", "ccA")); + SVN_ERR(sbox_wc_copy(&b, "cB", "ccB")); + SVN_ERR(sbox_wc_copy(&b, "cC", "ccC")); + SVN_ERR(sbox_wc_copy(&b, "cD", "ccD")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + /* Copy of a deleted + committed node */ + {0, "A", "normal", 1, "A"}, + {0, "A/B", "not-present", 2, "A/B"}, + + {1, "cA", "normal", 1, "A"}, + {1, "cA/B", "not-present", 2, "A/B"}, + + {1, "ccA", "normal", 1, "A"}, + {1, "ccA/B", "not-present", 2, "A/B"}, + + /* Copy of a local deleted node */ + {0, "B", "normal", 1, "B"}, + {0, "B/B", "normal", 1, "B/B"}, + {2, "B/B", "base-deleted", NO_COPY_FROM}, + + {1, "cB", "normal", 1, "B",}, + {1, "cB/B", "normal", 1, "B/B"}, + {2, "cB/B", "base-deleted", NO_COPY_FROM}, + + {1, "ccB", "normal", 1, "B"}, + {1, "ccB/B", "normal", 1, "B/B"}, + {2, "ccB/B", "base-deleted", NO_COPY_FROM}, + + /* Copy of a to r0 updated node */ + {0, "C", "normal", 1, "C"}, + {0, "C/B", "not-present", 0, "C/B"}, + + {1, "cC", "normal", 1, "C"}, + {1, "cC/B", "not-present", 0, "C/B"}, + + {1, "ccC", "normal", 1, "C"}, + {1, "ccC/B", "not-present", 0, "C/B"}, + + /* Copy of an excluded node */ + {0, "D", "normal", 1, "D"}, + {0, "D/B", "excluded", 1, "D/B"}, + + {1, "cD", "normal", 1, "D"}, + {1, "cD/B", "excluded", 1, "D/B"}, + + {1, "ccD", "normal", 1, "D"}, + {1, "ccD/B", "excluded", 1, "D/B"}, + + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity)); + SVN_ERR(sbox_wc_delete(&b, "B/B")); + + /* And now do the same thing with moves */ + + SVN_ERR(sbox_wc_move(&b, "A", "mA")); + SVN_ERR(sbox_wc_move(&b, "B", "mB")); + SVN_ERR(sbox_wc_move(&b, "C", "mC")); + SVN_ERR(sbox_wc_move(&b, "D", "mD")); + + SVN_ERR(sbox_wc_move(&b, "mA", "mmA")); + SVN_ERR(sbox_wc_move(&b, "mB", "mmB")); + SVN_ERR(sbox_wc_move(&b, "mC", "mmC")); + SVN_ERR(sbox_wc_move(&b, "mD", "mmD")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + /* Move of a deleted + committed node */ + {0, "A", "normal", 1, "A"}, + {0, "A/B", "not-present", 2, "A/B"}, + {1, "A", "base-deleted", NO_COPY_FROM, "mmA"}, + + {1, "mmA", "normal", 1, "A", MOVED_HERE}, + {1, "mmA/B", "not-present", 2, "A/B", MOVED_HERE}, + + + /* Move of a local deleted node */ + {0, "B", "normal", 1, "B"}, + {0, "B/B", "normal", 1, "B/B"}, + {1, "B", "base-deleted", NO_COPY_FROM, "mmB"}, + {1, "B/B", "base-deleted", NO_COPY_FROM}, + + {1, "mmB", "normal", 1, "B", MOVED_HERE}, + {1, "mmB/B", "normal", 1, "B/B", MOVED_HERE}, + {2, "mmB/B", "base-deleted", NO_COPY_FROM}, + + /* Move of a to r0 updated node */ + {0, "C", "normal", 1, "C"}, + {0, "C/B", "not-present", 0, "C/B"}, + {1, "C", "base-deleted", NO_COPY_FROM, "mmC"}, + + {1, "mmC", "normal", 1, "C", MOVED_HERE}, + {1, "mmC/B", "not-present", 0, "C/B", MOVED_HERE}, + + /* Move of an excluded node */ + {0, "D", "normal", 1, "D",}, + {0, "D/B", "excluded", 1, "D/B", }, + {1, "D", "base-deleted", NO_COPY_FROM, "mmD"}, + + {1, "mmD", "normal", 1, "D", MOVED_HERE}, + {1, "mmD/B", "excluded", 1, "D/B", MOVED_HERE}, + + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* And move everything back */ + SVN_ERR(sbox_wc_move(&b, "mmA", "A")); + SVN_ERR(sbox_wc_move(&b, "mmB", "B")); + SVN_ERR(sbox_wc_move(&b, "mmC", "C")); + SVN_ERR(sbox_wc_move(&b, "mmD", "D")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + /* deleted + committed node */ + {0, "A", "normal", 1, "A"}, + {0, "A/B", "not-present", 2, "A/B"}, + + /* local deleted node */ + {0, "B", "normal", 1, "B"}, + {0, "B/B", "normal", 1, "B/B"}, + {2, "B/B", "base-deleted", NO_COPY_FROM}, + + /* To r0 updated node */ + {0, "C", "normal", 1, "C"}, + {0, "C/B", "not-present", 0, "C/B"}, + + /* Move of an excluded node */ + {0, "D", "normal", 1, "D",}, + {0, "D/B", "excluded", 1, "D/B", }, + + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +update_child_under_add(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + svn_error_t *err; + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "not-present", 0, "A/B"}, + {2, "A/B", "normal", NO_COPY_FROM}, + {3, "A/B/C", "normal", NO_COPY_FROM}, + {4, "A/B/C/D", "normal", NO_COPY_FROM}, + {0} + }; + + SVN_ERR(svn_test__sandbox_create(&b, "update_child_under_add", + opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_update(&b, "A/B", 0)); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D")); + SVN_ERR(check_db_rows(&b, "", nodes)); + + /* A/B/C/D is skipped as it has no base node parent */ + SVN_ERR(sbox_wc_update(&b, "A/B/C/D", 1)); + SVN_ERR(check_db_rows(&b, "", nodes)); + + /* A/B/C should be skipped as it has a not-present base node parent */ + err = sbox_wc_update(&b, "A/B/C", 1); + svn_error_clear(err); /* Allow any error and always check NODES. */ + SVN_ERR(check_db_rows(&b, "", nodes)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +delete_over_moved_away(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "delete_over_moved_away", + opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A/B", "B")); + SVN_ERR(sbox_wc_delete(&b, "A")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + {0, "A", "normal", 1, "A"}, + {1, "A", "base-deleted", NO_COPY_FROM}, + {0, "A/B", "normal", 1, "A/B"}, + {1, "A/B", "base-deleted", NO_COPY_FROM, "B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + + {1, "B", "normal", 1, "A/B", MOVED_HERE}, + {1, "B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Now replace A with a similar tree */ + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + {0, "A", "normal", 1, "A"}, + {1, "A", "normal", NO_COPY_FROM}, + {0, "A/B", "normal", 1, "A/B"}, + {1, "A/B", "base-deleted", NO_COPY_FROM, "B"}, + {2, "A/B", "normal", NO_COPY_FROM}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {3, "A/B/C", "normal", NO_COPY_FROM}, + + {1, "B", "normal", 1, "A/B", MOVED_HERE}, + {1, "B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* And delete the new A */ + SVN_ERR(sbox_wc_delete(&b, "A")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + {0, "A", "normal", 1, "A"}, + {1, "A", "base-deleted", NO_COPY_FROM}, + {0, "A/B", "normal", 1, "A/B"}, + /* And here the moved-to information is lost */ + {1, "A/B", "base-deleted", NO_COPY_FROM, "B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + + /* But the moved-here is still there */ + {1, "B", "normal", 1, "A/B", MOVED_HERE}, + {1, "B/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +movedto_opdepth(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "moved_to_op_depth", + opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A/B/C", "C")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + {3, "A/B/C", "base-deleted", NO_COPY_FROM, "C"}, + + {1, "C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* And now the moved_to information has to switch op-depths */ + SVN_ERR(sbox_wc_delete(&b, "A/B")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + + {2, "A/B", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM, "C"}, + + {1, "C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* And again */ + SVN_ERR(sbox_wc_delete(&b, "A")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + + {1, "A", "base-deleted", NO_COPY_FROM}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM, "C"}, + + {1, "C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* And now stay at the depth of A */ + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + + {1, "A", "normal", NO_COPY_FROM}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM, "C"}, + + {2, "A/B", "normal", NO_COPY_FROM}, + + {1, "C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* And see if it can jump back to B again? */ + SVN_ERR(sbox_wc_delete(&b, "A")); + SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_empty)); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + + {2, "A/B", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM, "C"}, + + {1, "C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* And can we bump it back to C itself? */ + SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_immediates)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + + {3, "A/B/C", "base-deleted", NO_COPY_FROM, "C"}, + + {1, "C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +new_basemove(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "new_basemove", + opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + /* We keep track of moved children of copies */ + SVN_ERR(sbox_wc_copy(&b, "A", "Copy")); + SVN_ERR(sbox_wc_move(&b, "Copy/B/C", "C")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + + {1, "Copy", "normal", 1, "A"}, + {1, "Copy/B", "normal", 1, "A/B"}, + {1, "Copy/B/C", "normal", 1, "A/B/C"}, + + {3, "Copy/B/C", "base-deleted", NO_COPY_FROM, "C"}, + + /* C is a copy of A/B/C */ + {1, "C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + { + apr_array_header_t *targets = apr_array_make(pool, 1, sizeof(char *)); + APR_ARRAY_PUSH(targets, const char*) = sbox_wc_path(&b, "Copy"); + + SVN_ERR(sbox_wc_commit_ex(&b, targets, svn_depth_empty)); + } + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 1, "A/B/C"}, + + {0, "Copy", "normal", 2, "Copy"}, + {0, "Copy/B", "normal", 2, "Copy/B"}, + {0, "Copy/B/C", "normal", 2, "Copy/B/C"}, + + {3, "Copy/B/C", "base-deleted", NO_COPY_FROM, "C"}, + + /* And this node is now a copy of Copy/B/C at r2 */ + {1, "C", "normal", 2, "Copy/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_back(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_back", opts, pool)); + + /* X just so we don't always test with local_relpath == repos_path */ + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_mkdir(&b, "X/A")); + SVN_ERR(sbox_wc_mkdir(&b, "X/A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/C")); + SVN_ERR(sbox_wc_mkdir(&b, "X/A/B/D")); + SVN_ERR(sbox_wc_mkdir(&b, "X/E")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_switch(&b, "", "/X", svn_depth_infinity)); + + SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, "X"}, + {0, "A", "normal", 1, "X/A"}, + {0, "A/B", "normal", 1, "X/A/B"}, + {0, "A/B/C", "normal", 1, "X/A/B/C"}, + {0, "A/B/D", "normal", 1, "X/A/B/D"}, + {0, "E", "normal", 1, "X/E"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/D", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 1, "X/A/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 1, "X/A/B/C", MOVED_HERE}, + {2, "A/B2/D", "normal", 1, "X/A/B/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_move(&b, "A/B2", "A/B")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, "X"}, + {0, "A", "normal", 1, "X/A"}, + {0, "A/B", "normal", 1, "X/A/B"}, + {0, "A/B/C", "normal", 1, "X/A/B/C"}, + {0, "A/B/D", "normal", 1, "X/A/B/D"}, + {0, "E", "normal", 1, "X/E"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2")); + SVN_ERR(sbox_wc_move(&b, "A/B2/C", "A/B2/C2")); + SVN_ERR(sbox_wc_move(&b, "A/B2/D", "D2")); + SVN_ERR(sbox_wc_move(&b, "E", "A/B2/E2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, "X"}, + {0, "A", "normal", 1, "X/A"}, + {0, "A/B", "normal", 1, "X/A/B"}, + {0, "A/B/C", "normal", 1, "X/A/B/C"}, + {0, "A/B/D", "normal", 1, "X/A/B/D"}, + {0, "E", "normal", 1, "X/E"}, + {1, "D2", "normal", 1, "X/A/B/D", MOVED_HERE}, + {1, "E", "base-deleted", NO_COPY_FROM, "A/B2/E2"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/D", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 1, "X/A/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 1, "X/A/B/C", MOVED_HERE}, + {2, "A/B2/D", "normal", 1, "X/A/B/D", MOVED_HERE}, + {3, "A/B2/C", "base-deleted", NO_COPY_FROM, "A/B2/C2"}, + {3, "A/B2/D", "base-deleted", NO_COPY_FROM, "D2"}, + {3, "A/B2/C2", "normal", 1, "X/A/B/C", MOVED_HERE}, + {3, "A/B2/E2", "normal", 1, "X/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_move(&b, "A/B2", "A/B")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, "X"}, + {0, "A", "normal", 1, "X/A"}, + {0, "A/B", "normal", 1, "X/A/B"}, + {0, "A/B/C", "normal", 1, "X/A/B/C"}, + {0, "A/B/D", "normal", 1, "X/A/B/D"}, + {0, "E", "normal", 1, "X/E"}, + {1, "D2", "normal", 1, "X/A/B/D", MOVED_HERE}, + {1, "E", "base-deleted", NO_COPY_FROM, "A/B/E2"}, + {3, "A/B/C", "base-deleted", NO_COPY_FROM, "A/B/C2"}, + {3, "A/B/D", "base-deleted", NO_COPY_FROM, "D2"}, + {3, "A/B/C2", "normal", 1, "X/A/B/C", MOVED_HERE}, + {3, "A/B/E2", "normal", 1, "X/E", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_update_subtree(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + svn_error_t *err; + + SVN_ERR(svn_test__sandbox_create(&b, "move_update_subtree", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C/D")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "X")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 3)); + SVN_ERR(sbox_wc_copy(&b, "A", "P")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + /* Subtree update is like an interrupted update, it leaves a + mixed-revision move source. */ + SVN_ERR(sbox_wc_move(&b, "A/B", "A/B2")); + SVN_ERR(sbox_wc_update(&b, "A/B/C", 2)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 1, "A/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Resolve fails because of the mixed-revision. */ + err = sbox_wc_resolve(&b, "A/B", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE); + + /* Update to single-revision to allow resolve; this requires update + while the tree-conflict on A/B is present. */ + SVN_ERR(sbox_wc_update(&b, "A/B", 2)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 1, "A/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 1, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Now resolve is possible. */ + SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {0, "A/B/C/D", "normal", 2, "A/B/C/D"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 2, "A/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 2, "A/B/C", MOVED_HERE}, + {2, "A/B2/C/D", "normal", 2, "A/B/C/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Subtree update that only bumps. */ + SVN_ERR(sbox_wc_update(&b, "A/B/C", 3)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 3, "A/B/C"}, + {0, "A/B/C/D", "normal", 3, "A/B/C/D"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 2, "A/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 2, "A/B/C", MOVED_HERE}, + {2, "A/B2/C/D", "normal", 2, "A/B/C/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Resolve fails because of the mixed-revision. */ + err = sbox_wc_resolve(&b, "A/B", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE); + + /* Update allowed while tree-conflict is present. */ + SVN_ERR(sbox_wc_update(&b, "A/B", 3)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 3, "A/B"}, + {0, "A/B/C", "normal", 3, "A/B/C"}, + {0, "A/B/C/D", "normal", 3, "A/B/C/D"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 2, "A/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 2, "A/B/C", MOVED_HERE}, + {2, "A/B2/C/D", "normal", 2, "A/B/C/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Now resolve works. */ + SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 3, "A/B"}, + {0, "A/B/C", "normal", 3, "A/B/C"}, + {0, "A/B/C/D", "normal", 3, "A/B/C/D"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 3, "A/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 3, "A/B/C", MOVED_HERE}, + {2, "A/B2/C/D", "normal", 3, "A/B/C/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Partial switch of source. */ + SVN_ERR(sbox_wc_switch(&b, "A", "/P", svn_depth_immediates)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 4, "P"}, + {0, "A/B", "normal", 4, "P/B"}, + {0, "A/B/C", "normal", 3, "A/B/C"}, + {0, "A/B/C/D", "normal", 3, "A/B/C/D"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 3, "A/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 3, "A/B/C", MOVED_HERE}, + {2, "A/B2/C/D", "normal", 3, "A/B/C/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Resolve fails because of the subtree-switch. */ + err = sbox_wc_resolve(&b, "A/B", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE); + + /* Switch works while tree-conflict is present. */ + SVN_ERR(sbox_wc_switch(&b, "A", "/P", svn_depth_infinity)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 4, "P"}, + {0, "A/B", "normal", 4, "P/B"}, + {0, "A/B/C", "normal", 4, "P/B/C"}, + {0, "A/B/C/D", "normal", 4, "P/B/C/D"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 3, "A/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 3, "A/B/C", MOVED_HERE}, + {2, "A/B2/C/D", "normal", 3, "A/B/C/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Now resolve works. */ + SVN_ERR(sbox_wc_resolve(&b, "A/B", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 4, "P"}, + {0, "A/B", "normal", 4, "P/B"}, + {0, "A/B/C", "normal", 4, "P/B/C"}, + {0, "A/B/C/D", "normal", 4, "P/B/C/D"}, + {2, "A/B", "base-deleted", NO_COPY_FROM, "A/B2"}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {2, "A/B/C/D", "base-deleted", NO_COPY_FROM}, + {2, "A/B2", "normal", 4, "P/B", MOVED_HERE}, + {2, "A/B2/C", "normal", 4, "P/B/C", MOVED_HERE}, + {2, "A/B2/C/D", "normal", 4, "P/B/C/D", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_parent_into_child(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_parent_into_child", opts, pool)); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A/B", "B2")); + SVN_ERR(sbox_wc_move(&b, "A", "B2/A")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {1, "A", "base-deleted", NO_COPY_FROM, "B2/A"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "B2", "normal", 1, "A/B", MOVED_HERE}, + {2, "B2/A", "normal", 1, "A", MOVED_HERE}, + {2, "B2/A/B", "normal", 1, "A/B", MOVED_HERE}, + {3, "B2/A/B", "base-deleted", NO_COPY_FROM, "B2"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_move(&b, "B2", "A")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {1, "A", "normal", 1, "A/B", FALSE, "A/A", TRUE}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {2, "A/A", "normal", 1, "A", MOVED_HERE}, + {2, "A/A/B", "normal", 1, "A/B", MOVED_HERE}, + {3, "A/A/B", "base-deleted", NO_COPY_FROM, "A"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_move(&b, "A/A", "A/B")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {1, "A", "normal", 1, "A/B", FALSE, "A/B", TRUE}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {2, "A/B", "normal", 1, "A", MOVED_HERE}, + {2, "A/B/B", "normal", 1, "A/B", MOVED_HERE}, + {3, "A/B/B", "base-deleted", NO_COPY_FROM, "A"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_depth_expand(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_depth_expand", opts, pool)); + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/A/A")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 0)); + + SVN_ERR(sbox_wc_update_depth(&b, "", 1, svn_depth_immediates, TRUE)); + SVN_ERR(sbox_wc_update_depth(&b, "A", 1, svn_depth_immediates, TRUE)); + /* Make A/B not present */ + SVN_ERR(sbox_wc_update_depth(&b, "A/B", 0, svn_depth_immediates, TRUE)); + + SVN_ERR(sbox_wc_move(&b, "A", "C")); + SVN_ERR(sbox_wc_mkdir(&b, "C/A/A")); /* Local addition obstruction */ + SVN_ERR(sbox_wc_copy(&b, "C/A", "C/B")); /* Copied obstruction */ + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, "" }, + + {0, "A", "normal", 1, "A" }, + {1, "A", "base-deleted", NO_COPY_FROM, "C" }, + {0, "A/A", "normal", 1, "A/A" }, + {1, "A/A", "base-deleted", NO_COPY_FROM }, + {0, "A/B", "not-present", 0, "A/B" }, + + {1, "C", "normal", 1, "A", MOVED_HERE }, + + {1, "C/A", "normal", 1, "A/A", MOVED_HERE }, + {3, "C/A/A", "normal", NO_COPY_FROM }, + + {1, "C/B", "not-present", 0, "A/B", MOVED_HERE}, + {2, "C/B", "normal", 1, "A/A" }, + {3, "C/B/A", "normal", NO_COPY_FROM }, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update_depth(&b, "", 1, svn_depth_infinity, TRUE)); + + /* This used to cause a segfault. Then it asserted in a different place */ + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + + /* And now verify that there are no not-present nodes left and a + consistent working copy */ + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, "" }, + + {0, "A", "normal", 1, "A" }, + {0, "A/A", "normal", 1, "A/A" }, + {0, "A/A/A", "normal", 1, "A/A/A" }, + {0, "A/A/A/A", "normal", 1, "A/A/A/A" }, + {0, "A/B", "normal", 1, "A/B" }, + {0, "A/B/A", "normal", 1, "A/B/A" }, + {0, "A/B/A/A", "normal", 1, "A/B/A/A" }, + + {1, "A", "base-deleted", NO_COPY_FROM, "C" }, + {1, "A/A", "base-deleted", NO_COPY_FROM }, + {1, "A/A/A", "base-deleted", NO_COPY_FROM }, + {1, "A/B", "base-deleted", NO_COPY_FROM }, + {1, "A/B/A", "base-deleted", NO_COPY_FROM }, + {1, "A/B/A/A", "base-deleted", NO_COPY_FROM }, + {1, "A/A/A/A", "base-deleted", NO_COPY_FROM }, + + + {1, "C", "normal", 1, "A", MOVED_HERE }, + {1, "C/A", "normal", 1, "A/A", MOVED_HERE }, + {1, "C/A/A", "normal", 1, "A/A/A", MOVED_HERE }, + {1, "C/A/A/A", "normal", 1, "A/A/A/A", MOVED_HERE }, + + {3, "C/A/A", "normal", NO_COPY_FROM }, + {3, "C/A/A/A", "base-deleted", NO_COPY_FROM }, + + {1, "C/B", "normal", 1, "A/B", MOVED_HERE }, + {1, "C/B/A", "normal", 1, "A/B/A", MOVED_HERE }, + {1, "C/B/A/A", "normal", 1, "A/B/A/A", MOVED_HERE }, + + {2, "C/B", "normal", 1, "A/A" }, + {2, "C/B/A", "base-deleted", NO_COPY_FROM }, + {2, "C/B/A/A", "base-deleted", NO_COPY_FROM }, + + {3, "C/B/A", "normal", NO_COPY_FROM }, + + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_retract(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_retract", opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/D")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/A/C")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/A/D")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_delete(&b, "A/A/A/A")); + SVN_ERR(sbox_wc_delete(&b, "A/A/A/C")); + SVN_ERR(sbox_wc_delete(&b, "A/A/A/D")); + SVN_ERR(sbox_wc_delete(&b, "A/B/A/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_move(&b, "A/B/A/D", "D")); + SVN_ERR(sbox_wc_delete(&b, "A/B")); + SVN_ERR(sbox_wc_move(&b, "A/A", "A/B")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, "" }, + + {0, "A", "normal", 1, "A" }, + {0, "A/A", "normal", 1, "A/A" }, + {0, "A/A/A", "normal", 1, "A/A/A" }, + {0, "A/A/A/A", "normal", 1, "A/A/A/A" }, + {0, "A/A/A/C", "normal", 1, "A/A/A/C" }, + {0, "A/A/A/D", "normal", 1, "A/A/A/D" }, + + {2, "A/A", "base-deleted", NO_COPY_FROM, "A/B"}, + {2, "A/A/A", "base-deleted", NO_COPY_FROM }, + {2, "A/A/A/A", "base-deleted", NO_COPY_FROM }, + {2, "A/A/A/C", "base-deleted", NO_COPY_FROM }, + {2, "A/A/A/D", "base-deleted", NO_COPY_FROM }, + + {0, "A/B", "normal", 1, "A/B" }, + {0, "A/B/A", "normal", 1, "A/B/A" }, + {0, "A/B/A/A", "normal", 1, "A/B/A/A" }, + {0, "A/B/A/C", "normal", 1, "A/B/A/C" }, + {0, "A/B/A/D", "normal", 1, "A/B/A/D" }, + + {2, "A/B", "normal", 1, "A/A", MOVED_HERE }, + {2, "A/B/A", "normal", 1, "A/A/A", MOVED_HERE }, + {2, "A/B/A/A", "normal", 1, "A/A/A/A", MOVED_HERE }, + {2, "A/B/A/C", "normal", 1, "A/A/A/C", MOVED_HERE }, + {2, "A/B/A/D", "normal", 1, "A/A/A/D", FALSE, "D", TRUE }, + + {1, "D", "normal", 1, "A/B/A/D", MOVED_HERE }, + + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "", 2)); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, "" }, + + {0, "A", "normal", 2, "A" }, + {0, "A/A", "normal", 2, "A/A" }, + {0, "A/A/A", "normal", 2, "A/A/A" }, + + {2, "A/A", "base-deleted", NO_COPY_FROM, "A/B"}, + {2, "A/A/A", "base-deleted", NO_COPY_FROM }, + + {0, "A/B", "normal", 2, "A/B" }, + {0, "A/B/A", "normal", 2, "A/B/A" }, + {0, "A/B/A/A", "normal", 2, "A/B/A/A" }, + {0, "A/B/A/D", "normal", 2, "A/B/A/D" }, + + {2, "A/B", "normal", 1, "A/A", MOVED_HERE }, + {2, "A/B/A", "normal", 1, "A/A/A", MOVED_HERE }, + {2, "A/B/A/A", "normal", 1, "A/A/A/A", MOVED_HERE }, + {2, "A/B/A/C", "normal", 1, "A/A/A/C", MOVED_HERE }, + {2, "A/B/A/D", "normal", 1, "A/A/A/D", FALSE, "D", TRUE }, + + {1, "D", "normal", 1, "A/B/A/D", MOVED_HERE }, + + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + + SVN_ERR(sbox_wc_resolve(&b, "A/A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 2, "" }, + + {0, "A", "normal", 2, "A" }, + {0, "A/A", "normal", 2, "A/A" }, + {0, "A/A/A", "normal", 2, "A/A/A" }, + + {0, "A/B", "normal", 2, "A/B" }, + {0, "A/B/A", "normal", 2, "A/B/A" }, + {0, "A/B/A/A", "normal", 2, "A/B/A/A" }, + {0, "A/B/A/D", "normal", 2, "A/B/A/D" }, + + {2, "A/A", "base-deleted", NO_COPY_FROM, "A/B"}, + {2, "A/A/A", "base-deleted", NO_COPY_FROM }, + + {2, "A/B", "normal", 2, "A/A", MOVED_HERE }, + {2, "A/B/A", "normal", 2, "A/A/A", MOVED_HERE }, + {2, "A/B/A/A", "base-deleted", NO_COPY_FROM }, /* ### MISSING! */ + {2, "A/B/A/D", "base-deleted", NO_COPY_FROM, "D" }, /* ### MISSING! */ + + /* Still conflicted */ + {1, "D", "normal", 1, "A/B/A/D", MOVED_HERE }, + + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* ### TODO: Resolve via which specific target? */ + SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, + svn_wc_conflict_choose_mine_conflict)); + + { + nodes_row_t nodes[] = { + + {1, "D", "normal", 2, "A/B/A/D", MOVED_HERE }, + + {0} + }; + SVN_ERR(check_db_rows(&b, "D", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_delete_file_externals(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_delete_file_externals", opts, + pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + sbox_file_write(&b, "f", "New file"); + SVN_ERR(sbox_wc_add(&b, "f")); + SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f B/P/g", "A")); + SVN_ERR(sbox_wc_propset(&b, "svn:externals", "^/f Q/g\n^/f g", "A/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "f", "normal", 1, "f"}, + {0, "A/B/g", "normal", 1, "f", TRUE}, + {0, "A/B/P/g", "normal", 1, "f", TRUE}, + {0, "A/B/Q/g", "normal", 1, "f", TRUE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Delete removes the file external rows. */ + SVN_ERR(sbox_wc_delete(&b, "A")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "f", "normal", 1, "f"}, + {1, "A", "base-deleted", NO_COPY_FROM}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Revert doesn't restore the file external rows... */ + SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_infinity)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "f", "normal", 1, "f"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + /* ... but update does. */ + SVN_ERR(sbox_wc_update(&b, "", 1)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "f", "normal", 1, "f"}, + {0, "A/B/g", "normal", 1, "f", TRUE}, + {0, "A/B/P/g", "normal", 1, "f", TRUE}, + {0, "A/B/Q/g", "normal", 1, "f", TRUE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Move removes the file external rows. */ + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "f", "normal", 1, "f"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + /* Update adds file external rows to the copy. */ + SVN_ERR(sbox_wc_update(&b, "", 1)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {0, "f", "normal", 1, "f"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {0, "A2/B/g", "normal", 1, "f", TRUE}, + {0, "A2/B/P/g", "normal", 1, "f", TRUE}, + {0, "A2/B/Q/g", "normal", 1, "f", TRUE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +update_with_tree_conflict(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "update_with_tree_conflict", opts, + pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + + SVN_ERR(sbox_wc_update_depth(&b, "A", 2, svn_depth_empty, FALSE)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_update(&b, "A", 2)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Second update overwrote the existing tree-conflict and that + causes the move-update to assert. */ + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_infinity, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 2, "A", MOVED_HERE}, + {1, "A2/B", "normal", 2, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 2, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Same again but second update is A/B rather than A which triggers + the problem through open_root rather than open_directory. */ + SVN_ERR(sbox_wc_revert(&b, "", svn_depth_infinity)); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + SVN_ERR(sbox_wc_update_depth(&b, "A", 2, svn_depth_empty, FALSE)); + SVN_ERR(sbox_wc_update(&b, "A/B", 2)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 1, "A", MOVED_HERE}, + {1, "A2/B", "normal", 1, "A/B", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_infinity, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A2"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {1, "A/B/C", "base-deleted", NO_COPY_FROM}, + {1, "A2", "normal", 2, "A", MOVED_HERE}, + {1, "A2/B", "normal", 2, "A/B", MOVED_HERE}, + {1, "A2/B/C", "normal", 2, "A/B/C", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_child_to_parent_revert(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_child_to_parent_revert", opts, + pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + + + SVN_ERR(sbox_wc_move(&b, "A/B", "B")); + SVN_ERR(sbox_wc_delete(&b, "A")); + + /* Verify that the move is still recorded correctly */ + { + nodes_row_t nodes[] = { + {0, "", "normal", 0, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + + {1, "A", "base-deleted", NO_COPY_FROM}, + {1, "A/B", "base-deleted", NO_COPY_FROM, "B"}, + + {1, "B", "normal", 1, "A/B", MOVED_HERE}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_infinity)); + + /* Verify that the move is now just a copy */ + { + nodes_row_t nodes[] = { + {0, "", "normal", 0, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 1, "A/B"}, + + {1, "B", "normal", 1, "A/B"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_delete_intermediate(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_delete_intermediate", opts, + pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "B")); + SVN_ERR(sbox_wc_mkdir(&b, "B/A")); + SVN_ERR(sbox_wc_mkdir(&b, "B/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "B/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "C")); + SVN_ERR(sbox_wc_mkdir(&b, "C/A")); + SVN_ERR(sbox_wc_mkdir(&b, "C/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "C/A/A/A")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_1")); + + SVN_ERR(sbox_wc_delete(&b, "A")); + SVN_ERR(sbox_wc_move(&b, "B", "A")); + + SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_2")); + + SVN_ERR(sbox_wc_delete(&b, "A/A")); + SVN_ERR(sbox_wc_move(&b, "C/A", "A/A")); + + SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_3")); + + /* Verify that the move is still recorded correctly */ + { + nodes_row_t nodes[] = { + + {0, "", "normal", 0, ""}, + + {1, "AAA_1", "normal", 1, "A/A/A", MOVED_HERE}, + {1, "AAA_1/A", "normal", 1, "A/A/A/A", MOVED_HERE}, + {1, "AAA_2", "normal", 1, "B/A/A", MOVED_HERE}, + {1, "AAA_2/A", "normal", 1, "B/A/A/A", MOVED_HERE}, + {1, "AAA_3", "normal", 1, "C/A/A", MOVED_HERE}, + {1, "AAA_3/A", "normal", 1, "C/A/A/A", MOVED_HERE}, + + {0, "A", "normal", 1, "A"}, + {0, "A/A", "normal", 1, "A/A"}, + {0, "A/A/A", "normal", 1, "A/A/A"}, + {0, "A/A/A/A", "normal", 1, "A/A/A/A"}, + + {1, "A", "normal", 1, "B", MOVED_HERE}, + {1, "A/A", "normal", 1, "B/A", MOVED_HERE}, + {1, "A/A/A", "normal", 1, "B/A/A", FALSE, "AAA_1", TRUE}, + {1, "A/A/A/A", "normal", 1, "B/A/A/A", MOVED_HERE}, + + {2, "A/A", "normal", 1, "C/A", MOVED_HERE}, + {2, "A/A/A", "normal", 1, "C/A/A", FALSE, "AAA_2", TRUE}, + {2, "A/A/A/A", "normal", 1, "C/A/A/A", MOVED_HERE}, + + {3, "A/A/A", "base-deleted", NO_COPY_FROM, "AAA_3"}, + {3, "A/A/A/A", "base-deleted", NO_COPY_FROM}, + + {0, "B", "normal", 1, "B"}, + {0, "B/A", "normal", 1, "B/A"}, + {0, "B/A/A", "normal", 1, "B/A/A"}, + {0, "B/A/A/A", "normal", 1, "B/A/A/A"}, + + {1, "B", "base-deleted", NO_COPY_FROM, "A"}, + {1, "B/A", "base-deleted", NO_COPY_FROM}, + {1, "B/A/A", "base-deleted", NO_COPY_FROM}, + {1, "B/A/A/A", "base-deleted", NO_COPY_FROM}, + + {0, "C", "normal", 1, "C"}, + {0, "C/A", "normal", 1, "C/A"}, + {0, "C/A/A", "normal", 1, "C/A/A"}, + {0, "C/A/A/A", "normal", 1, "C/A/A/A"}, + + {2, "C/A", "base-deleted", NO_COPY_FROM, "A/A"}, + {2, "C/A/A", "base-deleted", NO_COPY_FROM}, + {2, "C/A/A/A", "base-deleted", NO_COPY_FROM}, + + {0}, + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Ok, now we are in the very ugly case where A/A/A is moved away 3 times */ + + /* Let's delete A */ + SVN_ERR(sbox_wc_delete(&b, "A")); + + /* AAA_1, AAA_2 and AAA_3 should still be moves after deleting A */ + { + nodes_row_t nodes[] = { + + {0, "", "normal", 0, ""}, + + {1, "AAA_1", "normal", 1, "A/A/A", MOVED_HERE}, + {1, "AAA_1/A", "normal", 1, "A/A/A/A", MOVED_HERE}, + + {1, "AAA_2", "normal", 1, "B/A/A", MOVED_HERE}, + {1, "AAA_2/A", "normal", 1, "B/A/A/A", MOVED_HERE}, + + {1, "AAA_3", "normal", 1, "C/A/A", MOVED_HERE}, + {1, "AAA_3/A", "normal", 1, "C/A/A/A", MOVED_HERE}, + + {0, "A", "normal", 1, "A"}, + {0, "A/A", "normal", 1, "A/A"}, + {0, "A/A/A", "normal", 1, "A/A/A"}, + {0, "A/A/A/A", "normal", 1, "A/A/A/A"}, + + {1, "A", "base-deleted", NO_COPY_FROM}, + {1, "A/A", "base-deleted", NO_COPY_FROM}, + {1, "A/A/A", "base-deleted", NO_COPY_FROM, "AAA_1"}, + {1, "A/A/A/A", "base-deleted", NO_COPY_FROM}, + + {0, "B", "normal", 1, "B"}, + {0, "B/A", "normal", 1, "B/A"}, + {0, "B/A/A", "normal", 1, "B/A/A"}, + {0, "B/A/A/A", "normal", 1, "B/A/A/A"}, + + {1, "B", "base-deleted", NO_COPY_FROM}, + {1, "B/A", "base-deleted", NO_COPY_FROM}, + {1, "B/A/A", "base-deleted", NO_COPY_FROM, "AAA_2"}, + {1, "B/A/A/A", "base-deleted", NO_COPY_FROM}, + + {0, "C", "normal", 1, "C"}, + {0, "C/A", "normal", 1, "C/A"}, + {0, "C/A/A", "normal", 1, "C/A/A"}, + {0, "C/A/A/A", "normal", 1, "C/A/A/A"}, + + {2, "C/A", "base-deleted", NO_COPY_FROM}, + {2, "C/A/A", "base-deleted", NO_COPY_FROM, "AAA_3"}, + {2, "C/A/A/A", "base-deleted", NO_COPY_FROM}, + + {0}, + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_revert_intermediate(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_revert_intermediate", opts, + pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "B")); + SVN_ERR(sbox_wc_mkdir(&b, "B/A")); + SVN_ERR(sbox_wc_mkdir(&b, "B/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "B/A/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "C")); + SVN_ERR(sbox_wc_mkdir(&b, "C/A")); + SVN_ERR(sbox_wc_mkdir(&b, "C/A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "C/A/A/A")); + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_1")); + + SVN_ERR(sbox_wc_delete(&b, "A")); + SVN_ERR(sbox_wc_move(&b, "B", "A")); + + SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_2")); + + SVN_ERR(sbox_wc_delete(&b, "A/A")); + SVN_ERR(sbox_wc_move(&b, "C/A", "A/A")); + + SVN_ERR(sbox_wc_move(&b, "A/A/A", "AAA_3")); + + /* Verify that the move is still recorded correctly */ + { + nodes_row_t nodes[] = { + + {0, "", "normal", 0, ""}, + + {1, "AAA_1", "normal", 1, "A/A/A", MOVED_HERE}, + {1, "AAA_1/A", "normal", 1, "A/A/A/A", MOVED_HERE}, + {1, "AAA_2", "normal", 1, "B/A/A", MOVED_HERE}, + {1, "AAA_2/A", "normal", 1, "B/A/A/A", MOVED_HERE}, + {1, "AAA_3", "normal", 1, "C/A/A", MOVED_HERE}, + {1, "AAA_3/A", "normal", 1, "C/A/A/A", MOVED_HERE}, + + {0, "A", "normal", 1, "A"}, + {0, "A/A", "normal", 1, "A/A"}, + {0, "A/A/A", "normal", 1, "A/A/A"}, + {0, "A/A/A/A", "normal", 1, "A/A/A/A"}, + + {1, "A", "normal", 1, "B", MOVED_HERE}, + {1, "A/A", "normal", 1, "B/A", MOVED_HERE}, + {1, "A/A/A", "normal", 1, "B/A/A", FALSE, "AAA_1", TRUE}, + {1, "A/A/A/A", "normal", 1, "B/A/A/A", MOVED_HERE}, + + {2, "A/A", "normal", 1, "C/A", MOVED_HERE}, + {2, "A/A/A", "normal", 1, "C/A/A", FALSE, "AAA_2", TRUE}, + {2, "A/A/A/A", "normal", 1, "C/A/A/A", MOVED_HERE}, + + {3, "A/A/A", "base-deleted", NO_COPY_FROM, "AAA_3"}, + {3, "A/A/A/A", "base-deleted", NO_COPY_FROM}, + + {0, "B", "normal", 1, "B"}, + {0, "B/A", "normal", 1, "B/A"}, + {0, "B/A/A", "normal", 1, "B/A/A"}, + {0, "B/A/A/A", "normal", 1, "B/A/A/A"}, + + {1, "B", "base-deleted", NO_COPY_FROM, "A"}, + {1, "B/A", "base-deleted", NO_COPY_FROM}, + {1, "B/A/A", "base-deleted", NO_COPY_FROM}, + {1, "B/A/A/A", "base-deleted", NO_COPY_FROM}, + + {0, "C", "normal", 1, "C"}, + {0, "C/A", "normal", 1, "C/A"}, + {0, "C/A/A", "normal", 1, "C/A/A"}, + {0, "C/A/A/A", "normal", 1, "C/A/A/A"}, + + {2, "C/A", "base-deleted", NO_COPY_FROM, "A/A"}, + {2, "C/A/A", "base-deleted", NO_COPY_FROM}, + {2, "C/A/A/A", "base-deleted", NO_COPY_FROM}, + + {0}, + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* Ok, now we are in the very ugly case where A/A/A is moved away 3 times */ + + /* Let's revert A */ + SVN_ERR(sbox_wc_revert(&b, "A", svn_depth_infinity)); + + /* AAA_1 should now be a copy, but AAA_2 and AAA_3 should still be moves, + but now from the original location instead of from "A/A/A" */ + { + nodes_row_t nodes[] = { + + {0, "", "normal", 0, ""}, + + {1, "AAA_1", "normal", 1, "A/A/A",}, + {1, "AAA_1/A", "normal", 1, "A/A/A/A"}, + {1, "AAA_2", "normal", 1, "B/A/A", MOVED_HERE}, + {1, "AAA_2/A", "normal", 1, "B/A/A/A", MOVED_HERE}, + {1, "AAA_3", "normal", 1, "C/A/A", MOVED_HERE}, + {1, "AAA_3/A", "normal", 1, "C/A/A/A", MOVED_HERE}, + + {0, "A", "normal", 1, "A"}, + {0, "A/A", "normal", 1, "A/A"}, + {0, "A/A/A", "normal", 1, "A/A/A"}, + {0, "A/A/A/A", "normal", 1, "A/A/A/A"}, + + {0, "B", "normal", 1, "B"}, + {0, "B/A", "normal", 1, "B/A"}, + {0, "B/A/A", "normal", 1, "B/A/A"}, + {0, "B/A/A/A", "normal", 1, "B/A/A/A"}, + + {1, "B", "base-deleted", NO_COPY_FROM}, + {1, "B/A", "base-deleted", NO_COPY_FROM}, + {1, "B/A/A", "base-deleted", NO_COPY_FROM, "AAA_2"}, + {1, "B/A/A/A", "base-deleted", NO_COPY_FROM}, + + {0, "C", "normal", 1, "C"}, + {0, "C/A", "normal", 1, "C/A"}, + {0, "C/A/A", "normal", 1, "C/A/A"}, + {0, "C/A/A/A", "normal", 1, "C/A/A/A"}, + + {2, "C/A", "base-deleted", NO_COPY_FROM}, + {2, "C/A/A", "base-deleted", NO_COPY_FROM, "AAA_3"}, + {2, "C/A/A/A", "base-deleted", NO_COPY_FROM}, + + {0}, + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +copy_mixed_rev_mods(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "copy_mixed_rev_mods", opts, + pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B/C")); + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + SVN_ERR(sbox_wc_update(&b, "A/B", 2)); + SVN_ERR(sbox_wc_delete(&b, "A/B")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 1, ""}, + {0, "A", "normal", 1, "A"}, + {0, "A/B", "normal", 2, "A/B"}, + {0, "A/B/C", "normal", 2, "A/B/C"}, + {2, "A/B", "normal", NO_COPY_FROM}, + {2, "A/B/C", "base-deleted", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_copy(&b, "A", "X")); + { + nodes_row_t nodes[] = { + {1, "X", "normal", 1, "A"}, + {1, "X/B", "not-present", 2, "A/B"}, + {2, "X/B", "normal", NO_COPY_FROM}, + {0} + }; + SVN_ERR(check_db_rows(&b, "X", nodes)); + } + + SVN_ERR(sbox_wc_commit(&b, "X")); + { + nodes_row_t nodes[] = { + {0, "X", "normal", 3, "X"}, + {0, "X/B", "normal", 3, "X/B"}, + {0} + }; + SVN_ERR(check_db_rows(&b, "X", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_replace_ancestor_with_child(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_replace_ancestor_with_child", opts, + pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A")); + + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A", "A2")); + + { + nodes_row_t nodes[] = { + + { 0, "", "normal", 1, "" }, + + { 0, "A", "normal", 1, "A"}, + { 0, "A/A", "normal", 1, "A/A" }, + + { 1, "A", "base-deleted", NO_COPY_FROM , "A2"}, + { 1, "A/A", "base-deleted", NO_COPY_FROM }, + + { 1, "A2", "normal", 1, "A", MOVED_HERE }, + { 1, "A2/A", "normal", 1, "A/A", MOVED_HERE }, + + { 0 }, + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_move(&b, "A2/A", "A")); + + { + nodes_row_t nodes[] = { + { 0, "", "normal", 1, "" }, + + { 0, "A", "normal", 1, "A"}, + { 0, "A/A", "normal", 1, "A/A" }, + + { 1, "A", "normal", 1, "A/A", FALSE, "A2", TRUE }, + { 1, "A/A", "base-deleted", NO_COPY_FROM }, + + { 1, "A2", "normal", 1, "A", MOVED_HERE }, + { 1, "A2/A", "normal", 1, "A/A", MOVED_HERE }, + + { 2, "A2/A", "base-deleted", NO_COPY_FROM, "A" }, + { 0 }, + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + /* ### This currently fails with an assertion in maintainer mode */ + SVN_ERR(sbox_wc_delete(&b, "A2")); + + { + nodes_row_t nodes[] = { + { 0, "", "normal", 1, "" }, + + { 0, "A", "normal", 1, "A"}, + { 0, "A/A", "normal", 1, "A/A" }, + + { 1, "A", "normal", 1, "A/A", MOVED_HERE }, + { 1, "A/A", "base-deleted", NO_COPY_FROM, "A" }, + + { 0 }, + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_commit(&b, "A")); + + return SVN_NO_ERROR; +} + +static svn_error_t * +move_twice_within_delete(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "move_twice_within_delete", opts, + pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/A/A")); + + SVN_ERR(sbox_wc_commit(&b, "")); + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_mkdir(&b, "B")); + SVN_ERR(sbox_wc_move(&b, "A", "B/A")); + SVN_ERR(sbox_wc_move(&b, "B/A/A", "B/AA")); + SVN_ERR(sbox_wc_move(&b, "B/AA/A", "AA")); + + { + nodes_row_t nodes[] = { + + { 0, "", "normal", 1, "" }, + + { 0, "A", "normal", 1, "A" }, + { 0, "A/A", "normal", 1, "A/A" }, + { 0, "A/A/A", "normal", 1, "A/A/A" }, + + { 1, "A", "base-deleted", NO_COPY_FROM, "B/A" }, + { 1, "A/A", "base-deleted", NO_COPY_FROM }, + { 1, "A/A/A", "base-deleted", NO_COPY_FROM }, + + { 1, "AA", "normal", 1, "A/A/A", MOVED_HERE }, + + { 1, "B", "normal", NO_COPY_FROM }, + { 2, "B/A", "normal", 1, "A", MOVED_HERE }, + { 2, "B/A/A", "normal", 1, "A/A", MOVED_HERE }, + { 2, "B/A/A/A", "normal", 1, "A/A/A", MOVED_HERE }, + + { 3, "B/A/A", "base-deleted", NO_COPY_FROM, "B/AA" }, + { 3, "B/A/A/A", "base-deleted", NO_COPY_FROM }, + + { 2, "B/AA", "normal", 1, "A/A", MOVED_HERE}, + { 2, "B/AA/A", "normal", 1, "A/A/A", MOVED_HERE }, + + { 3, "B/AA/A", "base-deleted", NO_COPY_FROM, "AA" }, + + { 0 }, + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + SVN_ERR(sbox_wc_delete(&b, "B")); + + { + nodes_row_t nodes[] = { + { 0, "", "normal", 1, "" }, + + { 0, "A", "normal", 1, "A" }, + { 0, "A/A", "normal", 1, "A/A" }, + { 0, "A/A/A", "normal", 1, "A/A/A" }, + + { 1, "A", "base-deleted", NO_COPY_FROM }, + { 1, "A/A", "base-deleted", NO_COPY_FROM }, + { 1, "A/A/A", "base-deleted", NO_COPY_FROM, "AA" }, + + { 1, "AA", "normal", 1, "A/A/A", MOVED_HERE }, + + { 0 }, + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +repo_wc_copy(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + const char *repos_dir; + const char *new_repos_dir; + const char *new_repos_url; + + SVN_ERR(svn_test__sandbox_create(&b, "repo_wc_copy", + opts, pool)); + SVN_ERR(sbox_add_and_commit_greek_tree(&b)); + + SVN_ERR(sbox_wc_copy_url(&b, + svn_path_url_add_component2(b.repos_url, "A/B", + pool), + -1, "AA")); + + { + nodes_row_t nodes[] = { + + {1, "AA/lambda", "normal", 1, "A/B/lambda"}, + {1, "AA", "normal", 1, "A/B"}, + {1, "AA/E/beta", "normal", 1, "A/B/E/beta"}, + {1, "AA/E/alpha", "normal", 1, "A/B/E/alpha"}, + {1, "AA/F", "normal", 1, "A/B/F"}, + {1, "AA/E", "normal", 1, "A/B/E"}, + + { 0 }, + }; + SVN_ERR(check_db_rows(&b, "AA", nodes)); + } + + SVN_ERR(svn_uri_get_dirent_from_file_url(&repos_dir, b.repos_url, + pool)); + new_repos_dir = apr_pstrcat(pool, repos_dir, "-2", SVN_VA_NULL); + new_repos_url = apr_pstrcat(pool, b.repos_url, "-2", SVN_VA_NULL); + + svn_test_add_dir_cleanup(new_repos_dir); + + SVN_ERR(svn_io_remove_dir2(new_repos_dir, TRUE, NULL, NULL, pool)); + SVN_ERR(svn_io_copy_dir_recursively(repos_dir, + svn_dirent_dirname(new_repos_dir, pool), + svn_dirent_basename(new_repos_dir, pool), + FALSE, NULL, NULL, pool)); + + SVN_ERR(sbox_wc_relocate(&b, new_repos_url)); + + /* This produced an invalid copy in Subversion <= 1.8.8. + Status would show all descendants as incomplete */ + SVN_ERR(sbox_wc_copy_url(&b, + svn_path_url_add_component2(b.repos_url, "A/B", + pool), + -1, "BB")); + + { + nodes_row_t nodes[] = { + + {1, "BB/lambda", "normal", 1, "A/B/lambda"}, + {1, "BB", "normal", 1, "A/B"}, + {1, "BB/E/beta", "normal", 1, "A/B/E/beta"}, + {1, "BB/E/alpha", "normal", 1, "A/B/E/alpha"}, + {1, "BB/F", "normal", 1, "A/B/F"}, + {1, "BB/E", "normal", 1, "A/B/E"}, + + { 0 }, + }; + SVN_ERR(check_db_rows(&b, "BB", nodes)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +movedhere_extract_retract(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "movedhere_extract_retract", + opts, pool)); + + SVN_ERR(sbox_wc_mkdir(&b, "A")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B1")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B2")); + SVN_ERR(sbox_wc_mkdir(&b, "A/B3")); + SVN_ERR(sbox_wc_mkdir(&b, "A/C1")); + SVN_ERR(sbox_wc_mkdir(&b, "A/C2")); + SVN_ERR(sbox_wc_mkdir(&b, "A/C3")); + SVN_ERR(sbox_wc_mkdir(&b, "A/D1")); + SVN_ERR(sbox_wc_mkdir(&b, "A/D2")); + SVN_ERR(sbox_wc_mkdir(&b, "A/D3")); + + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_propset(&b, "k", "v", "A/B1")); + SVN_ERR(sbox_wc_propset(&b, "k", "v", "A/B2")); + SVN_ERR(sbox_wc_propset(&b, "k", "v", "A/B3")); + SVN_ERR(sbox_wc_delete(&b, "A/C1")); + SVN_ERR(sbox_wc_delete(&b, "A/C2")); + SVN_ERR(sbox_wc_delete(&b, "A/C3")); + SVN_ERR(sbox_wc_mkdir(&b, "A/E1")); + SVN_ERR(sbox_wc_mkdir(&b, "A/E2")); + SVN_ERR(sbox_wc_mkdir(&b, "A/E3")); + + SVN_ERR(sbox_wc_commit(&b, "")); + + SVN_ERR(sbox_wc_update(&b, "", 1)); + + SVN_ERR(sbox_wc_move(&b, "A", "Z")); + + SVN_ERR(sbox_wc_delete(&b, "Z/B1")); + SVN_ERR(sbox_wc_delete(&b, "Z/C1")); + SVN_ERR(sbox_wc_delete(&b, "Z/D1")); + + SVN_ERR(sbox_wc_move(&b, "Z/B2", "B2")); + SVN_ERR(sbox_wc_move(&b, "Z/C2", "C2")); + SVN_ERR(sbox_wc_move(&b, "Z/D2", "D2")); + + SVN_ERR(sbox_wc_mkdir(&b, "Z/B2")); + SVN_ERR(sbox_wc_mkdir(&b, "Z/C2")); + SVN_ERR(sbox_wc_mkdir(&b, "Z/D2")); + SVN_ERR(sbox_wc_mkdir(&b, "Z/E2")); + + SVN_ERR(sbox_wc_update(&b, "", 2)); + SVN_ERR(sbox_wc_resolve(&b, "A", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + SVN_ERR(sbox_wc_resolve(&b, "Z/B1", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + SVN_ERR(sbox_wc_resolve(&b, "Z/B2", svn_depth_empty, + svn_wc_conflict_choose_mine_conflict)); + + SVN_ERR(sbox_wc_resolve(&b, "Z/C1", svn_depth_empty, + svn_wc_conflict_choose_merged)); + SVN_ERR(sbox_wc_resolve(&b, "Z/C2", svn_depth_empty, + svn_wc_conflict_choose_merged)); + + SVN_ERR(sbox_wc_resolve(&b, "", svn_depth_infinity, + svn_wc_conflict_choose_mine_conflict)); + { + nodes_row_t nodes[] = { + + {0, "", "normal", 2, ""}, + {0, "A", "normal", 2, "A"}, + {0, "A/B1", "normal", 2, "A/B1", FALSE, NULL, FALSE, "k"}, + {0, "A/B2", "normal", 2, "A/B2", FALSE, NULL, FALSE, "k"}, + {0, "A/B3", "normal", 2, "A/B3", FALSE, NULL, FALSE, "k"}, + {0, "A/D1", "normal", 2, "A/D1"}, + {0, "A/D2", "normal", 2, "A/D2"}, + {0, "A/D3", "normal", 2, "A/D3"}, + {0, "A/E1", "normal", 2, "A/E1"}, + {0, "A/E2", "normal", 2, "A/E2"}, + {0, "A/E3", "normal", 2, "A/E3"}, + + {1, "A", "base-deleted", NO_COPY_FROM, "Z"}, + {1, "A/B1", "base-deleted", NO_COPY_FROM}, + {1, "A/B2", "base-deleted", NO_COPY_FROM}, + {1, "A/B3", "base-deleted", NO_COPY_FROM}, + + {1, "A/D1", "base-deleted", NO_COPY_FROM}, + {1, "A/D2", "base-deleted", NO_COPY_FROM}, + {1, "A/D3", "base-deleted", NO_COPY_FROM}, + + {1, "A/E1", "base-deleted", NO_COPY_FROM}, + {1, "A/E2", "base-deleted", NO_COPY_FROM}, + {1, "A/E3", "base-deleted", NO_COPY_FROM}, + + {1, "B2", "normal", 2, "A/B2", MOVED_HERE, "k"}, + {1, "C2", "normal", 1, "A/C2"}, + {1, "D2", "normal", 1, "A/D2", MOVED_HERE}, + + {1, "Z", "normal", 2, "A", MOVED_HERE}, + {1, "Z/B1", "normal", 2, "A/B1", MOVED_HERE, "k"}, + {1, "Z/B2", "normal", 2, "A/B2", MOVED_HERE, "k"}, + {1, "Z/B3", "normal", 2, "A/B3", MOVED_HERE, "k"}, + {1, "Z/D1", "normal", 2, "A/D1", MOVED_HERE}, + {1, "Z/D2", "normal", 2, "A/D2", MOVED_HERE}, + {1, "Z/D3", "normal", 2, "A/D3", MOVED_HERE}, + {1, "Z/E1", "normal", 2, "A/E1", MOVED_HERE}, + {1, "Z/E2", "normal", 2, "A/E2", MOVED_HERE}, + {1, "Z/E3", "normal", 2, "A/E3", MOVED_HERE}, + + {2, "Z/B2", "normal", NO_COPY_FROM, "B2"}, + {2, "Z/C2", "normal", NO_COPY_FROM}, + {2, "Z/D2", "normal", NO_COPY_FROM, "D2"}, + {2, "Z/E2", "normal", NO_COPY_FROM}, + + {2, "Z/B1", "base-deleted", NO_COPY_FROM}, + {2, "Z/D1", "base-deleted", NO_COPY_FROM}, + + { 0 }, + }; + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + + +static svn_error_t * +nested_move_delete(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t b; + + SVN_ERR(svn_test__sandbox_create(&b, "nested_move_delete", opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(&b)); + + SVN_ERR(sbox_wc_mkdir(&b, "A/Z")); + SVN_ERR(sbox_wc_move(&b, "A/B/lambda", "A/Z/lambda")); + + SVN_ERR(sbox_wc_delete(&b, "A/B")); + + { + nodes_row_t nodes_AB[] = { + {0, "A/B", "normal", 1, "A/B"}, + {2, "A/B", "base-deleted", NO_COPY_FROM}, + {0, "A/B/E", "normal", 1, "A/B/E"}, + {2, "A/B/E", "base-deleted", NO_COPY_FROM}, + {0, "A/B/E/alpha", "normal", 1, "A/B/E/alpha"}, + {2, "A/B/E/alpha", "base-deleted", NO_COPY_FROM}, + {0, "A/B/E/beta", "normal", 1, "A/B/E/beta"}, + {2, "A/B/E/beta", "base-deleted", NO_COPY_FROM}, + {0, "A/B/F", "normal", 1, "A/B/F"}, + {2, "A/B/F", "base-deleted", NO_COPY_FROM}, + {0, "A/B/lambda", "normal", 1, "A/B/lambda"}, + {2, "A/B/lambda", "base-deleted", NO_COPY_FROM, "A/Z/lambda"}, + {0} + }; + nodes_row_t nodes_AZ[] = { + {2, "A/Z", "normal", NO_COPY_FROM}, + {3, "A/Z/lambda", "normal", 1, "A/B/lambda", MOVED_HERE }, + {0} + }; + + SVN_ERR(check_db_rows(&b, "A/B", nodes_AB)); + SVN_ERR(check_db_rows(&b, "A/Z", nodes_AZ)); + } + + SVN_ERR(sbox_wc_move(&b, "A", "A_moved")); + + { + nodes_row_t nodes[] = { + {0, "", "normal", 0, ""}, + {0, "A", "normal", 1, "A"}, + {1, "A", "base-deleted", NO_COPY_FROM, "A_moved"}, + {0, "A/B", "normal", 1, "A/B"}, + {1, "A/B", "base-deleted", NO_COPY_FROM}, + {0, "A/B/E", "normal", 1, "A/B/E"}, + {1, "A/B/E", "base-deleted", NO_COPY_FROM}, + {0, "A/B/E/alpha", "normal", 1, "A/B/E/alpha"}, + {1, "A/B/E/alpha", "base-deleted", NO_COPY_FROM}, + {0, "A/B/E/beta", "normal", 1, "A/B/E/beta"}, + {1, "A/B/E/beta", "base-deleted", NO_COPY_FROM}, + {0, "A/B/F", "normal", 1, "A/B/F"}, + {1, "A/B/F", "base-deleted", NO_COPY_FROM}, + {0, "A/B/lambda", "normal", 1, "A/B/lambda"}, + {1, "A/B/lambda", "base-deleted", NO_COPY_FROM}, + {0, "A/C", "normal", 1, "A/C"}, + {1, "A/C", "base-deleted", NO_COPY_FROM}, + {0, "A/D", "normal", 1, "A/D"}, + {1, "A/D", "base-deleted", NO_COPY_FROM}, + {0, "A/D/G", "normal", 1, "A/D/G"}, + {1, "A/D/G", "base-deleted", NO_COPY_FROM}, + {0, "A/D/G/pi", "normal", 1, "A/D/G/pi"}, + {1, "A/D/G/pi", "base-deleted", NO_COPY_FROM}, + {0, "A/D/G/rho", "normal", 1, "A/D/G/rho"}, + {1, "A/D/G/rho", "base-deleted", NO_COPY_FROM}, + {0, "A/D/G/tau", "normal", 1, "A/D/G/tau"}, + {1, "A/D/G/tau", "base-deleted", NO_COPY_FROM}, + {0, "A/D/H", "normal", 1, "A/D/H"}, + {1, "A/D/H", "base-deleted", NO_COPY_FROM}, + {0, "A/D/H/chi", "normal", 1, "A/D/H/chi"}, + {1, "A/D/H/chi", "base-deleted", NO_COPY_FROM}, + {0, "A/D/H/omega", "normal", 1, "A/D/H/omega"}, + {1, "A/D/H/omega", "base-deleted", NO_COPY_FROM}, + {0, "A/D/H/psi", "normal", 1, "A/D/H/psi"}, + {1, "A/D/H/psi", "base-deleted", NO_COPY_FROM}, + {0, "A/D/gamma", "normal", 1, "A/D/gamma"}, + {1, "A/D/gamma", "base-deleted", NO_COPY_FROM}, + {0, "A/mu", "normal", 1, "A/mu"}, + {1, "A/mu", "base-deleted", NO_COPY_FROM}, + {1, "A_moved", "normal", 1, "A", MOVED_HERE}, + {1, "A_moved/B", "normal", 1, "A/B", MOVED_HERE}, + {2, "A_moved/B", "base-deleted", NO_COPY_FROM}, + {2, "A_moved/B/E", "base-deleted", NO_COPY_FROM}, + {1, "A_moved/B/E", "normal", 1, "A/B/E", MOVED_HERE}, + {1, "A_moved/B/E/alpha", "normal", 1, "A/B/E/alpha", MOVED_HERE}, + {2, "A_moved/B/E/alpha", "base-deleted", NO_COPY_FROM}, + {1, "A_moved/B/E/beta", "normal", 1, "A/B/E/beta", MOVED_HERE}, + {2, "A_moved/B/E/beta", "base-deleted", NO_COPY_FROM}, + {1, "A_moved/B/F", "normal", 1, "A/B/F", MOVED_HERE}, + {2, "A_moved/B/F", "base-deleted", NO_COPY_FROM}, + {1, "A_moved/B/lambda", "normal", 1, "A/B/lambda", MOVED_HERE}, + {2, "A_moved/B/lambda", "base-deleted", NO_COPY_FROM, "A_moved/Z/lambda"}, + {1, "A_moved/C", "normal", 1, "A/C", MOVED_HERE}, + {1, "A_moved/D", "normal", 1, "A/D", MOVED_HERE}, + {1, "A_moved/D/G", "normal", 1, "A/D/G", MOVED_HERE}, + {1, "A_moved/D/G/pi", "normal", 1, "A/D/G/pi", MOVED_HERE}, + {1, "A_moved/D/G/rho", "normal", 1, "A/D/G/rho", MOVED_HERE}, + {1, "A_moved/D/G/tau", "normal", 1, "A/D/G/tau", MOVED_HERE}, + {1, "A_moved/D/H", "normal", 1, "A/D/H", MOVED_HERE}, + {1, "A_moved/D/H/chi", "normal", 1, "A/D/H/chi", MOVED_HERE}, + {1, "A_moved/D/H/omega", "normal", 1, "A/D/H/omega", MOVED_HERE}, + {1, "A_moved/D/H/psi", "normal", 1, "A/D/H/psi", MOVED_HERE}, + {1, "A_moved/D/gamma", "normal", 1, "A/D/gamma", MOVED_HERE}, + {2, "A_moved/Z", "normal", NO_COPY_FROM}, + {3, "A_moved/Z/lambda", "normal", 1, "A/B/lambda", MOVED_HERE}, + {1, "A_moved/mu", "normal", 1, "A/mu", MOVED_HERE}, + {0, "iota", "normal", 1, "iota"}, + + {0} + }; + + SVN_ERR(check_db_rows(&b, "", nodes)); + } + + return SVN_NO_ERROR; +} + /* ---------------------------------------------------------------------- */ /* The list of test functions */ @@ -3606,7 +9081,116 @@ struct svn_test_descriptor_t test_funcs[] = #endif SVN_TEST_OPTS_PASS(commit_file_external, "commit_file_external (issue #4002)"), + SVN_TEST_OPTS_PASS(revert_file_externals, + "revert_file_externals"), + SVN_TEST_OPTS_PASS(copy_file_externals, + "copy_file_externals"), + SVN_TEST_OPTS_PASS(copy_wc_wc_server_excluded, + "test_wc_wc_copy_server_excluded"), SVN_TEST_OPTS_PASS(incomplete_switch, "incomplete_switch (issue 4040)"), + SVN_TEST_OPTS_PASS(nested_moves_child_first, + "nested_moves_child_first"), + SVN_TEST_OPTS_PASS(nested_moves_child_last, + "nested_moves_child_last"), + SVN_TEST_OPTS_PASS(move_in_copy, + "move_in_copy"), + SVN_TEST_OPTS_PASS(move_in_replace, + "move_in_replace"), + SVN_TEST_OPTS_PASS(copy_a_move, + "copy_a_move"), + SVN_TEST_OPTS_PASS(move_to_swap, + "move_to_swap"), + SVN_TEST_OPTS_PASS(revert_nested_move, + "revert_nested_move"), + SVN_TEST_OPTS_PASS(move_on_move, + "move_on_move"), + SVN_TEST_OPTS_PASS(move_on_move2, + "move_on_move2"), + SVN_TEST_OPTS_PASS(move_added, + "move_added"), + SVN_TEST_OPTS_PASS(move_update, + "move_update"), + SVN_TEST_OPTS_PASS(test_scan_delete, + "scan_delete"), + SVN_TEST_OPTS_PASS(test_follow_moved_to, + "follow_moved_to"), + SVN_TEST_OPTS_WIMP(mixed_rev_move, + "mixed_rev_move", + "needs different libsvn_wc entry point"), + SVN_TEST_OPTS_PASS(update_prop_mod_into_moved, + "update_prop_mod_into_moved"), + SVN_TEST_OPTS_PASS(nested_move_update, + "nested_move_update"), + SVN_TEST_OPTS_PASS(nested_move_commit, + "nested_move_commit (issue 4291)"), + SVN_TEST_OPTS_PASS(nested_move_update2, + "nested_move_update2"), + SVN_TEST_OPTS_PASS(move_update_conflicts, + "move_update_conflicts"), + SVN_TEST_OPTS_PASS(move_update_delete_mods, + "move_update_delete_mods"), + SVN_TEST_OPTS_PASS(nested_moves2, + "nested_moves2"), + SVN_TEST_OPTS_PASS(move_in_delete, + "move_in_delete (issue 4303)"), + SVN_TEST_OPTS_PASS(switch_move, + "switch_move"), + SVN_TEST_OPTS_PASS(move_replace, + "move_replace"), + SVN_TEST_OPTS_PASS(layered_moved_to, + "layered_moved_to"), + SVN_TEST_OPTS_PASS(update_within_move, + "update_within_move"), + SVN_TEST_OPTS_PASS(commit_moved_descendant, + "commit_moved_descendant"), + SVN_TEST_OPTS_XFAIL(commit_moved_away_descendant, + "commit_moved_away_descendant"), + SVN_TEST_OPTS_PASS(finite_move_update_bump, + "finite_move_update_bump"), + SVN_TEST_OPTS_PASS(move_away_delete_update, + "move_away_delete_update"), + SVN_TEST_OPTS_PASS(move_not_present_variants, + "move_not_present_variants"), + SVN_TEST_OPTS_PASS(update_child_under_add, + "update_child_under_add (issue 4111)"), + SVN_TEST_OPTS_PASS(delete_over_moved_away, + "delete_over_moved_away"), + SVN_TEST_OPTS_PASS(movedto_opdepth, + "moved_to op_depth"), + SVN_TEST_OPTS_PASS(new_basemove, + "new_basemove"), + SVN_TEST_OPTS_PASS(move_back, + "move_back (issue 4302)"), + SVN_TEST_OPTS_PASS(move_update_subtree, + "move_update_subtree (issue 4232)"), + SVN_TEST_OPTS_PASS(move_parent_into_child, + "move_parent_into_child (issue 4333)"), + SVN_TEST_OPTS_PASS(move_depth_expand, + "move depth expansion"), + SVN_TEST_OPTS_PASS(move_retract, + "move retract (issue 4336)"), + SVN_TEST_OPTS_PASS(move_delete_file_externals, + "move/delete file externals (issue 4293)"), + SVN_TEST_OPTS_PASS(update_with_tree_conflict, + "update with tree conflict (issue 4347)"), + SVN_TEST_OPTS_PASS(move_child_to_parent_revert, + "move child to parent and revert (issue 4436)"), + SVN_TEST_OPTS_PASS(move_delete_intermediate, + "move more than once, delete intermediate"), + SVN_TEST_OPTS_XFAIL(move_revert_intermediate, + "move more than once, revert intermediate"), + SVN_TEST_OPTS_PASS(move_replace_ancestor_with_child, + "move replace ancestor with child"), + SVN_TEST_OPTS_PASS(move_twice_within_delete, + "move twice and then delete"), + SVN_TEST_OPTS_PASS(repo_wc_copy, + "repo_wc_copy"), + SVN_TEST_OPTS_PASS(copy_mixed_rev_mods, + "copy mixed-rev with mods"), + SVN_TEST_OPTS_PASS(movedhere_extract_retract, + "movedhere extract retract"), + SVN_TEST_OPTS_PASS(nested_move_delete, + "nested move delete"), SVN_TEST_NULL }; diff --git a/subversion/tests/libsvn_wc/pristine-store-test.c b/subversion/tests/libsvn_wc/pristine-store-test.c index 4227026..6df96fc 100644 --- a/subversion/tests/libsvn_wc/pristine-store-test.c +++ b/subversion/tests/libsvn_wc/pristine-store-test.c @@ -187,8 +187,7 @@ pristine_write_read(const svn_test_opts_t *opts, SVN_ERR(svn_wc__db_pristine_remove(db, wc_abspath, data_sha1, pool)); err = svn_wc__db_pristine_read(&data_read_back, NULL, db, wc_abspath, data_sha1, pool, pool); - SVN_TEST_ASSERT(err != NULL); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_PATH_NOT_FOUND); } /* Ensure it's no longer found in the store. */ @@ -308,9 +307,7 @@ reject_mismatching_text(const svn_test_opts_t *opts, SVN_ERR(write_and_checksum_temp_file(&path, NULL, NULL, data2, pristine_tmp_dir, pool)); err = svn_wc__db_pristine_install(db, path, data_sha1, data_md5, pool); - SVN_TEST_ASSERT(err != NULL); - SVN_TEST_ASSERT(err->apr_err == SVN_ERR_WC_CORRUPT_TEXT_BASE); - svn_error_clear(err); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_CORRUPT_TEXT_BASE); } return SVN_NO_ERROR; diff --git a/subversion/tests/libsvn_wc/tree-conflict-data-test.c b/subversion/tests/libsvn_wc/tree-conflict-data-test.c deleted file mode 100644 index 40d42ac..0000000 --- a/subversion/tests/libsvn_wc/tree-conflict-data-test.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * - * 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. - * - */ - -/* - * tree-conflict-data-test.c -- test the storage of tree conflict data - */ - -#include <stdio.h> -#include <string.h> -#include <apr_hash.h> -#include <apr_tables.h> - -#include "svn_pools.h" -#include "svn_hash.h" -#include "svn_types.h" -#include "svn_wc.h" -#include "private/svn_wc_private.h" -#include "utils.h" -#include "../svn_test.h" -#include "../../libsvn_wc/tree_conflicts.h" -#include "../../libsvn_wc/wc.h" -#include "../../libsvn_wc/wc_db.h" - -/* A quick way to create error messages. */ -static svn_error_t * -fail(apr_pool_t *pool, const char *fmt, ...) -{ - va_list ap; - char *msg; - - va_start(ap, fmt); - msg = apr_pvsprintf(pool, fmt, ap); - va_end(ap); - - return svn_error_create(SVN_ERR_TEST_FAILED, 0, msg); -} - -/* Raise a test error if EXPECTED and ACTUAL differ. */ -static svn_error_t * -compare_version(const svn_wc_conflict_version_t *expected, - const svn_wc_conflict_version_t *actual) -{ - SVN_TEST_STRING_ASSERT(expected->repos_url, actual->repos_url); - SVN_TEST_ASSERT(expected->peg_rev == actual->peg_rev); - SVN_TEST_STRING_ASSERT(expected->path_in_repos, actual->path_in_repos); - SVN_TEST_ASSERT(expected->node_kind == actual->node_kind); - return SVN_NO_ERROR; -} - -/* Raise a test error if EXPECTED and ACTUAL differ or if ACTUAL is NULL. */ -static svn_error_t * -compare_conflict(const svn_wc_conflict_description2_t *expected, - const svn_wc_conflict_description2_t *actual) -{ - SVN_TEST_ASSERT(actual != NULL); - - SVN_TEST_STRING_ASSERT(expected->local_abspath, actual->local_abspath); - SVN_TEST_ASSERT(expected->node_kind == actual->node_kind); - SVN_TEST_ASSERT(expected->kind == actual->kind); - SVN_TEST_STRING_ASSERT(expected->property_name, actual->property_name); - SVN_TEST_ASSERT(expected->is_binary == actual->is_binary); - SVN_TEST_STRING_ASSERT(expected->mime_type, actual->mime_type); - SVN_TEST_ASSERT(expected->action == actual->action); - SVN_TEST_ASSERT(expected->reason == actual->reason); - SVN_TEST_STRING_ASSERT(expected->base_abspath, actual->base_abspath); - SVN_TEST_STRING_ASSERT(expected->their_abspath, actual->their_abspath); - SVN_TEST_STRING_ASSERT(expected->my_abspath, actual->my_abspath); - SVN_TEST_STRING_ASSERT(expected->merged_file, actual->merged_file); - SVN_TEST_ASSERT(expected->operation == actual->operation); - compare_version(expected->src_left_version, actual->src_left_version); - compare_version(expected->src_right_version, actual->src_right_version); - return SVN_NO_ERROR; -} - -/* Create and return a tree conflict description */ -static svn_wc_conflict_description2_t * -tree_conflict_create(const char *local_abspath, - svn_node_kind_t node_kind, - svn_wc_operation_t operation, - svn_wc_conflict_action_t action, - svn_wc_conflict_reason_t reason, - const char *left_repo, - const char *left_path, - svn_revnum_t left_revnum, - svn_node_kind_t left_kind, - const char *right_repo, - const char *right_path, - svn_revnum_t right_revnum, - svn_node_kind_t right_kind, - apr_pool_t *result_pool) -{ - svn_wc_conflict_version_t *left, *right; - svn_wc_conflict_description2_t *conflict; - - left = svn_wc_conflict_version_create(left_repo, left_path, left_revnum, - left_kind, result_pool); - right = svn_wc_conflict_version_create(right_repo, right_path, right_revnum, - right_kind, result_pool); - conflict = svn_wc_conflict_description_create_tree2( - local_abspath, node_kind, operation, - left, right, result_pool); - conflict->action = action; - conflict->reason = reason; - return conflict; -} - -static svn_error_t * -test_deserialize_tree_conflict(apr_pool_t *pool) -{ - const svn_wc_conflict_description2_t *conflict; - svn_wc_conflict_description2_t *exp_conflict; - const char *tree_conflict_data; - const char *local_abspath; - const svn_skel_t *skel; - - tree_conflict_data = "(conflict Foo.c file update deleted edited " - "(version 0 2 -1 0 0 ) (version 0 2 -1 0 0 ))"; - - SVN_ERR(svn_dirent_get_absolute(&local_abspath, "Foo.c", pool)); - exp_conflict = svn_wc_conflict_description_create_tree2( - local_abspath, svn_node_file, svn_wc_operation_update, - NULL, NULL, pool); - exp_conflict->action = svn_wc_conflict_action_delete; - exp_conflict->reason = svn_wc_conflict_reason_edited; - - skel = svn_skel__parse(tree_conflict_data, strlen(tree_conflict_data), pool); - SVN_ERR(svn_wc__deserialize_conflict(&conflict, skel, "", pool, pool)); - - if ((conflict->node_kind != exp_conflict->node_kind) || - (conflict->action != exp_conflict->action) || - (conflict->reason != exp_conflict->reason) || - (conflict->operation != exp_conflict->operation) || - (strcmp(conflict->local_abspath, exp_conflict->local_abspath) != 0)) - return fail(pool, "Unexpected tree conflict"); - - return SVN_NO_ERROR; -} - -static svn_error_t * -test_serialize_tree_conflict(apr_pool_t *pool) -{ - svn_wc_conflict_description2_t *conflict; - const char *tree_conflict_data; - const char *expected; - const char *local_abspath; - svn_skel_t *skel; - - SVN_ERR(svn_dirent_get_absolute(&local_abspath, "Foo.c", pool)); - - conflict = svn_wc_conflict_description_create_tree2( - local_abspath, svn_node_file, svn_wc_operation_update, - NULL, NULL, pool); - conflict->action = svn_wc_conflict_action_delete; - conflict->reason = svn_wc_conflict_reason_edited; - - SVN_ERR(svn_wc__serialize_conflict(&skel, conflict, pool, pool)); - tree_conflict_data = svn_skel__unparse(skel, pool)->data; - - expected = "(conflict Foo.c file update deleted edited " - "(version 0 2 -1 0 0 ) (version 0 2 -1 0 0 ))"; - - if (strcmp(expected, tree_conflict_data) != 0) - return fail(pool, "Unexpected text from tree conflict\n" - " Expected: %s\n" - " Actual: %s\n", expected, tree_conflict_data); - - return SVN_NO_ERROR; -} - -/* Test WC-DB-level conflict APIs. Especially tree conflicts. */ -static svn_error_t * -test_read_write_tree_conflicts(const svn_test_opts_t *opts, - apr_pool_t *pool) -{ - svn_test__sandbox_t sbox; - - const char *parent_abspath; - const char *child1_abspath, *child2_abspath; - svn_wc_conflict_description2_t *conflict1, *conflict2; - - SVN_ERR(svn_test__sandbox_create(&sbox, "read_write_tree_conflicts", opts, pool)); - parent_abspath = svn_dirent_join(sbox.wc_abspath, "A", pool); - SVN_ERR(svn_wc__db_op_add_directory(sbox.wc_ctx->db, parent_abspath, NULL, - pool)); - child1_abspath = svn_dirent_join(parent_abspath, "foo", pool); - child2_abspath = svn_dirent_join(parent_abspath, "bar", pool); - - conflict1 = tree_conflict_create(child1_abspath, svn_node_file, - svn_wc_operation_update, - svn_wc_conflict_action_delete, - svn_wc_conflict_reason_edited, - "dummy://localhost", "path/to/foo", - 51, svn_node_file, - "dummy://localhost", "path/to/foo", - 52, svn_node_none, - pool); - - conflict2 = tree_conflict_create(child2_abspath, svn_node_dir, - svn_wc_operation_merge, - svn_wc_conflict_action_replace, - svn_wc_conflict_reason_edited, - "dummy://localhost", "path/to/bar", - 51, svn_node_dir, - "dummy://localhost", "path/to/bar", - 52, svn_node_file, - pool); - - /* Write (conflict1 through WC-DB API, conflict2 through WC API) */ - SVN_ERR(svn_wc__db_op_set_tree_conflict(sbox.wc_ctx->db, child1_abspath, - conflict1, pool)); - SVN_ERR(svn_wc__add_tree_conflict(sbox.wc_ctx, /*child2_abspath,*/ - conflict2, pool)); - - /* Query (conflict1 through WC-DB API, conflict2 through WC API) */ - { - svn_boolean_t text_c, prop_c, tree_c; - - SVN_ERR(svn_wc__internal_conflicted_p(&text_c, &prop_c, &tree_c, - sbox.wc_ctx->db, child1_abspath, pool)); - SVN_TEST_ASSERT(tree_c); - SVN_TEST_ASSERT(! text_c && ! prop_c); - - SVN_ERR(svn_wc_conflicted_p3(&text_c, &prop_c, &tree_c, - sbox.wc_ctx, child2_abspath, pool)); - SVN_TEST_ASSERT(tree_c); - SVN_TEST_ASSERT(! text_c && ! prop_c); - } - - /* Read one (conflict1 through WC-DB API, conflict2 through WC API) */ - { - const svn_wc_conflict_description2_t *read_conflict; - - SVN_ERR(svn_wc__db_op_read_tree_conflict(&read_conflict, sbox.wc_ctx->db, - child1_abspath, pool, pool)); - SVN_ERR(compare_conflict(conflict1, read_conflict)); - - SVN_ERR(svn_wc__get_tree_conflict(&read_conflict, sbox.wc_ctx, - child2_abspath, pool, pool)); - SVN_ERR(compare_conflict(conflict2, read_conflict)); - } - - /* Read many (both through WC-DB API, both through WC API) */ - { - apr_hash_t *all_conflicts; - const svn_wc_conflict_description2_t *read_conflict; - - SVN_ERR(svn_wc__db_op_read_all_tree_conflicts( - &all_conflicts, sbox.wc_ctx->db, parent_abspath, pool, pool)); - SVN_TEST_ASSERT(apr_hash_count(all_conflicts) == 2); - read_conflict = apr_hash_get(all_conflicts, "foo", APR_HASH_KEY_STRING); - SVN_ERR(compare_conflict(conflict1, read_conflict)); - read_conflict = apr_hash_get(all_conflicts, "bar", APR_HASH_KEY_STRING); - SVN_ERR(compare_conflict(conflict2, read_conflict)); - - SVN_ERR(svn_wc__get_all_tree_conflicts( - &all_conflicts, sbox.wc_ctx, parent_abspath, pool, pool)); - SVN_TEST_ASSERT(apr_hash_count(all_conflicts) == 2); - read_conflict = apr_hash_get(all_conflicts, child1_abspath, - APR_HASH_KEY_STRING); - SVN_ERR(compare_conflict(conflict1, read_conflict)); - read_conflict = apr_hash_get(all_conflicts, child2_abspath, - APR_HASH_KEY_STRING); - SVN_ERR(compare_conflict(conflict2, read_conflict)); - } - - /* ### TODO: to test... - * svn_wc__db_read_conflict_victims - * svn_wc__db_read_conflicts - * svn_wc__node_get_conflict_info - * svn_wc__del_tree_conflict - */ - - return SVN_NO_ERROR; -} - -/* The test table. */ - -struct svn_test_descriptor_t test_funcs[] = - { - SVN_TEST_NULL, - SVN_TEST_PASS2(test_deserialize_tree_conflict, - "deserialize tree conflict"), - SVN_TEST_PASS2(test_serialize_tree_conflict, - "serialize tree conflict"), - SVN_TEST_OPTS_PASS(test_read_write_tree_conflicts, - "read and write tree conflicts"), - SVN_TEST_NULL - }; - diff --git a/subversion/tests/libsvn_wc/utils.c b/subversion/tests/libsvn_wc/utils.c index 49d3303..1682b98 100644 --- a/subversion/tests/libsvn_wc/utils.c +++ b/subversion/tests/libsvn_wc/utils.c @@ -23,6 +23,7 @@ #include "svn_error.h" #include "svn_client.h" #include "svn_pools.h" +#include "private/svn_dep_compat.h" #include "utils.h" @@ -81,17 +82,18 @@ create_repos_and_wc(const char **repos_url, /* 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_client_create_context(&ctx, pool)); - /* SVN_ERR(svn_config_get_config(&ctx->config, config_dir, pool)); */ + SVN_ERR(svn_client_create_context2(&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, pool)); + ctx, subpool)); + svn_pool_destroy(subpool); } /* Register this WC for cleanup. */ @@ -132,7 +134,8 @@ svn_test__create_fake_wc(const char *wc_abspath, svn_error_clear(svn_io_remove_file2(db_abspath, FALSE, scratch_pool)); SVN_ERR(svn_wc__db_util_open_db(&sdb, wc_abspath, "wc.db", - svn_sqlite__mode_rwcreate, my_statements, + svn_sqlite__mode_rwcreate, + FALSE /* exclusive */, my_statements, result_pool, scratch_pool)); for (i = 0; my_statements[i] != NULL; i++) SVN_ERR(svn_sqlite__exec_statements(sdb, /* my_statements[] */ i)); @@ -153,3 +156,377 @@ svn_test__sandbox_create(svn_test__sandbox_t *sandbox, SVN_ERR(svn_wc_context_create(&sandbox->wc_ctx, NULL, pool, pool)); return SVN_NO_ERROR; } + +void +sbox_file_write(svn_test__sandbox_t *b, const char *path, const char *text) +{ + FILE *f = fopen(sbox_wc_path(b, path), "w"); + + fputs(text, f); + fclose(f); +} + +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_disk2(b->wc_ctx, path, NULL /*props*/, + 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_client_create_context2(&ctx, NULL, scratch_pool)); + ctx->wc_ctx = b->wc_ctx; + + 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_copy6(sources, sbox_wc_path(b, to_path), + FALSE, FALSE, FALSE, NULL, NULL, NULL, + 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_revert4(b->wc_ctx, abspath, depth, FALSE, NULL, + 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_client_create_context2(&ctx, NULL, scratch_pool)); + ctx->wc_ctx = b->wc_ctx; + + /* 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; + + revision.kind = svn_opt_revision_number; + revision.value.number = revnum; + + APR_ARRAY_PUSH(paths, const char *) = sbox_wc_path(b, path); + SVN_ERR(svn_client_create_context2(&ctx, NULL, b->pool)); + ctx->wc_ctx = b->wc_ctx; + 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, (char*)NULL); + SVN_ERR(svn_client_create_context2(&ctx, NULL, b->pool)); + ctx->wc_ctx = b->wc_ctx; + 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_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_client_create_context2(&ctx, NULL, b->pool)); + ctx->wc_ctx = b->wc_ctx; + 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_client_create_context2(&ctx, NULL, b->pool)); + ctx->wc_ctx = b->wc_ctx; + 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_client_create_context2(&ctx, NULL, scratch_pool)); + ctx->wc_ctx = b->wc_ctx; + + 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) + { + 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; +} diff --git a/subversion/tests/libsvn_wc/utils.h b/subversion/tests/libsvn_wc/utils.h index 83114e2..3004634 100644 --- a/subversion/tests/libsvn_wc/utils.h +++ b/subversion/tests/libsvn_wc/utils.h @@ -74,6 +74,107 @@ svn_test__sandbox_create(svn_test__sandbox_t *sandbox, const svn_test_opts_t *opts, apr_pool_t *pool); +/* ---------------------------------------------------------------------- */ +/* Functions for easy manipulation of a WC. Paths given to these functions + * can be relative to the WC root as stored in the WC baton. */ + +/* Return the abspath of PATH which is absolute or relative to the WC in B. */ +#define sbox_wc_path(b, path) \ + (svn_dirent_join((b)->wc_abspath, (path), (b)->pool)) + +/* Create a file on disk at PATH, with TEXT as its content. */ +void +sbox_file_write(svn_test__sandbox_t *b, const char *path, const char *text); + +/* Schedule for addition the single node that exists on disk at PATH, + * non-recursively. */ +svn_error_t * +sbox_wc_add(svn_test__sandbox_t *b, const char *path); + +/* Create a single directory on disk. */ +svn_error_t * +sbox_disk_mkdir(svn_test__sandbox_t *b, const char *path); + +/* Create a single directory on disk and schedule it for addition. */ +svn_error_t * +sbox_wc_mkdir(svn_test__sandbox_t *b, const char *path); + +/* Copy the WC file or directory tree FROM_PATH to TO_PATH which must not + * exist beforehand. */ +svn_error_t * +sbox_wc_copy(svn_test__sandbox_t *b, const char *from_path, const char *to_path); + +svn_error_t * +sbox_wc_copy_url(svn_test__sandbox_t *b, const char *from_url, + svn_revnum_t revision, const char *to_path); + +svn_error_t * +sbox_wc_relocate(svn_test__sandbox_t *b, + const char *new_repos_url); + +/* Revert a WC file or directory tree at PATH */ +svn_error_t * +sbox_wc_revert(svn_test__sandbox_t *b, const char *path, svn_depth_t depth); + +/* */ +svn_error_t * +sbox_wc_delete(svn_test__sandbox_t *b, const char *path); + +/* */ +svn_error_t * +sbox_wc_exclude(svn_test__sandbox_t *b, const char *path); + +/* */ +svn_error_t * +sbox_wc_commit(svn_test__sandbox_t *b, const char *path); + +/* */ +svn_error_t * +sbox_wc_commit_ex(svn_test__sandbox_t *b, + apr_array_header_t *targets, + svn_depth_t depth); + +/* */ +svn_error_t * +sbox_wc_update(svn_test__sandbox_t *b, const char *path, svn_revnum_t revnum); + +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_error_t * +sbox_wc_switch(svn_test__sandbox_t *b, + const char *path, + const char *url, + svn_depth_t depth); + +/* */ +svn_error_t * +sbox_wc_resolved(svn_test__sandbox_t *b, const char *path); + +/* */ +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); + +/* */ +svn_error_t * +sbox_wc_move(svn_test__sandbox_t *b, const char *src, const char *dst); + +/* Set property NAME to VALUE on PATH. If VALUE=NULL, delete the property. */ +svn_error_t * +sbox_wc_propset(svn_test__sandbox_t *b, + const char *name, + const char *value, + const char *path); + +/* Create the Greek tree on disk in the WC, and commit it. */ +svn_error_t * +sbox_add_and_commit_greek_tree(svn_test__sandbox_t *b); + /* Create a WC directory at WC_ABSPATH containing a fake WC DB, generated by * executing the SQL statements EXTRA_STATEMENTS in addition to the standard diff --git a/subversion/tests/libsvn_wc/wc-incomplete-tester.c b/subversion/tests/libsvn_wc/wc-incomplete-tester.c index bd66641..2aac605 100644 --- a/subversion/tests/libsvn_wc/wc-incomplete-tester.c +++ b/subversion/tests/libsvn_wc/wc-incomplete-tester.c @@ -78,7 +78,7 @@ int main(int argc, const char *argv[]) "Mark WCPATH incomplete at REVISION [and REPOS_RELPATH]\n"); exit(EXIT_FAILURE); } - + if (apr_initialize()) { fprintf(stderr, "apr_initialize failed\n"); diff --git a/subversion/tests/libsvn_wc/wc-queries-test.c b/subversion/tests/libsvn_wc/wc-queries-test.c new file mode 100644 index 0000000..0621720 --- /dev/null +++ b/subversion/tests/libsvn_wc/wc-queries-test.c @@ -0,0 +1,897 @@ +/* + * wc-queries-test.c -- test the evaluation of the wc Sqlite queries + * + * ==================================================================== + * 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_pools.h" +#include "svn_ctype.h" +#include "private/svn_dep_compat.h" + +#include "svn_private_config.h" + +#include "../svn_test.h" + +#ifdef SVN_SQLITE_INLINE +/* Include sqlite3 inline, making all symbols private. */ + #define SQLITE_API static + #ifdef __APPLE__ + #include <Availability.h> + #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + /* <libkern/OSAtomic.h> is included on OS X by sqlite3.c, and + on old systems (Leopard or older), it cannot be compiled + with -std=c89 because it uses inline. This is a work-around. */ + #define inline __inline__ + #include <libkern/OSAtomic.h> + #undef inline + #endif + #endif + #include <sqlite3.c> +#else + #include <sqlite3.h> +#endif + +#include "../../libsvn_wc/wc-queries.h" + +WC_QUERIES_SQL_DECLARE_STATEMENTS(wc_queries); +WC_QUERIES_SQL_DECLARE_STATEMENT_INFO(wc_query_info); + +/* The first query after the normal wc queries */ +#define STMT_SCHEMA_FIRST STMT_CREATE_SCHEMA + +#define SQLITE_ERR(x) \ +{ \ + int sqlite_err__temp = (x); \ + if (sqlite_err__temp != SQLITE_OK) \ + return svn_error_createf(SVN_ERR_SQLITE_ERROR, \ + NULL, "sqlite: %s", \ + sqlite3_errmsg(sdb)); \ +} while (0) + +/* Schema creation statements fail during preparing when the table + already exists, and must be evaluated before testing the + queries. Statements above STMT_SCHEMA_FIRST only need to be + included here when they need to be evaluated before testing the + statements */ +static const int schema_statements[] = +{ + /* Usual tables */ + STMT_CREATE_SCHEMA, + STMT_CREATE_NODES, + STMT_CREATE_NODES_TRIGGERS, + STMT_CREATE_EXTERNALS, + STMT_INSTALL_SCHEMA_STATISTICS, + /* Memory tables */ + STMT_CREATE_TARGETS_LIST, + STMT_CREATE_CHANGELIST_LIST, + STMT_CREATE_CHANGELIST_TRIGGER, + STMT_CREATE_TARGET_PROP_CACHE, + STMT_CREATE_REVERT_LIST, + STMT_CREATE_DELETE_LIST, + STMT_CREATE_UPDATE_MOVE_LIST, + -1 /* final marker */ +}; + +/* These statements currently trigger warnings. It would be nice if + we could annotate these in wc-queries.sql */ +static const int slow_statements[] = +{ + /* Operate on the entire WC */ + STMT_SELECT_ALL_NODES, /* schema validation code */ + + /* Updates all records for a repository (designed slow) */ + STMT_UPDATE_LOCK_REPOS_ID, + + /* Full temporary table read */ + STMT_INSERT_ACTUAL_EMPTIES, + STMT_SELECT_REVERT_LIST_RECURSIVE, + STMT_SELECT_DELETE_LIST, + STMT_SELECT_UPDATE_MOVE_LIST, + + /* Designed as slow to avoid penalty on other queries */ + STMT_SELECT_UNREFERENCED_PRISTINES, + + /* Slow, but just if foreign keys are enabled: + * STMT_DELETE_PRISTINE_IF_UNREFERENCED, + */ + STMT_HAVE_STAT1_TABLE, /* Queries sqlite_master which has no index */ + + -1 /* final marker */ +}; + +/* Statements that just read the first record from a table, + using the primary key. Specialized as different sqlite + versions produce different results */ +static const int primary_key_statements[] = +{ + /* Is there a record? ### Can we somehow check for LIMIT 1, + and primary key instead of adding a list? */ + STMT_LOOK_FOR_WORK, + STMT_SELECT_WORK_ITEM, + + -1 /* final marker */ +}; + +/* Helper function to determine if a statement is in a list */ +static svn_boolean_t +in_list(const int list[], int stmt_idx) +{ + int i; + + for (i = 0; list[i] != -1; i++) + { + if (list[i] == stmt_idx) + return TRUE; + } + return FALSE; +} + +/* Helpers to determine if a statement is in a common list */ +#define is_slow_statement(stmt_idx) in_list(slow_statements, stmt_idx) +#define is_schema_statement(stmt_idx) \ + ((stmt_idx >= STMT_SCHEMA_FIRST) || in_list(schema_statements, stmt_idx)) + + +/* Create an in-memory db for evaluating queries */ +static svn_error_t * +create_memory_db(sqlite3 **db, + apr_pool_t *pool) +{ + sqlite3 *sdb; + int i; + + /* Create an in-memory raw database */ + SVN_TEST_ASSERT(sqlite3_initialize() == SQLITE_OK); + SQLITE_ERR(sqlite3_open_v2("", &sdb, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + NULL)); + + /* Create schema */ + for (i = 0; schema_statements[i] != -1; i++) + { + SQLITE_ERR(sqlite3_exec(sdb, wc_queries[schema_statements[i]], NULL, NULL, NULL)); + } + + *db = sdb; + return SVN_NO_ERROR; +} + +/* Verify sqlite3 runtime version */ +static svn_error_t * +test_sqlite_version(apr_pool_t *scratch_pool) +{ + printf("DBG: Using Sqlite %s\n", sqlite3_version); + + if (sqlite3_libversion_number() != SQLITE_VERSION_NUMBER) + printf("DBG: Compiled against Sqlite %s", SQLITE_VERSION); + + if (sqlite3_libversion_number() < SQLITE_VERSION_NUMBER) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Compiled against Sqlite %s (at runtime we have Sqlite %s)", + SQLITE_VERSION, sqlite3_version); + +#if !SQLITE_VERSION_AT_LEAST(3, 7, 9) + return svn_error_create(SVN_ERR_TEST_FAILED, NULL, + "Sqlite upgrade recommended:\n" + "****************************************************************\n" + "* Subversion needs at least SQLite 3.7.9 to work optimally *\n" + "* *\n" + "* With older versions, at least some queries that are expected *\n" + "* to be using an index are not. This makes some operations use *\n" + "* every node in the working copy instead of just one. *\n" + "* *\n" + "* While Subversion works correctly in this case, you may see *\n" + "* slowdowns of WELL MORE THAN 1000* in some cases! *\n" + "* *\n" + "* *\n" + "* SQLITE UPGRADE RECOMMENDED *\n" + "****************************************************************\n"); +#else + return SVN_NO_ERROR; +#endif +} + +/* Parse all normal queries */ +static svn_error_t * +test_parsable(apr_pool_t *scratch_pool) +{ + sqlite3 *sdb; + int i; + + SVN_ERR(create_memory_db(&sdb, scratch_pool)); + + for (i=0; i < STMT_SCHEMA_FIRST; i++) + { + sqlite3_stmt *stmt; + const char *text = wc_queries[i]; + + if (is_schema_statement(i)) + continue; + + /* Some of our statement texts contain multiple queries. We prepare + them all. */ + while (*text != '\0') + { + const char *tail; + int r = sqlite3_prepare_v2(sdb, text, -1, &stmt, &tail); + + if (r != SQLITE_OK) + return svn_error_createf(SVN_ERR_SQLITE_ERROR, NULL, + "Preparing %s failed: %s\n%s", + wc_query_info[i][0], + sqlite3_errmsg(sdb), + text); + + SQLITE_ERR(sqlite3_finalize(stmt)); + + /* Continue after the current statement */ + text = tail; + } + } + + SQLITE_ERR(sqlite3_close(sdb)); /* Close the DB if ok; otherwise leaked */ + + return SVN_NO_ERROR; +} + +/* Contains a parsed record from EXPLAIN QUERY PLAN */ +struct explanation_item +{ + const char *operation; + const char *table; + const char *alias; + svn_boolean_t scan; + svn_boolean_t search; + svn_boolean_t covered_by_index; + svn_boolean_t primary_key; + svn_boolean_t automatic_index; + const char *index; + const char *expressions; + const char *expected; + + const char *compound_left; + const char *compound_right; + svn_boolean_t create_btree; + + int expression_vars; + int expected_rows; +}; + +#define MATCH_TOKEN(x, y) (x && (strcmp(x, y) == 0)) + +/* Simple parser for the Sqlite textual explanation into an explanation_item. + Writes "DBG:" lines when sqlite produces unexpected results. When no + valid explanation_item can be parsed sets *PARSED_ITEM to NULL, otherwise + to a valid result. */ +static svn_error_t * +parse_explanation_item(struct explanation_item **parsed_item, + const char *text, + apr_pool_t *result_pool) +{ + struct explanation_item *item = apr_pcalloc(result_pool, sizeof(*item)); + char *token; + char *last; + char *tmp = apr_pstrdup(result_pool, text); + const char *tmp_end = &tmp[strlen(tmp)]; + + *parsed_item = NULL; + + item->operation = apr_strtok(tmp, " ", &last); + + if (!item->operation) + { + return SVN_NO_ERROR; + } + + item->scan = MATCH_TOKEN(item->operation, "SCAN"); + + if (item->scan || MATCH_TOKEN(item->operation, "SEARCH")) + { + item->search = TRUE; /* Search or scan */ + token = apr_strtok(NULL, " ", &last); + + if (!MATCH_TOKEN(token, "TABLE")) + { + printf("DBG: Expected 'TABLE', got '%s' in '%s'\n", token, text); + return SVN_NO_ERROR; /* Nothing to parse */ + } + + item->table = apr_strtok(NULL, " ", &last); + + token = apr_strtok(NULL, " ", &last); + + /* Skip alias */ + if (MATCH_TOKEN(token, "AS")) + { + item->alias = apr_strtok(NULL, " ", &last); + token = apr_strtok(NULL, " ", &last); + } + + if (MATCH_TOKEN(token, "USING")) + { + token = apr_strtok(NULL, " ", &last); + + if (MATCH_TOKEN(token, "AUTOMATIC")) + { + /* Pain: A temporary index is created */ + item->automatic_index = TRUE; + token = apr_strtok(NULL, " ", &last); + } + + /* Handle COVERING */ + if (MATCH_TOKEN(token, "COVERING")) + { + /* Bonus: Query will be answered by just using the index */ + item->covered_by_index = TRUE; + token = apr_strtok(NULL, " ", &last); + } + + if (MATCH_TOKEN(token, "INDEX")) + { + item->index = apr_strtok(NULL, " ", &last); + } + else if (MATCH_TOKEN(token, "INTEGER")) + { + token = apr_strtok(NULL, " ", &last); + if (!MATCH_TOKEN(token, "PRIMARY")) + { + printf("DBG: Expected 'PRIMARY', got '%s' in '%s'\n", + token, text); + return SVN_NO_ERROR; + } + + token = apr_strtok(NULL, " ", &last); + if (!MATCH_TOKEN(token, "KEY")) + { + printf("DBG: Expected 'KEY', got '%s' in '%s'\n", + token, text); + return SVN_NO_ERROR; + } + + item->primary_key = TRUE; + } + else + { + printf("DBG: Expected 'INDEX' or 'PRIMARY', got '%s' in '%s'\n", + token, text); + return SVN_NO_ERROR; + } + + token = apr_strtok(NULL, " ", &last); + } + + if (token && token[0] == '(' && token[1] != '~') + { + /* Undo the tokenization to switch parser rules */ + size_t token_len = strlen(token); + + if (token + token_len < tmp_end) + token[token_len] = ' '; + + if (token[token_len] == '\0') + last[-1] = ' '; + + token++; /* Skip the '(' */ + + item->expressions = apr_strtok(token, ")", &last); + token = apr_strtok(NULL, " ", &last); + } + + if (token && *token == '(' && token[1] == '~') + { + /* Undo the tokenization to switch parser rules */ + size_t token_len = strlen(token); + + if (token + token_len < tmp_end) + token[token_len] = ' '; + + if (token[token_len] == '\0') + last[-1] = ' '; + + token += 2; /* Skip "(~" */ + + item->expected = apr_strtok(token, ")", &last); + token = apr_strtok(NULL, " ", &last); + } + + if (token) + { + printf("DBG: Unexpected token '%s' in '%s'\n", + token, text); + return SVN_NO_ERROR; + } + + /* Parsing successfull */ + } + else if (MATCH_TOKEN(item->operation, "EXECUTE")) + { + /* Subquery handling */ + return SVN_NO_ERROR; + } + else if (MATCH_TOKEN(item->operation, "COMPOUND")) + { + /* 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")) + { + 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; + } + } + else if (MATCH_TOKEN(item->operation, "USE")) + { + /* Using a temporary table for ordering results */ + /* ### Need parsing */ + item->create_btree = TRUE; + } + else + { + printf("DBG: Unhandled sqlite operation '%s' in explanation\n", item->operation); + return SVN_NO_ERROR; + } + + if (item->expressions) + { + const char *p; + + for (p = item->expressions; *p; p++) + { + if (*p == '?') + item->expression_vars++; + } + } + if (item->expected) + { + item->expected_rows = atoi(item->expected); + } + + *parsed_item = item; + return SVN_NO_ERROR; +} + +/* Sqlite has an SQLITE_OMIT_EXPLAIN compilation flag, which may make + explain query just evaluate the query. Some older versions use a + different number of columns (and different texts) for + EXPLAIN query plan. + + If none of this is true set *SUPPORTED to TRUE, otherwise to FALSE */ +static svn_error_t * +supported_explain_query_plan(svn_boolean_t *supported, + sqlite3 *sdb, + apr_pool_t *scratch_pool) +{ + sqlite3_stmt *stmt; + int r; + + *supported = TRUE; + + r = sqlite3_prepare(sdb, "EXPLAIN QUERY PLAN SELECT 1", + -1, &stmt, NULL); + + if (r != SQLITE_OK) + { + *supported = FALSE; + return SVN_NO_ERROR; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) + { + if (sqlite3_column_count(stmt) < 4) + { + *supported = FALSE; + /* Fall through */ + } + } + + SQLITE_ERR(sqlite3_reset(stmt)); + SQLITE_ERR(sqlite3_finalize(stmt)); + return SVN_NO_ERROR; +} + + +/* Returns TRUE if TABLE_NAME specifies a nodes table, which should be indexed + by wc_id and either local_relpath or parent_relpath */ +static svn_boolean_t +is_node_table(const char *table_name) +{ + return (apr_strnatcasecmp(table_name, "nodes") == 0 + || apr_strnatcasecmp(table_name, "actual_node") == 0 + || apr_strnatcasecmp(table_name, "externals") == 0 + || apr_strnatcasecmp(table_name, "lock") == 0 + || apr_strnatcasecmp(table_name, "wc_lock") == 0 + || FALSE); +} + +/* Returns TRUE if TABLE specifies an intermediate result table, which is + allowed to have table scans, etc. */ +static svn_boolean_t +is_result_table(const char *table_name) +{ + return (apr_strnatcasecmp(table_name, "target_prop_cache") == 0 + || apr_strnatcasecmp(table_name, "changelist_list") == 0 + || FALSE); +} + +static svn_error_t * +test_query_expectations(apr_pool_t *scratch_pool) +{ + sqlite3 *sdb; + int i; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + svn_error_t *warnings = NULL; + svn_boolean_t supports_query_info; + + SVN_ERR(create_memory_db(&sdb, scratch_pool)); + + SVN_ERR(supported_explain_query_plan(&supports_query_info, sdb, + scratch_pool)); + if (!supports_query_info) + { + SQLITE_ERR(sqlite3_close(sdb)); + return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, + "Sqlite doesn't support EXPLAIN QUERY PLAN"); + } + + for (i=0; i < STMT_SCHEMA_FIRST; i++) + { + sqlite3_stmt *stmt; + const char *tail; + int r; + svn_boolean_t warned = FALSE; + apr_array_header_t *rows = NULL; + + if (is_schema_statement(i)) + continue; + + /* Prepare statement to find if it is a single statement. */ + r = sqlite3_prepare_v2(sdb, wc_queries[i], -1, &stmt, &tail); + + if (r != SQLITE_OK) + continue; /* Parse failure is already reported by 'test_parable' */ + + SQLITE_ERR(sqlite3_finalize(stmt)); + if (tail[0] != '\0') + continue; /* Multi-queries are currently not testable */ + + svn_pool_clear(iterpool); + + r = sqlite3_prepare_v2(sdb, + apr_pstrcat(iterpool, + "EXPLAIN QUERY PLAN ", + wc_queries[i], + NULL), + -1, &stmt, &tail); + + if (r != SQLITE_OK) + continue; /* EXPLAIN not enabled or doesn't support this query */ + + while (SQLITE_ROW == (r = sqlite3_step(stmt))) + { + /*int iSelectid; + int iOrder; + int iFrom;*/ + const unsigned char *zDetail; + char *detail; + struct explanation_item *item; + + /* ### The following code is correct for current Sqlite versions + ### (tested with 3.7.x), but the EXPLAIN QUERY PLAN output + ### is not guaranteed to be stable for future versions. */ + + /* Names as in Sqlite documentation */ + /*iSelectid = sqlite3_column_int(stmt, 0); + iOrder = sqlite3_column_int(stmt, 1); + iFrom = sqlite3_column_int(stmt, 2);*/ + zDetail = sqlite3_column_text(stmt, 3); + + if (! zDetail) + continue; + + if (!rows) + rows = apr_array_make(iterpool, 10, sizeof(const char*)); + + detail = apr_pstrdup(iterpool, (const char*)zDetail); + + APR_ARRAY_PUSH(rows, const char *) = detail; + + SVN_ERR(parse_explanation_item(&item, detail, iterpool)); + + if (!item) + continue; /* Not parsable or not interesting */ + + if (item->search + && item->automatic_index) + { + warned = TRUE; + if (!is_slow_statement(i)) + { + warnings = svn_error_createf(SVN_ERR_TEST_FAILED, warnings, + "%s: " + "Creates a temporary index: %s\n", + wc_query_info[i][0], wc_queries[i]); + } + } + else if (item->search && item->primary_key) + { + /* Nice */ + } + else if (item->search + && ((item->expression_vars < 2 && is_node_table(item->table)) + || (item->expression_vars < 1)) + && !is_result_table(item->table)) + { + 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 + identical: read first record from table */ + } + else if (!is_slow_statement(i)) + { + warned = TRUE; + warnings = svn_error_createf(SVN_ERR_TEST_FAILED, warnings, + "%s: " + "Uses %s with only %d index component: (%s)\n%s", + wc_query_info[i][0], item->table, + item->expression_vars, item->expressions, + wc_queries[i]); + } + else + warned = TRUE; + } + else if (item->search && !item->index) + { + warned = TRUE; + if (!is_slow_statement(i)) + warnings = svn_error_createf(SVN_ERR_TEST_FAILED, warnings, + "%s: " + "Query on %s doesn't use an index:\n%s", + wc_query_info[i][0], item->table, wc_queries[i]); + } + else if (item->scan && !is_result_table(item->table)) + { + warned = TRUE; + if (!is_slow_statement(i)) + warnings = svn_error_createf(SVN_ERR_TEST_FAILED, warnings, + "Query %s: " + "Performs scan on %s:\n%s", + wc_query_info[i][0], item->table, wc_queries[i]); + } + else if (item->create_btree) + { + warned = TRUE; + if (!is_slow_statement(i)) + warnings = svn_error_createf(SVN_ERR_TEST_FAILED, warnings, + "Query %s: Creates a temporary B-TREE:\n%s", + wc_query_info[i][0], wc_queries[i]); + } + } + SQLITE_ERR(sqlite3_reset(stmt)); + SQLITE_ERR(sqlite3_finalize(stmt)); + + if (!warned && is_slow_statement(i)) + { + printf("DBG: Expected %s to be reported as slow, but it wasn't\n", + wc_query_info[i][0]); + } + + if (rows && warned != is_slow_statement(i)) + { + int w; + svn_error_t *info = NULL; + for (w = rows->nelts-1; w >= 0; w--) + { + if (warned) + info = svn_error_createf(SVN_ERR_SQLITE_CONSTRAINT, info, + "|%s", APR_ARRAY_IDX(rows, w, + const char*)); + else + printf("|%s\n", APR_ARRAY_IDX(rows, w, const char*)); + } + + warnings = svn_error_compose_create(warnings, info); + } + } + SQLITE_ERR(sqlite3_close(sdb)); /* Close the DB if ok; otherwise leaked */ + + return warnings; +} + +/* Helper to verify a bit of data in the sqlite3 statistics */ +static int +parse_stat_data(const char *stat) +{ + int n = 0; + apr_int64_t last = APR_INT64_MAX; + while (*stat) + { + apr_int64_t v; + char *next; + + if (*stat < '0' || *stat > '9') + return -2; + + errno = 0; + v = apr_strtoi64(stat, &next, 10); + + /* All numbers specify the average number of rows + with the same values in all columns left of it, + so the value must be >= 1 and lower than or equal + to all previous seen numbers */ + if (v <= 0 || (v > last) || (errno != 0)) + return -1; + + last = v; + + n++; + stat = next; + + if (*stat == ' ') + stat++; + } + + return n; +} + +static svn_error_t * +test_schema_statistics(apr_pool_t *scratch_pool) +{ + sqlite3 *sdb; + sqlite3_stmt *stmt; + + SVN_ERR(create_memory_db(&sdb, scratch_pool)); + + SQLITE_ERR( + sqlite3_exec(sdb, + "CREATE TABLE shadow_stat1(tbl TEXT, idx TEXT, stat TEXT)", + NULL, NULL, NULL)); + + SQLITE_ERR( + sqlite3_exec(sdb, + "INSERT INTO shadow_stat1 (tbl, idx, stat) " + "SELECT tbl, idx, stat FROM sqlite_stat1", + NULL, NULL, NULL)); + + SQLITE_ERR( + sqlite3_exec(sdb, + "DROP TABLE sqlite_stat1", + NULL, NULL, NULL)); + + /* Insert statement to give index at least 1 record */ + SQLITE_ERR( + sqlite3_exec(sdb, + "INSERT INTO nodes (wc_id, local_relpath, op_depth," + " presence, kind) " + "VALUES (1, '', 0, 'normal', 'dir')", + NULL, NULL, NULL)); + + SQLITE_ERR( + sqlite3_exec(sdb, + "INSERT INTO actual_node (wc_id, local_relpath) " + "VALUES (1, '')", + NULL, NULL, NULL)); + + SQLITE_ERR( + sqlite3_exec(sdb, + "INSERT INTO lock (repos_id, repos_relpath, lock_token) " + "VALUES (1, '', '')", + NULL, NULL, NULL)); + + /* These are currently not necessary for query optimization, but it's better + to tell Sqlite how we intend to use this table anyway */ + SQLITE_ERR( + sqlite3_exec(sdb, + "INSERT INTO wc_lock (wc_id, local_dir_relpath) " + "VALUES (1, '')", + NULL, NULL, NULL)); + + SQLITE_ERR( + sqlite3_exec(sdb, + "INSERT INTO WORK_QUEUE (work) " + "VALUES ('')", + NULL, NULL, NULL)); + + SQLITE_ERR( + sqlite3_exec(sdb, + "ANALYZE", + NULL, NULL, NULL)); + + SQLITE_ERR( + sqlite3_prepare(sdb, "SELECT s.tbl, s.idx, s.stat, r.stat " + "FROM shadow_stat1 s " + "LEFT JOIN sqlite_stat1 r ON " + "s.tbl=r.tbl and s.idx=r.idx", + -1, &stmt, NULL)); + + while (sqlite3_step(stmt) == SQLITE_ROW) + { + const char *wc_stat = (const char*)sqlite3_column_text(stmt, 2); + const char *sqlite_stat = (const char*)sqlite3_column_text(stmt, 3); + + if (! sqlite_stat) + { + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Schema statistic failure:" + " Refering to unknown index '%s' on '%s'", + sqlite3_column_text(stmt, 1), + sqlite3_column_text(stmt, 0)); + } + + if (parse_stat_data(wc_stat) != parse_stat_data(sqlite_stat)) + { + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "Schema statistic failure:" + " Column mismatch for '%s' on '%s'", + sqlite3_column_text(stmt, 1), + sqlite3_column_text(stmt, 0)); + } + } + + SQLITE_ERR(sqlite3_reset(stmt)); + SQLITE_ERR(sqlite3_finalize(stmt)); + + SQLITE_ERR(sqlite3_close(sdb)); /* Close the DB if ok; otherwise leaked */ + + return SVN_NO_ERROR; +} + +struct svn_test_descriptor_t test_funcs[] = + { + SVN_TEST_NULL, + SVN_TEST_PASS2(test_sqlite_version, + "sqlite up-to-date"), + SVN_TEST_PASS2(test_parsable, + "queries are parsable"), + SVN_TEST_PASS2(test_query_expectations, + "test query expectations"), + SVN_TEST_PASS2(test_schema_statistics, + "test schema statistics"), + SVN_TEST_NULL + }; diff --git a/subversion/tests/libsvn_wc/wc-test.c b/subversion/tests/libsvn_wc/wc-test.c new file mode 100644 index 0000000..30eb18a --- /dev/null +++ b/subversion/tests/libsvn_wc/wc-test.c @@ -0,0 +1,323 @@ +/* + * wc-test.c : test WC APIs + * + * ==================================================================== + * 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 <apr_pools.h> +#include <apr_general.h> + +#include "svn_types.h" +#include "svn_io.h" +#include "svn_dirent_uri.h" +#include "svn_pools.h" +#include "svn_repos.h" +#include "svn_wc.h" +#include "svn_client.h" +#include "svn_hash.h" + +#include "utils.h" + +#include "private/svn_wc_private.h" +#include "private/svn_sqlite.h" +#include "private/svn_dep_compat.h" +#include "../../libsvn_wc/wc.h" +#include "../../libsvn_wc/wc_db.h" +#define SVN_WC__I_AM_WC_DB +#include "../../libsvn_wc/wc_db_private.h" + +#include "../svn_test.h" + +#ifdef _MSC_VER +#pragma warning(disable: 4221) /* nonstandard extension used */ +#endif + + +/* ---------------------------------------------------------------------- */ +/* The test functions */ + +/* Structure for testing node_get_base and node_get_origin. */ +struct base_origin_t +{ + /* Path to create and test, WC-relative */ + const char *path; + /* Expected base rev. "-1" means no base. (Expected base path + * == base_rev valid ? path : NULL) */ + svn_revnum_t base_rev; + /* Path to copy from, WC-relative */ + const char *src_path; + /* Expected "origin" */ + struct { + const char *path; + svn_revnum_t rev; + } origin; +}; + +/* Data for testing node_get_base and node_get_origin. */ +struct base_origin_t base_origin_subtests[] = + { + /* file copied onto nothing */ + { "A/C/copy1", -1, "iota", {"iota", 1} }, + + /* dir copied onto nothing */ + { "A/C/copy2", -1, "A/B/E", {"A/B/E", 1} }, + + /* replacement: file copied over a schedule-delete file */ + { "A/B/lambda", 1, "iota", {"iota", 1} }, + + /* replacement: dir copied over a schedule-delete dir */ + { "A/D/G", 1, "A/B/E", {"A/B/E", 1} }, + + /* replacement: dir copied over a schedule-delete file */ + { "A/D/gamma", 1, "A/B/E", {"A/B/E", 1} }, + + /* replacement: file copied over a schedule-delete dir */ + { "A/D/H", 1, "iota", {"iota", 1} }, + + { 0 } + }; + +/* Create a WC containing lots of different node states, in the sandbox B. */ +static svn_error_t * +create_wc_for_base_and_origin_tests(svn_test__sandbox_t *b) +{ + struct base_origin_t *copy; + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); + + /* Copy various things */ + for (copy = base_origin_subtests; copy->src_path; copy++) + { + if (SVN_IS_VALID_REVNUM(copy->base_rev)) + SVN_ERR(sbox_wc_delete(b, copy->path)); + SVN_ERR(sbox_wc_copy(b, copy->src_path, copy->path)); + } + + return SVN_NO_ERROR; +} + +/* Test svn_wc__node_get_base(). */ +static svn_error_t * +test_node_get_base(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + + SVN_ERR(svn_test__sandbox_create(b, "node_get_base", opts, pool)); + + SVN_ERR(create_wc_for_base_and_origin_tests(b)); + + { + struct base_origin_t *subtest; + + for (subtest = base_origin_subtests; subtest->path; subtest++) + { + const char *local_abspath + = svn_dirent_join(b->wc_abspath, subtest->path, b->pool); + svn_revnum_t revision; + const char *repos_relpath, *repos_root_url, *repos_uuid; + + SVN_ERR(svn_wc__node_get_base(NULL, &revision, &repos_relpath, + &repos_root_url, &repos_uuid, + NULL, + b->wc_ctx, local_abspath, + TRUE /* ignore_enoent */, + FALSE /* show_hidden */, + b->pool, b->pool)); + SVN_TEST_ASSERT(revision == subtest->base_rev); + if (SVN_IS_VALID_REVNUM(subtest->base_rev)) + { + SVN_TEST_STRING_ASSERT(repos_relpath, subtest->path); + SVN_TEST_STRING_ASSERT(repos_root_url, b->repos_url); + SVN_TEST_ASSERT(repos_uuid != NULL); + } + else + { + SVN_TEST_STRING_ASSERT(repos_relpath, NULL); + SVN_TEST_STRING_ASSERT(repos_root_url, NULL); + SVN_TEST_STRING_ASSERT(repos_uuid, NULL); + } + } + } + + return SVN_NO_ERROR; +} + +/* Test svn_wc__node_get_origin(). */ +static svn_error_t * +test_node_get_origin(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + + SVN_ERR(svn_test__sandbox_create(b, "node_get_origin", opts, pool)); + + SVN_ERR(create_wc_for_base_and_origin_tests(b)); + + { + struct base_origin_t *subtest; + + for (subtest = base_origin_subtests; subtest->path; subtest++) + { + const char *local_abspath + = svn_dirent_join(b->wc_abspath, subtest->path, b->pool); + svn_revnum_t revision; + const char *repos_relpath, *repos_root_url, *repos_uuid; + + SVN_ERR(svn_wc__node_get_origin(NULL, &revision, &repos_relpath, + &repos_root_url, &repos_uuid, NULL, + b->wc_ctx, local_abspath, FALSE, + b->pool, b->pool)); + SVN_TEST_ASSERT(revision == subtest->origin.rev); + if (SVN_IS_VALID_REVNUM(subtest->origin.rev)) + { + SVN_TEST_STRING_ASSERT(repos_relpath, subtest->origin.path); + SVN_TEST_STRING_ASSERT(repos_root_url, b->repos_url); + SVN_TEST_ASSERT(repos_uuid != NULL); + } + else + { + SVN_TEST_STRING_ASSERT(repos_relpath, NULL); + SVN_TEST_STRING_ASSERT(repos_root_url, NULL); + SVN_TEST_STRING_ASSERT(repos_uuid, NULL); + } + } + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_externals_parse(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + int i; + struct external_info + { + const char *line; + const char *url; + const char *local_path; + svn_revnum_t peg_rev; + svn_revnum_t rev; + + } items[] = { + { + "dir http://server/svn/a", + "http://server/svn/a", + "dir" + }, + { + "/svn/home dir", + "u://svr/svn/home", + "dir" + }, + { + "//server/home dir", + "u://server/home", + "dir" + }, + { + "../../../../home dir", + "u://svr/svn/home", + "dir", + }, + { + "^/../repB/tools/scripts scripts", + "u://svr/svn/cur/repB/tools/scripts", + "scripts" + }, + { + "^/../repB/tools/README.txt scripts/README.txt", + "u://svr/svn/cur/repB/tools/README.txt", + "scripts/README.txt" + }, + }; + + for (i = 0; i < sizeof(items) / sizeof(items[0]); i++) + { + apr_array_header_t *results; + svn_wc_external_item2_t *external_item; + const char *resolved_url; + SVN_ERR(svn_wc_parse_externals_description3(&results, "/my/current/dir", + items[i].line, FALSE, pool)); + + SVN_TEST_ASSERT(results && results->nelts == 1); + + external_item = APR_ARRAY_IDX(results, 0, svn_wc_external_item2_t *); + + SVN_ERR(svn_wc__resolve_relative_external_url(&resolved_url, + external_item, + "u://svr/svn/cur/dir", + "u://svr/svn/cur/dir/sd/fl", + pool, pool)); + + SVN_TEST_STRING_ASSERT(resolved_url, items[i].url); + SVN_TEST_STRING_ASSERT(external_item->target_dir, items[i].local_path); + + if (items[i].peg_rev != 0) + SVN_TEST_ASSERT(external_item->peg_revision.value.number + == items[i].peg_rev); + if (items[i].rev != 0) + SVN_TEST_ASSERT(external_item->revision.value.number == items[i].rev); + SVN_TEST_ASSERT(svn_uri_is_canonical(resolved_url, pool)); + } + + + return SVN_NO_ERROR; + +} + +static svn_error_t * +test_externals_parse_erratic(apr_pool_t *pool) +{ + svn_error_t *err; + apr_array_header_t *list = NULL; + + err = svn_wc_parse_externals_description3( + &list, "parent_dir", + "^/valid/but/should/not/be/on/record wc_target\n" + "because_this_is_an_error", + FALSE, pool); + + /* DESC above has an error, so expect one. */ + SVN_TEST_ASSERT(err != NULL); + svn_error_clear(err); + + /* svn_wc_parse_externals_description3() should not + touch LIST when DESC had an error.*/ + SVN_TEST_ASSERT(list == NULL); + + return SVN_NO_ERROR; +} + + +/* ---------------------------------------------------------------------- */ +/* The list of test functions */ + +struct svn_test_descriptor_t test_funcs[] = + { + SVN_TEST_NULL, + SVN_TEST_OPTS_PASS(test_node_get_base, + "test_node_get_base"), + SVN_TEST_OPTS_PASS(test_node_get_origin, + "test_node_get_origin"), + SVN_TEST_OPTS_PASS(test_externals_parse, + "test svn_wc_parse_externals_description3"), + SVN_TEST_PASS2(test_externals_parse_erratic, + "parse erratic externals definition"), + SVN_TEST_NULL + }; |