Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_client/conflicts-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_client/conflicts-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_client/conflicts-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_client/conflicts-test.c Fri Jan 14 14:01:45 2022 @@ -154,7 +154,7 @@ assert_text_conflict_options(svn_client_ return SVN_NO_ERROR; } -/* +/* * The following tests verify resolution of "incoming file add vs. * local file obstruction upon merge" tree conflicts. */ @@ -170,6 +170,7 @@ static const char *deleted_file_name = " static const char *deleted_dir_name = "B"; static const char *deleted_dir_child = "lambda"; static const char *new_dir_name = "newdir"; +static const char *unversioned_file_name = "unversioned.txt"; /* File property content. */ static const char *propval_trunk = "This is a property on the trunk."; @@ -177,6 +178,8 @@ static const char *propval_branch = "Thi static const char *propval_different = "This is a different property value."; /* File content. */ +static const char *new_file_content = + "This is a new file\n"; static const char *modified_file_content = "This is a modified file\n"; static const char *modified_file_on_branch_content = @@ -185,6 +188,8 @@ static const char *added_file_on_branch_ "This is a file added on the branch\n"; static const char *modified_file_in_working_copy_content = "This is a modified file in the working copy\n"; +static const char *unversioned_file_content = + "This is an unversioned file\n"; /* A helper function which prepares a working copy for the tests below. */ static svn_error_t * @@ -477,7 +482,7 @@ test_merge_incoming_added_file_replace_a return SVN_NO_ERROR; } -/* +/* * The following tests verify resolution of "incoming dir add vs. * local dir obstruction upon merge" tree conflicts. */ @@ -1357,7 +1362,7 @@ create_wc_with_incoming_delete_file_merg } else { - /* Commit modifcation and run a merge from the trunk to the branch. */ + /* Commit modification and run a merge from the trunk to the branch. */ SVN_ERR(sbox_wc_commit(b, "")); SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); /* This should raise an "incoming delete vs local edit" tree conflict. */ @@ -1448,7 +1453,7 @@ test_merge_incoming_delete_file_ignore(c SVN_TEST_ASSERT(!status->file_external); SVN_TEST_ASSERT(status->moved_from_abspath == NULL); SVN_TEST_ASSERT(status->moved_to_abspath == NULL); - + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path), ctx, b->pool, b->pool)); @@ -1801,7 +1806,7 @@ test_update_incoming_delete_file_ignore( SVN_TEST_ASSERT(!status->file_external); SVN_TEST_ASSERT(status->moved_from_abspath == NULL); SVN_TEST_ASSERT(status->moved_to_abspath == NULL); - + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path), ctx, b->pool, b->pool)); @@ -2111,7 +2116,7 @@ create_wc_with_incoming_delete_dir_confl if (local_add) { const char *new_child_path; - + new_child_path = svn_relpath_join(branch_path, svn_relpath_join(deleted_dir_name, new_file_name_branch, @@ -2621,6 +2626,7 @@ test_merge_incoming_delete_vs_local_dele svn_client_conflict_option_id_t expected_opts[] = { svn_client_conflict_option_postpone, svn_client_conflict_option_accept_current_wc_state, + svn_client_conflict_option_incoming_delete_ignore, svn_client_conflict_option_incoming_delete_accept, -1 /* end of list */ }; @@ -2634,6 +2640,7 @@ test_merge_incoming_delete_vs_local_dele svn_client_conflict_option_id_t expected_opts[] = { svn_client_conflict_option_postpone, svn_client_conflict_option_accept_current_wc_state, + svn_client_conflict_option_incoming_delete_ignore, svn_client_conflict_option_incoming_delete_accept, -1 /* end of list */ }; @@ -4120,7 +4127,7 @@ create_wc_with_dir_add_vs_dir_add_update SVN_ERR(svn_io_dir_make(sbox_wc_path(b, new_file_child_path), APR_OS_DEFAULT, b->pool)); - /* Update to the HEAD revision. + /* Update to the HEAD revision. * This should raise an "incoming add vs local add" tree conflict. */ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); @@ -5019,7 +5026,7 @@ test_cherry_pick_post_move_edit(const sv /* And "A1/mu" should have expected contents. */ SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A1/mu"), pool)); - SVN_TEST_STRING_ASSERT(buf->data, + SVN_TEST_STRING_ASSERT(buf->data, "<<<<<<< .working" "\n" "This is the file 'mu'." "\n" "||||||| .old" "\n" @@ -5119,7 +5126,7 @@ test_merge_incoming_move_dir_across_bran apr_array_header_t *possible_moved_to_abspaths; SVN_ERR(svn_test__sandbox_create(b, - "merge_incoming_move_dir accross branches", + "merge_incoming_move_dir across branches", opts, pool)); SVN_ERR(create_wc_with_incoming_delete_dir_conflict_across_branches(b)); @@ -6189,6 +6196,1269 @@ test_file_vs_dir_move_merge_assertion_fa return SVN_NO_ERROR; } +static svn_error_t * +test_update_file_add_vs_unversiond_file(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + svn_client_conflict_t *conflict; + svn_client_ctx_t *ctx; + struct status_baton sb; + struct svn_client_status_t *status; + svn_opt_revision_t opt_rev; + svn_stringbuf_t *buf; + + SVN_ERR(svn_test__sandbox_create(b, "update_file_add_vs_unversioned_file", + opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + /* Add a new file. */ + SVN_ERR(sbox_file_write(b, new_file_name, new_file_content)); + SVN_ERR(sbox_wc_add(b, new_file_name)); + SVN_ERR(sbox_wc_commit(b, "")); /* r2 */ + + SVN_ERR(sbox_wc_update(b, "", 1)); /* back to r1 */ + + /* Create an identical unversioned file. */ + SVN_ERR(sbox_file_write(b, new_file_name, new_file_content)); + SVN_ERR(sbox_wc_update(b, "", 2)); /* back to r2 */ + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_name), + ctx, b->pool, b->pool)); + SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) == + svn_wc_conflict_reason_unversioned); + SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) == + svn_wc_conflict_action_add); + { + svn_client_conflict_option_id_t expected_opts[] = { + svn_client_conflict_option_postpone, + svn_client_conflict_option_accept_current_wc_state, + svn_client_conflict_option_incoming_added_file_text_merge, + -1 /* end of list */ + }; + SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, + b->pool)); + } + + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_incoming_added_file_text_merge, + ctx, b->pool)); + + /* Ensure that the file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_file_name), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Ensure that the file has the expected content. */ + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, new_file_name), + b->pool)); + SVN_TEST_STRING_ASSERT(buf->data, new_file_content); + return SVN_NO_ERROR; +} + +static svn_error_t * +test_switch_file_add_vs_unversiond_file(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + svn_client_conflict_t *conflict; + svn_client_ctx_t *ctx; + struct status_baton sb; + struct svn_client_status_t *status; + svn_opt_revision_t opt_rev; + svn_stringbuf_t *buf; + svn_revnum_t result_rev; + const char *trunk_url; + const char *new_file_path; + + SVN_ERR(svn_test__sandbox_create(b, "switch_file_add_vs_unversioned_file", + opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + /* Create a branch of node "A". */ + SVN_ERR(sbox_wc_copy(b, trunk_path, branch_path)); + SVN_ERR(sbox_wc_commit(b, "")); /* r2 */ + + /* Add a new file on trunk. */ + new_file_path = svn_relpath_join(trunk_path, new_file_name, b->pool); + SVN_ERR(sbox_file_write(b, new_file_path, new_file_content)); + SVN_ERR(sbox_wc_add(b, new_file_path)); + SVN_ERR(sbox_wc_commit(b, "")); /* r3 */ + + SVN_ERR(sbox_wc_update(b, "", 2)); /* back to r2 */ + + /* Create an identical unversioned file on the branch. */ + new_file_path = svn_relpath_join(branch_path, new_file_name, b->pool); + SVN_ERR(sbox_file_write(b, new_file_path, new_file_content)); + + /* Switch branch to trunk. */ + trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, SVN_VA_NULL); + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + opt_rev.kind = svn_opt_revision_head; + opt_rev.value.number = SVN_INVALID_REVNUM; + SVN_ERR(svn_client_switch3(&result_rev, sbox_wc_path(b, branch_path), + trunk_url, &opt_rev, &opt_rev, + svn_depth_infinity, + TRUE, FALSE, FALSE, FALSE, ctx, b->pool)); + + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path), + ctx, b->pool, b->pool)); + SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) == + svn_wc_conflict_reason_unversioned); + SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) == + svn_wc_conflict_action_add); + { + svn_client_conflict_option_id_t expected_opts[] = { + svn_client_conflict_option_postpone, + svn_client_conflict_option_accept_current_wc_state, + svn_client_conflict_option_incoming_added_file_text_merge, + -1 /* end of list */ + }; + SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, + b->pool)); + } + + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_incoming_added_file_text_merge, + ctx, b->pool)); + + /* Ensure that the file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_file_path), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Ensure that the file has the expected content. */ + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, new_file_path), + b->pool)); + SVN_TEST_STRING_ASSERT(buf->data, new_file_content); + return SVN_NO_ERROR; +} + +static svn_error_t * +create_unversioned_dir(const char **new_file_path, + const char **unversioned_file_path, + const char *new_dir_path, + svn_test__sandbox_t *b, apr_pool_t *pool) +{ + apr_file_t *file; + apr_size_t content_len; + + /* Create an unversioned directory. */ + SVN_ERR(svn_io_dir_make(sbox_wc_path(b, new_dir_path), APR_OS_DEFAULT, + b->pool)); + + /* Create an unversioned file which will collide with a versioned file. */ + *new_file_path = svn_relpath_join(new_dir_path, new_file_name, b->pool); + SVN_ERR(svn_io_file_open(&file, sbox_wc_path(b, *new_file_path), + (APR_READ | APR_WRITE | APR_CREATE | APR_TRUNCATE), APR_OS_DEFAULT, + b->pool)); + content_len = strlen(unversioned_file_content); + SVN_ERR(svn_io_file_write(file, unversioned_file_content, &content_len, + b->pool)); + SVN_ERR(svn_io_file_close(file, b->pool)); + + /* Create another unversioned file at a different path. */ + *unversioned_file_path = svn_relpath_join(new_dir_path, unversioned_file_name, + b->pool); + SVN_ERR(svn_io_file_open(&file, sbox_wc_path(b, *unversioned_file_path), + (APR_READ | APR_WRITE | APR_CREATE | APR_TRUNCATE), APR_OS_DEFAULT, + b->pool)); + content_len = strlen(unversioned_file_content); + SVN_ERR(svn_io_file_write(file, unversioned_file_content, &content_len, + b->pool)); + SVN_ERR(svn_io_file_close(file, b->pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +resolve_added_dir_vs_unversioned_dir(const char *new_dir_path, + const char *new_file_path, + const char *unversioned_file_path, + svn_test__sandbox_t *b, apr_pool_t *pool) +{ + svn_client_ctx_t *ctx; + svn_client_conflict_t *conflict; + svn_opt_revision_t opt_rev; + struct status_baton sb; + struct svn_client_status_t *status; + svn_stringbuf_t *buf; + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path), + ctx, b->pool, b->pool)); + SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) == + svn_wc_conflict_reason_unversioned); + SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) == + svn_wc_conflict_action_add); + { + svn_client_conflict_option_id_t expected_opts[] = { + svn_client_conflict_option_postpone, + svn_client_conflict_option_accept_current_wc_state, + svn_client_conflict_option_incoming_added_dir_merge, + -1 /* end of list */ + }; + SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, + b->pool)); + } + + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_incoming_added_dir_merge, + ctx, b->pool)); + + /* Ensure that the directory has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_dir_path), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_dir); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Ensure that the "collision" file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_file_path), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_modified); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Ensure that the file has the expected content. */ + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, new_file_path), + b->pool)); + SVN_TEST_STRING_ASSERT(buf->data, unversioned_file_content); + + /* Ensure that the unversioned file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_TEST_ASSERT_ERROR( + svn_client_status6(NULL, ctx, sbox_wc_path(b, unversioned_file_path), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool), + SVN_ERR_ENTRY_NOT_FOUND); + + /* Ensure that the file has the expected content. */ + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, unversioned_file_path), + b->pool)); + SVN_TEST_STRING_ASSERT(buf->data, unversioned_file_content); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_update_dir_add_vs_unversioned_dir(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + const char *new_dir_path; + const char *new_file_path; + const char *unversioned_file_path; + + SVN_ERR(svn_test__sandbox_create(b, "update_dir_add_vs_unversioned_dir", + opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + /* Add a new directory */ + new_dir_path = svn_relpath_join(trunk_path, new_dir_name, b->pool); + SVN_ERR(sbox_wc_mkdir(b, new_dir_path)); + new_file_path = svn_relpath_join(new_dir_path, new_file_name, b->pool); + SVN_ERR(sbox_file_write(b, new_file_path, new_file_content)); + SVN_ERR(sbox_wc_add(b, new_file_path)); + SVN_ERR(sbox_wc_commit(b, "")); /* r2 */ + + SVN_ERR(sbox_wc_update(b, "", 1)); /* back to r1 */ + + new_dir_path = svn_relpath_join(trunk_path, new_dir_name, b->pool); + SVN_ERR(create_unversioned_dir(&new_file_path, &unversioned_file_path, + new_dir_path, b, b->pool)); + + SVN_ERR(sbox_wc_update(b, "", 2)); /* back to r2 */ + + SVN_ERR(resolve_added_dir_vs_unversioned_dir(new_dir_path, new_file_path, + unversioned_file_path, + b, b->pool)); + return SVN_NO_ERROR; +} + +static svn_error_t * +test_switch_dir_add_vs_unversioned_dir(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + svn_client_ctx_t *ctx; + svn_opt_revision_t opt_rev; + svn_revnum_t result_rev; + const char *trunk_url; + const char *new_dir_path; + const char *new_file_path; + const char *unversioned_file_path; + + SVN_ERR(svn_test__sandbox_create(b, "switch_dir_add_vs_unversioned_dir", + opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + /* Create a branch of node "A". */ + SVN_ERR(sbox_wc_copy(b, trunk_path, branch_path)); + SVN_ERR(sbox_wc_commit(b, "")); /* r2 */ + + /* Add a new directory on trunk. */ + new_dir_path = svn_relpath_join(trunk_path, new_dir_name, b->pool); + SVN_ERR(sbox_wc_mkdir(b, new_dir_path)); + new_file_path = svn_relpath_join(new_dir_path, new_file_name, b->pool); + SVN_ERR(sbox_file_write(b, new_file_path, new_file_content)); + SVN_ERR(sbox_wc_add(b, new_file_path)); + SVN_ERR(sbox_wc_commit(b, "")); /* r3 */ + + SVN_ERR(sbox_wc_update(b, "", 2)); /* back to r2 */ + + new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool); + SVN_ERR(create_unversioned_dir(&new_file_path, &unversioned_file_path, + new_dir_path, b, b->pool)); + + /* Switch branch to trunk. */ + trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, SVN_VA_NULL); + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + opt_rev.kind = svn_opt_revision_head; + opt_rev.value.number = SVN_INVALID_REVNUM; + SVN_ERR(svn_client_switch3(&result_rev, sbox_wc_path(b, branch_path), + trunk_url, &opt_rev, &opt_rev, + svn_depth_infinity, + TRUE, FALSE, FALSE, FALSE, ctx, b->pool)); + + SVN_ERR(resolve_added_dir_vs_unversioned_dir(new_dir_path, new_file_path, + unversioned_file_path, + b, b->pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +create_file_move_vs_file_move_merge_conflict(svn_client_conflict_t **conflict, + svn_boolean_t edit_file, + svn_test__sandbox_t *b, + svn_client_ctx_t *ctx) +{ + svn_opt_revision_t opt_rev; + const char *branch_url; + + /* Create a branch of node "A". */ + SVN_ERR(sbox_wc_copy(b, "A", "A2")); + SVN_ERR(sbox_wc_commit(b, "")); /* r2 */ + + /* Move a file on trunk. */ + SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved")); + SVN_ERR(sbox_wc_commit(b, "")); /* r3 */ + + /* Move the same file to a different location on the branch. */ + SVN_ERR(sbox_wc_move(b, "A2/mu", "A2/mu-also-moved")); + SVN_ERR(sbox_wc_commit(b, "")); + if (edit_file) + { + /* Edit moved a file on the branch. */ + SVN_ERR(sbox_file_write(b, "A2/mu-also-moved", modified_file_content)); + SVN_ERR(sbox_wc_commit(b, "")); + } + + /* Merge branch to trunk. */ + SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); + branch_url = apr_pstrcat(b->pool, b->repos_url, "/A2", SVN_VA_NULL); + opt_rev.kind = svn_opt_revision_head; + opt_rev.value.number = SVN_INVALID_REVNUM; + SVN_ERR(svn_client_merge_peg5(branch_url, NULL, &opt_rev, + sbox_wc_path(b, "A"), + svn_depth_infinity, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + NULL, ctx, b->pool)); + + SVN_ERR(svn_client_conflict_get(conflict, sbox_wc_path(b, "A/mu"), + ctx, b->pool, b->pool)); + { + svn_client_conflict_option_id_t expected_opts[] = { + svn_client_conflict_option_postpone, + svn_client_conflict_option_accept_current_wc_state, + svn_client_conflict_option_incoming_delete_ignore, + svn_client_conflict_option_incoming_delete_accept, + -1 /* end of list */ + }; + SVN_ERR(assert_tree_conflict_options(*conflict, ctx, expected_opts, + b->pool)); + } + + SVN_ERR(svn_client_conflict_tree_get_details(*conflict, ctx, b->pool)); + { + svn_client_conflict_option_id_t expected_opts[] = { + svn_client_conflict_option_postpone, + svn_client_conflict_option_accept_current_wc_state, + svn_client_conflict_option_both_moved_file_merge, + svn_client_conflict_option_both_moved_file_move_merge, + -1 /* end of list */ + }; + SVN_ERR(assert_tree_conflict_options(*conflict, ctx, expected_opts, + b->pool)); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_merge_file_move_vs_file_move(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + svn_client_ctx_t *ctx; + svn_client_conflict_t *conflict; + svn_opt_revision_t opt_rev; + struct status_baton sb; + struct svn_client_status_t *status; + + SVN_ERR(svn_test__sandbox_create(b, "merge_file_move_vs_file_move", + opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(create_file_move_vs_file_move_merge_conflict(&conflict, + FALSE, b, ctx)); + + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_both_moved_file_merge, + ctx, b->pool)); + + /* The node "A/mu" should no longer exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get( + &conflict, sbox_wc_path(b, "A/mu"), ctx, pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* The node "A/mu-also-moved" should not exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get( + &conflict, sbox_wc_path(b, "A/mu-also-moved"), ctx, + pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* Ensure that the merged file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-moved"), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_merge_file_move_vs_file_move_accept_move(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + svn_client_ctx_t *ctx; + svn_client_conflict_t *conflict; + svn_opt_revision_t opt_rev; + struct status_baton sb; + struct svn_client_status_t *status; + + SVN_ERR(svn_test__sandbox_create(b, + "merge_file_move_vs_file_move_accept_move", + opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(create_file_move_vs_file_move_merge_conflict(&conflict, + FALSE, b, ctx)); + + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_both_moved_file_move_merge, + ctx, b->pool)); + + /* The node "A/mu" should no longer exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict, + sbox_wc_path(b, "A/mu"), + ctx, pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* The node "A/mu-moved" should be moved to "A/mu-also-moved". */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-moved"), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_deleted); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_STRING_ASSERT(status->moved_to_abspath, + sbox_wc_path(b, "A/mu-also-moved")); + + + /* Ensure that the merged file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-also-moved"), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_added); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_STRING_ASSERT(status->moved_from_abspath, + sbox_wc_path(b, "A/mu-moved")); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_merge_file_edit_move_vs_file_move(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + svn_client_ctx_t *ctx; + svn_client_conflict_t *conflict; + svn_opt_revision_t opt_rev; + struct status_baton sb; + struct svn_client_status_t *status; + svn_stringbuf_t *buf; + + SVN_ERR(svn_test__sandbox_create(b, "merge_file_edit_move_vs_file_move", + opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(create_file_move_vs_file_move_merge_conflict(&conflict, + TRUE, b, ctx)); + + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_both_moved_file_merge, + ctx, b->pool)); + + /* The node "A/mu" should no longer exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get( + &conflict, sbox_wc_path(b, "A/mu"), ctx, pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* The node "A/mu-also-moved" should not exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get( + &conflict, sbox_wc_path(b, "A/mu-also-moved"), ctx, + pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* Ensure that the merged file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-moved"), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_modified); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Make sure the file has the expected content. */ + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-moved"), pool)); + SVN_TEST_STRING_ASSERT(buf->data, modified_file_content); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_merge_file_edit_move_vs_file_move_accept_move(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + svn_client_ctx_t *ctx; + svn_client_conflict_t *conflict; + svn_opt_revision_t opt_rev; + struct status_baton sb; + struct svn_client_status_t *status; + svn_stringbuf_t *buf; + + SVN_ERR(svn_test__sandbox_create( + b, "merge_file_edit_move_vs_file_move_accept_move", opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(create_file_move_vs_file_move_merge_conflict(&conflict, + TRUE, b, ctx)); + + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_both_moved_file_move_merge, + ctx, b->pool)); + + /* The node "A/mu" should no longer exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict, + sbox_wc_path(b, "A/mu"), + ctx, pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* The node "A/mu-moved" should be moved to "A/mu-also-moved". */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-moved"), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_deleted); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_STRING_ASSERT(status->moved_to_abspath, + sbox_wc_path(b, "A/mu-also-moved")); + + + /* Ensure that the merged file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-also-moved"), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_added); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_STRING_ASSERT(status->moved_from_abspath, + sbox_wc_path(b, "A/mu-moved")); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Make sure the file has the expected content. */ + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-also-moved"), + pool)); + SVN_TEST_STRING_ASSERT(buf->data, modified_file_content); + + return SVN_NO_ERROR; +} + +static svn_error_t * +create_dir_move_vs_dir_move_merge_conflict(svn_client_conflict_t **conflict, + svn_test__sandbox_t *b, + svn_client_ctx_t *ctx) +{ + svn_opt_revision_t opt_rev; + const char *branch_url; + apr_array_header_t *options; + svn_client_conflict_option_t *option; + apr_array_header_t *possible_moved_to_abspaths; + + /* Create a branch of node "A". */ + SVN_ERR(sbox_wc_copy(b, "A", "A2")); + SVN_ERR(sbox_wc_commit(b, "")); /* r2 */ + + /* Move a directory on trunk. */ + SVN_ERR(sbox_wc_move(b, "A/B", "A/B-moved")); + SVN_ERR(sbox_wc_commit(b, "")); /* r3 */ + + /* Edit a file in the moved directory on trunk. */ + SVN_ERR(sbox_file_write(b, "A/B-moved/E/alpha", + modified_file_content)); + SVN_ERR(sbox_wc_commit(b, "")); + + /* Move the same directory to a different location on the branch. */ + SVN_ERR(sbox_wc_move(b, "A2/B", "A2/B-also-moved")); + SVN_ERR(sbox_wc_commit(b, "")); + + /* Edit a file in the moved directory on the branch. */ + SVN_ERR(sbox_file_write(b, "A2/B-also-moved/lambda", + modified_file_content)); + SVN_ERR(sbox_wc_commit(b, "")); + + /* Merge branch to trunk. */ + SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); + branch_url = apr_pstrcat(b->pool, b->repos_url, "/A2", SVN_VA_NULL); + opt_rev.kind = svn_opt_revision_head; + opt_rev.value.number = SVN_INVALID_REVNUM; + SVN_ERR(svn_client_merge_peg5(branch_url, NULL, &opt_rev, + sbox_wc_path(b, "A"), + svn_depth_infinity, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + NULL, ctx, b->pool)); + + SVN_ERR(svn_client_conflict_get(conflict, sbox_wc_path(b, "A/B"), + ctx, b->pool, b->pool)); + { + svn_client_conflict_option_id_t expected_opts[] = { + svn_client_conflict_option_postpone, + svn_client_conflict_option_accept_current_wc_state, + svn_client_conflict_option_incoming_delete_ignore, + svn_client_conflict_option_incoming_delete_accept, + -1 /* end of list */ + }; + SVN_ERR(assert_tree_conflict_options(*conflict, ctx, expected_opts, + b->pool)); + } + + SVN_ERR(svn_client_conflict_tree_get_details(*conflict, ctx, b->pool)); + { + svn_client_conflict_option_id_t expected_opts[] = { + svn_client_conflict_option_postpone, + svn_client_conflict_option_accept_current_wc_state, + svn_client_conflict_option_both_moved_dir_merge, + svn_client_conflict_option_both_moved_dir_move_merge, + -1 /* end of list */ + }; + SVN_ERR(assert_tree_conflict_options(*conflict, ctx, expected_opts, + b->pool)); + } + + SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options, *conflict, + ctx, b->pool, + b->pool)); + option = svn_client_conflict_option_find_by_id( + options, svn_client_conflict_option_both_moved_dir_merge); + SVN_TEST_ASSERT(option != NULL); + SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates( + &possible_moved_to_abspaths, option, b->pool, b->pool)); + + /* The resolver finds two possible move destinations because both + * branches are checked out into the same working copy. + * + * Possible working copy destinations for moved-away 'A/B' are: + * (1): 'A/B-also-moved + * (2): 'A2/B-also-moved + * Only one destination can be a move; the others are copies. + */ + SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 2); + SVN_TEST_STRING_ASSERT( + APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *), + sbox_wc_path(b, "A/B-also-moved")); + SVN_TEST_STRING_ASSERT( + APR_ARRAY_IDX(possible_moved_to_abspaths, 1, const char *), + sbox_wc_path(b, "A2/B-also-moved")); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_merge_dir_move_vs_dir_move(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + svn_client_ctx_t *ctx; + svn_client_conflict_t *conflict; + svn_opt_revision_t opt_rev; + struct status_baton sb; + struct svn_client_status_t *status; + svn_stringbuf_t *buf; + + SVN_ERR(svn_test__sandbox_create(b, "merge_dir_move_vs_dir_move", + opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(create_dir_move_vs_dir_move_merge_conflict(&conflict, b, ctx)); + + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_both_moved_dir_merge, + ctx, b->pool)); + + /* The node "A/B" should not exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict, + sbox_wc_path(b, "A/B"), + ctx, pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* The node "A/B-also-moved" should not exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get( + &conflict, + sbox_wc_path(b, "A/B-also-moved"), ctx, pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* Ensure that the merged directory has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/B-moved"), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_dir); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Make sure the edited files have the expected content. */ + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/B-moved/lambda"), + pool)); + SVN_TEST_STRING_ASSERT(buf->data, modified_file_content); + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/B-moved/E/alpha"), + pool)); + SVN_TEST_STRING_ASSERT(buf->data, modified_file_content); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_merge_dir_move_vs_dir_move_accept_move(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + svn_client_ctx_t *ctx; + svn_client_conflict_t *conflict; + svn_opt_revision_t opt_rev; + struct status_baton sb; + struct svn_client_status_t *status; + svn_stringbuf_t *buf; + + SVN_ERR(svn_test__sandbox_create(b, "merge_dir_move_vs_dir_move_accept_move", + opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(create_dir_move_vs_dir_move_merge_conflict(&conflict, b, ctx)); + + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_both_moved_dir_move_merge, + ctx, b->pool)); + + /* The node "A/B" should not exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict, + sbox_wc_path(b, "A/B"), + ctx, pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* The node "A/B-moved" should be moved to A/B-also-moved. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/B-moved"), + &opt_rev, svn_depth_empty, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_dir); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_deleted); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_STRING_ASSERT(status->moved_to_abspath, + sbox_wc_path(b, "A/B-also-moved")); + + /* Ensure that the merged directory has the expected status. */ + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/B-also-moved"), + &opt_rev, svn_depth_empty, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_dir); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_added); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_STRING_ASSERT(status->moved_from_abspath, + sbox_wc_path(b, "A/B-moved")); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Make sure the edited files have the expected content. */ + SVN_ERR(svn_stringbuf_from_file2(&buf, + sbox_wc_path(b, "A/B-also-moved/lambda"), + pool)); + SVN_TEST_STRING_ASSERT(buf->data, modified_file_content); + SVN_ERR(svn_stringbuf_from_file2(&buf, + sbox_wc_path(b, "A/B-also-moved/E/alpha"), + pool)); + SVN_TEST_STRING_ASSERT(buf->data, modified_file_content); + + return SVN_NO_ERROR; +} + +static svn_error_t * +create_file_move_vs_file_move_update_conflict(svn_client_conflict_t **conflict, + svn_test__sandbox_t *b, + svn_client_ctx_t *ctx) +{ + apr_array_header_t *options; + svn_client_conflict_option_t *option; + apr_array_header_t *possible_moved_to_abspaths; + + /* Move a file. */ + SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved")); + + /* Edit moved file. */ + SVN_ERR(sbox_file_write(b, "A/mu-moved", modified_file_content)); + SVN_ERR(sbox_wc_commit(b, "")); + + /* Update back to r1, */ + SVN_ERR(sbox_wc_update(b, "", 1)); /* r2 */ + + /* Copy the file to test handling of ambiguous moves. */ + SVN_ERR(sbox_wc_copy(b, "A/mu", "A/mu-copied")); + + /* Move the same file to a different location. */ + SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-also-moved")); + + /* Edit moved file. */ + SVN_ERR(sbox_file_write(b, "A/mu-also-moved", + modified_file_in_working_copy_content)); + + /* Update to r2. */ + /* This should raise an "incoming delete vs local delete" tree conflict. */ + SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); + + SVN_ERR(svn_client_conflict_get(conflict, sbox_wc_path(b, "A/mu"), + ctx, b->pool, b->pool)); + { + svn_client_conflict_option_id_t expected_opts[] = { + svn_client_conflict_option_postpone, + svn_client_conflict_option_accept_current_wc_state, + svn_client_conflict_option_incoming_delete_ignore, + svn_client_conflict_option_incoming_delete_accept, + -1 /* end of list */ + }; + SVN_ERR(assert_tree_conflict_options(*conflict, ctx, expected_opts, + b->pool)); + } + + SVN_ERR(svn_client_conflict_tree_get_details(*conflict, ctx, b->pool)); + { + svn_client_conflict_option_id_t expected_opts[] = { + svn_client_conflict_option_postpone, + svn_client_conflict_option_accept_current_wc_state, + svn_client_conflict_option_both_moved_file_merge, + svn_client_conflict_option_both_moved_file_move_merge, + -1 /* end of list */ + }; + SVN_ERR(assert_tree_conflict_options(*conflict, ctx, expected_opts, + b->pool)); + } + + /* Check possible move destinations for the file. */ + SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options, *conflict, + ctx, b->pool, + b->pool)); + option = svn_client_conflict_option_find_by_id( + options, svn_client_conflict_option_both_moved_file_merge); + SVN_TEST_ASSERT(option != NULL); + + SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates( + &possible_moved_to_abspaths, option, b->pool, b->pool)); + + /* The resolver finds two possible destinations for the moved file: + * + * Possible working copy destinations for moved-away 'A/mu' are: + * (1): 'A/mu-also-moved' + * (2): 'A/mu-copied' + * Only one destination can be a move; the others are copies. + */ + SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 2); + SVN_TEST_STRING_ASSERT( + APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *), + sbox_wc_path(b, "A/mu-also-moved")); + SVN_TEST_STRING_ASSERT( + APR_ARRAY_IDX(possible_moved_to_abspaths, 1, const char *), + sbox_wc_path(b, "A/mu-copied")); + + return SVN_NO_ERROR; +} + + +static svn_error_t * +test_update_file_move_vs_file_move(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + svn_client_ctx_t *ctx; + svn_client_conflict_t *conflict; + svn_opt_revision_t opt_rev; + struct status_baton sb; + struct svn_client_status_t *status; + svn_stringbuf_t *buf; + svn_node_kind_t kind; + char *conflicted_content; + + SVN_ERR(svn_test__sandbox_create(b, "update_file_move_vs_file_move", + opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(create_file_move_vs_file_move_update_conflict(&conflict, b, ctx)); + + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_both_moved_file_merge, + ctx, b->pool)); + + /* The node "A/mu" should no longer exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get( + &conflict, sbox_wc_path(b, "A/mu"), ctx, pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* The node "A/mu-moved" should now have moved to "A/mu-also-moved. */ + SVN_ERR(svn_io_check_path(sbox_wc_path(b, "A/mu"), &kind, b->pool)); + SVN_TEST_ASSERT(kind == svn_node_none); + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-moved"), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(!status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_deleted); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_STRING_ASSERT(status->moved_to_abspath, + sbox_wc_path(b, "A/mu-also-moved")); + + /* Ensure that the merged file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-also-moved"), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_conflicted); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_STRING_ASSERT(status->moved_from_abspath, + sbox_wc_path(b, "A/mu-moved")); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Ensure that the moved+merged file has the expected content. */ + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-also-moved"), + b->pool)); + conflicted_content = apr_psprintf(b->pool, + "<<<<<<< .working\n" + "%s" + "||||||| .old\n" + "This is the file 'mu'.\n" + "=======\n" + "%s" + ">>>>>>> .new\n", + modified_file_in_working_copy_content, + modified_file_content); + SVN_TEST_STRING_ASSERT(buf->data, conflicted_content); + + return SVN_NO_ERROR; +} + +/* Same test case as above, but accept the incoming move. */ +static svn_error_t * +test_update_file_move_vs_file_move_accept_move(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + svn_client_ctx_t *ctx; + svn_client_conflict_t *conflict; + svn_opt_revision_t opt_rev; + struct status_baton sb; + struct svn_client_status_t *status; + svn_stringbuf_t *buf; + char *conflicted_content; + + SVN_ERR(svn_test__sandbox_create(b, + "update_file_move_vs_file_move_accept_move", + opts, pool)); + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */ + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(create_file_move_vs_file_move_update_conflict(&conflict, b, ctx)); + + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_both_moved_file_move_merge, + ctx, b->pool)); + + /* The node "A/mu" should no longer exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get( + &conflict, sbox_wc_path(b, "A/mu"), ctx, pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* The node "A/mu-also-moved" should not exist. */ + SVN_TEST_ASSERT_ERROR(svn_client_conflict_get( + &conflict, sbox_wc_path(b, "A/mu-also-moved"), ctx, + pool, pool), + SVN_ERR_WC_PATH_NOT_FOUND); + + /* Ensure that the merged file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-moved"), + &opt_rev, svn_depth_unknown, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, NULL, + status_func, &sb, b->pool)); + status = sb.status; + SVN_TEST_ASSERT(status->kind == svn_node_file); + SVN_TEST_ASSERT(status->versioned); + SVN_TEST_ASSERT(status->conflicted); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_conflicted); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none); + SVN_TEST_ASSERT(!status->copied); + SVN_TEST_ASSERT(!status->switched); + SVN_TEST_ASSERT(!status->file_external); + SVN_TEST_ASSERT(status->moved_from_abspath == NULL); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Ensure that the moved+merged file has the expected content. */ + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-moved"), + b->pool)); + conflicted_content = apr_psprintf(b->pool, + "<<<<<<< .working\n" /* ### labels need fixing */ + "%s" + "||||||| .old\n" + "This is the file 'mu'.\n" + "=======\n" + "%s" + ">>>>>>> .new\n", + modified_file_content, + modified_file_in_working_copy_content); + SVN_TEST_STRING_ASSERT(buf->data, conflicted_content); + + return SVN_NO_ERROR; +} + + /* ========================================================================== */ @@ -6293,6 +7563,30 @@ static struct svn_test_descriptor_t test "local missing conflict with ambiguous dir moves"), SVN_TEST_OPTS_PASS(test_file_vs_dir_move_merge_assertion_failure, "file v dir move merge assertion failure"), + SVN_TEST_OPTS_PASS(test_update_file_add_vs_unversiond_file, + "file add vs unversioned file during update"), + SVN_TEST_OPTS_PASS(test_switch_file_add_vs_unversiond_file, + "file add vs unversioned file during switch"), + SVN_TEST_OPTS_PASS(test_update_dir_add_vs_unversioned_dir, + "dir add vs unversioned dir during update"), + SVN_TEST_OPTS_PASS(test_switch_dir_add_vs_unversioned_dir, + "dir add vs unversioned dir during switch"), + SVN_TEST_OPTS_PASS(test_merge_file_move_vs_file_move, + "file move vs file move during merge"), + SVN_TEST_OPTS_PASS(test_merge_file_move_vs_file_move_accept_move, + "file move vs file move during merge accept move"), + SVN_TEST_OPTS_PASS(test_merge_file_edit_move_vs_file_move, + "file move vs file edit-move during merge"), + SVN_TEST_OPTS_PASS(test_merge_file_edit_move_vs_file_move_accept_move, + "file edit-move vs file move merge accept move"), + SVN_TEST_OPTS_PASS(test_merge_dir_move_vs_dir_move, + "dir move vs dir move during merge"), + SVN_TEST_OPTS_PASS(test_merge_dir_move_vs_dir_move_accept_move, + "dir move vs dir move during merge accept move"), + SVN_TEST_OPTS_PASS(test_update_file_move_vs_file_move, + "file move vs file move during update"), + SVN_TEST_OPTS_PASS(test_update_file_move_vs_file_move_accept_move, + "file move vs file move during update accept move"), SVN_TEST_NULL };
Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_client/mtcc-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_client/mtcc-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_client/mtcc-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_client/mtcc-test.c Fri Jan 14 14:01:45 2022 @@ -693,7 +693,7 @@ test_file_revs_both_ways(const svn_test_ subpool)); SVN_TEST_ASSERT(hrb.last == 6); - /* Ressurect mu */ + /* Resurrect mu */ svn_pool_clear(subpool); SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 7, ctx, subpool, subpool)); SVN_ERR(svn_client__mtcc_add_copy("mu", 6, "mu", mtcc, subpool)); Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_diff/diff-diff3-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_diff/diff-diff3-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_diff/diff-diff3-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_diff/diff-diff3-test.c Fri Jan 14 14:01:45 2022 @@ -28,6 +28,8 @@ #include "svn_pools.h" #include "svn_utf.h" +#include "private/svn_string_private.h" + /* Used to terminate lines in large multi-line string literals. */ #define NL APR_EOL_STR @@ -2071,15 +2073,13 @@ test_three_way_merge_conflict_styles(apr } -#define MAKE_STRING(cstr) { (cstr), sizeof((cstr))-1 } - static svn_error_t * test_diff4(apr_pool_t *pool) { svn_diff_t *diff; svn_stream_t *actual, *expected; svn_boolean_t same; - static svn_string_t B2 = MAKE_STRING( + static svn_string_t B2 = SVN__STATIC_STRING( "int main (int argc, char **argv)\n" "{\n" " /* line minus-five of context */\n" @@ -2094,7 +2094,7 @@ test_diff4(apr_pool_t *pool) " /* line plus-four of context */\n" " /* line plus-five of context */\n" "}\n"); - static svn_string_t B2new = MAKE_STRING( + static svn_string_t B2new = SVN__STATIC_STRING( "int main (int argc, char **argv)\n" "{\n" " /* line minus-five of context */\n" @@ -2109,7 +2109,7 @@ test_diff4(apr_pool_t *pool) " /* line plus-four of context */\n" " /* line plus-five of context */\n" "}\n"); - static svn_string_t T1 = MAKE_STRING( + static svn_string_t T1 = SVN__STATIC_STRING( "int main (int argc, char **argv)\n" "{\n" " /* line minus-five of context */\n" @@ -2124,7 +2124,7 @@ test_diff4(apr_pool_t *pool) " /* line plus-four of context */\n" " /* line plus-five of context */\n" "}\n"); - static svn_string_t T2 = MAKE_STRING( + static svn_string_t T2 = SVN__STATIC_STRING( "#include <stdio.h>\n" "\n" "int main (int argc, char **argv)\n" @@ -2141,7 +2141,7 @@ test_diff4(apr_pool_t *pool) " /* line plus-four of context */\n" " /* line plus-five of context */\n" "}\n"); - static svn_string_t T3 = MAKE_STRING( + static svn_string_t T3 = SVN__STATIC_STRING( "#include <stdio.h>\n" "\n" "int main (int argc, char **argv)\n" Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_fs/fs-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_fs/fs-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_fs/fs-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_fs/fs-test.c Fri Jan 14 14:01:45 2022 @@ -5479,7 +5479,7 @@ commit_timestamp(const svn_test_opts_t * APR_HASH_KEY_STRING); SVN_TEST_ASSERT(!svn_date); - /* Commit that overwites a missing svn:date. */ + /* Commit that overwrites a missing svn:date. */ SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, pool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); SVN_ERR(svn_fs_make_dir(txn_root, "/zig", pool)); @@ -7219,7 +7219,7 @@ test_cache_clear_during_stream(const svn * Just to be sure, make it not too uniform to keep self-txdelta at bay. */ SVN_ERR(svn_fs_apply_textdelta(&consumer_func, &consumer_baton, txn_root, "/foo", NULL, NULL, subpool)); - stream = svn_txdelta_target_push(consumer_func, consumer_baton, + stream = svn_txdelta_target_push(consumer_func, consumer_baton, svn_stream_empty(subpool), subpool); for (i = 0; i < 10000; ++ i) { @@ -7369,6 +7369,106 @@ closest_copy_test_svn_4677(const svn_tes return SVN_NO_ERROR; } +static svn_error_t * +test_closest_copy_file_replaced_with_dir(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_fs_t *fs; + svn_fs_txn_t *txn; + svn_fs_root_t *txn_root; + svn_fs_root_t *rev_root; + svn_revnum_t youngest_rev; + svn_fs_root_t *copy_root; + const char *copy_path; + + /* Prepare a filesystem. */ + SVN_ERR(svn_test__create_fs(&fs, "test-closest-copy-file-replaced-with-dir", + opts, pool)); + + youngest_rev = 0; + + /* Modeled after the case described in the thread: + "[PATCH] A test for "Can't get entries" error" + https://lists.apache.org/thread.html/693a95b0da834387e78a7f08df2392b634397d32f37428c81c02f8c5@%3Cdev.subversion.apache.org%3E + */ + /* r1: Add a directory with a file. */ + SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_fs_make_dir(txn_root, "/A", pool)); + SVN_ERR(svn_fs_make_file(txn_root, "/A/mu", pool)); + SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool)); + SVN_TEST_INT_ASSERT(youngest_rev, 1); + + /* r2: Copy the directory. */ + SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_fs_revision_root(&rev_root, fs, 1, pool)); + SVN_ERR(svn_fs_copy(rev_root, "/A", txn_root, "/B", pool)); + SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool)); + SVN_TEST_INT_ASSERT(youngest_rev, 2); + + /* r3: Delete the file. */ + SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_fs_delete(txn_root, "/B/mu", pool)); + SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool)); + SVN_TEST_INT_ASSERT(youngest_rev, 3); + + /* r4: Replace the file with a new directory containing a file. */ + SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_fs_make_dir(txn_root, "/B/mu", pool)); + SVN_ERR(svn_fs_make_file(txn_root, "/B/mu/iota", pool)); + SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool)); + SVN_TEST_INT_ASSERT(youngest_rev, 4); + + /* Test a couple of svn_fs_closest_copy() calls; the second call used + to fail with an unexpected SVN_ERR_FS_NOT_DIRECTORY error. */ + + SVN_ERR(svn_fs_revision_root(&rev_root, fs, 2, pool)); + SVN_ERR(svn_fs_closest_copy(©_root, ©_path, rev_root, "/B/mu", pool)); + + SVN_TEST_ASSERT(copy_root != NULL); + SVN_TEST_INT_ASSERT(svn_fs_revision_root_revision(copy_root), 2); + SVN_TEST_STRING_ASSERT(copy_path, "/B"); + + SVN_ERR(svn_fs_revision_root(&rev_root, fs, 4, pool)); + SVN_ERR(svn_fs_closest_copy(©_root, ©_path, rev_root, "/B/mu/iota", pool)); + + SVN_TEST_ASSERT(copy_root == NULL); + SVN_TEST_ASSERT(copy_path == NULL); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_unrecognized_ioctl(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_fs_t *fs; + svn_error_t *err; + svn_fs_ioctl_code_t code = {0}; + + SVN_ERR(svn_test__create_fs(&fs, "test-unrecognized-ioctl", opts, pool)); + + code.fs_type = "NON-EXISTING"; + code.code = 98765; + err = svn_fs_ioctl(fs, code, NULL, NULL, NULL, NULL, pool, pool); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE); + + code.fs_type = "NON-EXISTING"; + code.code = 98765; + err = svn_fs_ioctl(NULL, code, NULL, NULL, NULL, NULL, pool, pool); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNKNOWN_FS_TYPE); + + code.fs_type = opts->fs_type; + code.code = 98765; + err = svn_fs_ioctl(NULL, code, NULL, NULL, NULL, NULL, pool, pool); + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE); + + return SVN_NO_ERROR; +} + /* ------------------------------------------------------------------------ */ /* The test table. */ @@ -7513,6 +7613,10 @@ static struct svn_test_descriptor_t test "test rep-sharing on content rather than SHA1"), SVN_TEST_OPTS_PASS(closest_copy_test_svn_4677, "test issue SVN-4677 regression"), + SVN_TEST_OPTS_PASS(test_closest_copy_file_replaced_with_dir, + "svn_fs_closest_copy after replacing file with dir"), + SVN_TEST_OPTS_PASS(test_unrecognized_ioctl, + "test svn_fs_ioctl with unrecognized code"), SVN_TEST_NULL }; Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_fs/locks-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_fs/locks-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_fs/locks-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_fs/locks-test.c Fri Jan 14 14:01:45 2022 @@ -1089,6 +1089,9 @@ lock_cb_error(const svn_test_opts_t *opt return SVN_NO_ERROR; } +/* XXX NOTE: + This test will fail on most Unix-like systems when run as the + root user, because flock() will ignore file permissions. */ static svn_error_t * obtain_write_lock_failure(const svn_test_opts_t *opts, apr_pool_t *pool) Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c Fri Jan 14 14:01:45 2022 @@ -59,7 +59,8 @@ static const char * get_rev_contents(svn_revnum_t rev, apr_pool_t *pool) { /* Toss in a bunch of magic numbers for spice. */ - apr_int64_t num = ((rev * 1234353 + 4358) * 4583 + ((rev % 4) << 1)) / 42; + apr_int64_t rev64 = rev; + apr_int64_t num = ((rev64 * 1234353 + 4358) * 4583 + ((rev64 % 4) << 1)) / 42; return apr_psprintf(pool, "%" APR_INT64_T_FMT "\n", num); } @@ -1693,7 +1694,7 @@ compare_0_length_rep(const svn_test_opts enum { COUNT = 5 }; const char *file_names[COUNT] = { no_rep_file, - empty_plain_file, + empty_plain_file, plain_file, empty_delta_file, delta_file }; Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c Fri Jan 14 14:01:45 2022 @@ -35,6 +35,8 @@ #include "private/svn_subr_private.h" #include "../../libsvn_fs_fs/index.h" +#include "../../libsvn_fs_fs/rep-cache.h" +#include "../../libsvn_fs/fs-loader.h" #include "../svn_test_fs.h" @@ -199,8 +201,10 @@ get_repo_stats(const svn_test_opts_t *op svn_repos_t *repos; svn_revnum_t rev; apr_size_t i; - svn_fs_fs__stats_t *stats; svn_fs_fs__extension_info_t *extension_info; + svn_fs_fs__ioctl_get_stats_input_t input = {0}; + svn_fs_fs__ioctl_get_stats_output_t *output; + const svn_fs_fs__stats_t *stats; /* Bail (with success) on known-untestable scenarios */ if (strcmp(opts->fs_type, "fsfs") != 0) @@ -211,8 +215,9 @@ get_repo_stats(const svn_test_opts_t *op SVN_ERR(create_greek_repo(&repos, &rev, opts, REPO_NAME, pool, pool)); /* Gather statistics info on that repo. */ - SVN_ERR(svn_fs_fs__get_stats(&stats, svn_repos_fs(repos), NULL, NULL, - NULL, NULL, pool, pool)); + SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_GET_STATS, + &input, (void**)&output, NULL, NULL, pool, pool)); + stats = output->stats; /* Check that the stats make sense. */ SVN_TEST_ASSERT(stats->total_size > 1000 && stats->total_size < 10000); @@ -324,6 +329,7 @@ dump_index(const svn_test_opts_t *opts, svn_repos_t *repos; svn_revnum_t rev; dump_baton_t baton; + svn_fs_fs__ioctl_dump_index_input_t input = {0}; /* Bail (with success) on known-untestable scenarios */ if (strcmp(opts->fs_type, "fsfs") != 0) @@ -342,8 +348,12 @@ dump_index(const svn_test_opts_t *opts, baton.offset = 0; baton.revision = rev; baton.numbers_seen = svn_bit_array__create(100, pool); - SVN_ERR(svn_fs_fs__dump_index(svn_repos_fs(repos), rev, dump_index_entry, - &baton, NULL, NULL, pool)); + + input.revision = rev; + input.callback_func = dump_index_entry; + input.callback_baton = &baton; + SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_DUMP_INDEX, + &input, NULL, NULL, NULL, pool, pool)); /* Check that we've got all data (20 noderevs + 20 reps + 1 changes list). */ SVN_TEST_ASSERT(baton.invocations == 41); @@ -377,6 +387,8 @@ load_index(const svn_test_opts_t *opts, apr_array_header_t *entries = apr_array_make(pool, 41, sizeof(void *)); apr_array_header_t *alt_entries = apr_array_make(pool, 1, sizeof(void *)); svn_fs_fs__p2l_entry_t entry; + svn_fs_fs__ioctl_dump_index_input_t dump_input = {0}; + svn_fs_fs__ioctl_load_index_input_t load_input = {0}; /* Bail (with success) on known-untestable scenarios */ if (strcmp(opts->fs_type, "fsfs") != 0) @@ -391,8 +403,11 @@ load_index(const svn_test_opts_t *opts, SVN_ERR(create_greek_repo(&repos, &rev, opts, REPO_NAME, pool, pool)); /* Read the original index contents for REV in ENTRIES. */ - SVN_ERR(svn_fs_fs__dump_index(svn_repos_fs(repos), rev, receive_index, - entries, NULL, NULL, pool)); + dump_input.revision = rev; + dump_input.callback_func = receive_index; + dump_input.callback_baton = entries; + SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_DUMP_INDEX, + &dump_input, NULL, NULL, NULL, pool, pool)); /* Replace it with an index that declares the whole revision contents as * "unused". */ @@ -404,14 +419,21 @@ load_index(const svn_test_opts_t *opts, entry.item.revision = SVN_INVALID_REVNUM; APR_ARRAY_PUSH(alt_entries, svn_fs_fs__p2l_entry_t *) = &entry; - SVN_ERR(svn_fs_fs__load_index(svn_repos_fs(repos), rev, alt_entries, pool)); + load_input.revision = rev; + load_input.entries = alt_entries; + SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_LOAD_INDEX, + &load_input, NULL, NULL, NULL, pool, pool)); + SVN_TEST_ASSERT_ERROR(svn_repos_verify_fs3(repos, rev, rev, FALSE, FALSE, NULL, NULL, NULL, NULL, NULL, NULL, pool), SVN_ERR_FS_INDEX_CORRUPTION); /* Restore the original index. */ - SVN_ERR(svn_fs_fs__load_index(svn_repos_fs(repos), rev, entries, pool)); + load_input.revision = rev; + load_input.entries = entries; + SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_LOAD_INDEX, + &load_input, NULL, NULL, NULL, pool, pool)); SVN_ERR(svn_repos_verify_fs3(repos, rev, rev, FALSE, FALSE, NULL, NULL, NULL, NULL, NULL, NULL, pool)); @@ -420,6 +442,63 @@ load_index(const svn_test_opts_t *opts, #undef REPO_NAME +/* ------------------------------------------------------------------------ */ + +static svn_error_t * +build_rep_cache(const svn_test_opts_t *opts, apr_pool_t *pool) +{ + svn_fs_t *fs; + fs_fs_data_t *ffd; + svn_fs_txn_t *txn; + svn_fs_root_t *txn_root; + svn_revnum_t rev; + svn_boolean_t exists; + const char *fs_path; + svn_fs_fs__ioctl_build_rep_cache_input_t input = {0}; + + /* Bail (with success) on known-untestable scenarios */ + if (strcmp(opts->fs_type, "fsfs") != 0) + return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, + "this will test FSFS repositories only"); + + if (opts->server_minor_version && (opts->server_minor_version < 6)) + return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, + "pre-1.6 SVN doesn't support FSFS rep-sharing"); + + /* Create a filesystem and explicitly disable rep-sharing. */ + fs_path = "test-repo-build-rep-cache-test"; + SVN_ERR(svn_test__create_fs2(&fs, fs_path, opts, NULL, pool)); + ffd = fs->fsap_data; + ffd->rep_sharing_allowed = FALSE; + + /* Add the Greek tree. */ + SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_test__create_greek_tree(txn_root, pool)); + SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool)); + SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev)); + + /* Make sure the rep-cache does not exist. */ + SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, fs, pool)); + SVN_TEST_ASSERT(!exists); + + /* Build and verify the rep-cache. */ + ffd->rep_sharing_allowed = TRUE; + + input.start_rev = rev; + input.end_rev = rev; + SVN_ERR(svn_fs_ioctl(fs, SVN_FS_FS__IOCTL_BUILD_REP_CACHE, + &input, NULL, NULL, NULL, pool, pool)); + + SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, fs, pool)); + SVN_TEST_ASSERT(exists); + + SVN_ERR(svn_fs_verify(fs_path, NULL, 0, SVN_INVALID_REVNUM, + NULL, NULL, NULL, NULL, pool)); + + return SVN_NO_ERROR; +} + /* The test table. */ @@ -435,6 +514,8 @@ static struct svn_test_descriptor_t test "dump the P2L index"), SVN_TEST_OPTS_PASS(load_index, "load the P2L index"), + SVN_TEST_OPTS_PASS(build_rep_cache, + "build the representation cache"), SVN_TEST_NULL }; Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_x/fs-x-pack-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_x/fs-x-pack-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_x/fs-x-pack-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_fs_x/fs-x-pack-test.c Fri Jan 14 14:01:45 2022 @@ -680,7 +680,7 @@ recover_fully_packed(const svn_test_opts /* ------------------------------------------------------------------------ */ /* Regression test for issue #4320 (fsfs file-hinting fails when reading a rep - from the transaction that is commiting rev = SHARD_SIZE). */ + from the transaction that is committing rev = SHARD_SIZE). */ #define REPO_NAME "test-repo-file-hint-at-shard-boundary" #define SHARD_SIZE 4 #define MAX_REV (SHARD_SIZE - 1) Propchange: subversion/branches/multi-wc-format/subversion/tests/libsvn_ra/ ------------------------------------------------------------------------------ --- svn:ignore (original) +++ svn:ignore Fri Jan 14 14:01:45 2022 @@ -1,4 +1,13 @@ *.lo .libs +base_revision_above_youngest +commit_cb_failure +commit_empty_last_change +delete_revision_above_youngest +errors_from_callbacks ra-test +ra_list_has_props +ra_revision_errors +test-get-dir test-repo-* +test-run_checkout Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_ra/ra-test.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_ra/ra-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/libsvn_ra/ra-test.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/libsvn_ra/ra-test.c Fri Jan 14 14:01:45 2022 @@ -62,7 +62,8 @@ make_and_open_repos(svn_ra_session_t **s pool, pool)); SVN_ERR(svn_ra_initialize(pool)); - SVN_ERR(svn_ra_open4(session, NULL, url, NULL, cbtable, NULL, NULL, pool)); + SVN_ERR(svn_ra_open5(session, NULL, NULL, url, NULL, cbtable, NULL, NULL, + pool)); return SVN_NO_ERROR; } @@ -94,6 +95,41 @@ commit_changes(svn_ra_session_t *session return SVN_NO_ERROR; } +/* Commit two revisions: add 'B', then delete 'A' */ +static svn_error_t * +commit_two_changes(svn_ra_session_t *session, + apr_pool_t *pool) +{ + apr_hash_t *revprop_table = apr_hash_make(pool); + const svn_delta_editor_t *editor; + void *edit_baton; + void *root_baton, *dir_baton; + + /* mkdir B */ + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + revprop_table, + NULL, NULL, NULL, TRUE, pool)); + SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, + pool, &root_baton)); + SVN_ERR(editor->add_directory("B", root_baton, NULL, SVN_INVALID_REVNUM, + pool, &dir_baton)); + SVN_ERR(editor->close_directory(dir_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + + /* delete A */ + SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, + revprop_table, + NULL, NULL, NULL, TRUE, pool)); + SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, + pool, &root_baton)); + SVN_ERR(editor->delete_entry("A", SVN_INVALID_REVNUM, root_baton, pool)); + SVN_ERR(editor->close_directory(root_baton, pool)); + SVN_ERR(editor->close_edit(edit_baton, pool)); + + return SVN_NO_ERROR; +} + static svn_error_t * commit_tree(svn_ra_session_t *session, apr_pool_t *pool) @@ -352,7 +388,7 @@ check_tunnel_callback_test(const svn_tes NULL, NULL, NULL, pool)); b->last_check = TRUE; - SVN_TEST_ASSERT_ERROR(svn_ra_open4(&session, NULL, + SVN_TEST_ASSERT_ERROR(svn_ra_open5(&session, NULL, NULL, "svn+foo://localhost/no-repo", NULL, cbtable, NULL, NULL, pool), SVN_ERR_RA_CANNOT_CREATE_SESSION); @@ -395,7 +431,7 @@ tunnel_callback_test(const svn_test_opts NULL, NULL, NULL, pool)); b->last_check = FALSE; - SVN_ERR(svn_ra_open4(&session, NULL, url, NULL, cbtable, NULL, NULL, + SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, NULL, NULL, scratch_pool)); SVN_TEST_ASSERT(b->last_check); SVN_TEST_ASSERT(b->open_count > 0); @@ -1556,7 +1592,7 @@ tunnel_run_checkout(const svn_test_opts_ b->last_check = FALSE; - SVN_ERR(svn_ra_open4(&session, NULL, url, NULL, cbtable, NULL, NULL, + SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, NULL, NULL, scratch_pool)); SVN_ERR(commit_changes(session, pool)); @@ -1626,16 +1662,16 @@ commit_empty_last_change(const svn_test_ SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, revprop_table, NULL, NULL, NULL, TRUE, tmp_pool)); - + SVN_ERR(editor->open_root(edit_baton, 1, tmp_pool, &root_baton)); SVN_ERR(editor->close_directory(root_baton, tmp_pool)); SVN_ERR(editor->close_edit(edit_baton, tmp_pool)); - + SVN_ERR(svn_ra_stat(session, "", 2+i, &dirent, tmp_pool)); - + SVN_TEST_ASSERT(dirent != NULL); SVN_TEST_STRING_ASSERT(dirent->last_author, "jrandom"); - + /* BDB used to only updates last_changed on the repos_root when there was an actual change. Now all filesystems behave in the same way */ SVN_TEST_INT_ASSERT(dirent->created_rev, 2+i); @@ -1698,7 +1734,7 @@ commit_locked_file(const svn_test_opts_t SVN_ERR(svn_ra_create_callbacks(&cbtable, pool)); SVN_ERR(svn_test__init_auth_baton(&cbtable->auth_baton, pool)); - SVN_ERR(svn_ra_open4(&session, NULL, url, NULL, cbtable, + SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, NULL, NULL, pool)); SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton, apr_hash_make(pool), @@ -1731,7 +1767,7 @@ commit_locked_file(const svn_test_opts_t } /* Open a new session using the file parent's URL. */ - SVN_ERR(svn_ra_open4(&session, NULL, url, NULL, cbtable, + SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, NULL, NULL, pool)); /* Create a new commit editor supplying our lock token. */ @@ -1784,6 +1820,63 @@ commit_locked_file(const svn_test_opts_t return SVN_NO_ERROR; } +/* Cases of 'get-deleted-rev' that should return SVN_INVALID_REVNUM. */ +static svn_error_t * +test_get_deleted_rev_no_delete(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_ra_session_t *ra_session; + svn_revnum_t revision_deleted; + + SVN_ERR(make_and_open_repos(&ra_session, + "test-repo-get-deleted-rev-no-delete", opts, + pool)); + SVN_ERR(commit_changes(ra_session, pool)); + SVN_ERR(commit_two_changes(ra_session, pool)); + + /* expect 'no deletion' in the range up to r2, when it is deleted in r3 */ + /* This was failing over RA-SVN where the 'get-deleted-rev' wire command's + prototype cannot directly represent that result. A new enough client and + server collaborate on a work-around implemented using an error code. */ + SVN_ERR(svn_ra_get_deleted_rev(ra_session, "A", 1, 2, + &revision_deleted, pool)); + SVN_TEST_INT_ASSERT(revision_deleted, SVN_INVALID_REVNUM); + + /* this connection should still be open: a simple case should still work */ + SVN_ERR(svn_ra_get_deleted_rev(ra_session, "A", 1, 3, + &revision_deleted, pool)); + SVN_TEST_INT_ASSERT(revision_deleted, 3); + + return SVN_NO_ERROR; +} + +/* Cases of 'get-deleted-rev' that should return an error. */ +static svn_error_t * +test_get_deleted_rev_errors(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_ra_session_t *ra_session; + svn_revnum_t revision_deleted; + svn_error_t *err; + + SVN_ERR(make_and_open_repos(&ra_session, + "test-repo-get-deleted-rev-errors", opts, pool)); + SVN_ERR(commit_changes(ra_session, pool)); + + /* expect an error when searching up to r3, when repository head is r1 */ + err = svn_ra_get_deleted_rev(ra_session, "A", 1, 3, &revision_deleted, pool); + + /* mod_dav_svn returns a generic error code for "500 Internal Server Error"; + * the other RA layers return the specific error code for "no such revision". + * We should make these consistent, but for now that's how it is. */ + if (opts->repos_url && strncmp(opts->repos_url, "http", 4) == 0) + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_RA_DAV_REQUEST_FAILED); + else + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_NO_SUCH_REVISION); + + return SVN_NO_ERROR; +} + /* The test table. */ @@ -1820,6 +1913,10 @@ static struct svn_test_descriptor_t test "check how last change applies to empty commit"), SVN_TEST_OPTS_PASS(commit_locked_file, "check commit editor for a locked file"), + SVN_TEST_OPTS_PASS(test_get_deleted_rev_no_delete, + "test get-deleted-rev no delete"), + SVN_TEST_OPTS_PASS(test_get_deleted_rev_errors, + "test get-deleted-rev errors"), SVN_TEST_NULL };
