Modified: subversion/branches/authzperf/subversion/tests/libsvn_client/conflicts-test.c URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/tests/libsvn_client/conflicts-test.c?rev=1764707&r1=1764706&r2=1764707&view=diff ============================================================================== --- subversion/branches/authzperf/subversion/tests/libsvn_client/conflicts-test.c (original) +++ subversion/branches/authzperf/subversion/tests/libsvn_client/conflicts-test.c Thu Oct 13 15:25:15 2016 @@ -52,6 +52,107 @@ status_func(void *baton, const char *pat return SVN_NO_ERROR; } +struct info_baton +{ + svn_client_info2_t *info; + apr_pool_t *result_pool; +}; + +/* Implements svn_client_info_receiver2_t */ +static svn_error_t * +info_func(void *baton, const char *abspath_or_url, + const svn_client_info2_t *info, + apr_pool_t *scratch_pool) +{ + struct info_baton *ib = baton; + + ib->info = svn_client_info2_dup(info, ib->result_pool); + + return SVN_NO_ERROR; +} + +/* A helper function which checks offered conflict resolution options. */ +static svn_error_t * +assert_conflict_options(const apr_array_header_t *actual, + const svn_client_conflict_option_id_t *expected, + apr_pool_t *pool) +{ + svn_stringbuf_t *actual_str = svn_stringbuf_create_empty(pool); + svn_stringbuf_t *expected_str = svn_stringbuf_create_empty(pool); + int i; + + for (i = 0; i < actual->nelts; i++) + { + svn_client_conflict_option_t *opt; + svn_client_conflict_option_id_t id; + + opt = APR_ARRAY_IDX(actual, i, svn_client_conflict_option_t *); + + if (i > 0) + svn_stringbuf_appendcstr(actual_str, ", "); + + id = svn_client_conflict_option_get_id(opt); + svn_stringbuf_appendcstr(actual_str, apr_itoa(pool, id)); + } + + for (i = 0; expected[i] >= 0; i++) + { + if (i > 0) + svn_stringbuf_appendcstr(expected_str, ", "); + + svn_stringbuf_appendcstr(expected_str, apr_itoa(pool, expected[i])); + } + + SVN_TEST_STRING_ASSERT(actual_str->data, expected_str->data); + + return SVN_NO_ERROR; +} + +static svn_error_t * +assert_tree_conflict_options(svn_client_conflict_t *conflict, + svn_client_ctx_t *ctx, + const svn_client_conflict_option_id_t *expected, + apr_pool_t *pool) +{ + apr_array_header_t *actual; + + SVN_ERR(svn_client_conflict_tree_get_resolution_options(&actual, conflict, + ctx, pool, pool)); + SVN_ERR(assert_conflict_options(actual, expected, pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +assert_prop_conflict_options(svn_client_conflict_t *conflict, + svn_client_ctx_t *ctx, + const svn_client_conflict_option_id_t *expected, + apr_pool_t *pool) +{ + apr_array_header_t *actual; + + SVN_ERR(svn_client_conflict_prop_get_resolution_options(&actual, conflict, + ctx, pool, pool)); + SVN_ERR(assert_conflict_options(actual, expected, pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +assert_text_conflict_options(svn_client_conflict_t *conflict, + svn_client_ctx_t *ctx, + const svn_client_conflict_option_id_t *expected, + apr_pool_t *pool) +{ + apr_array_header_t *actual; + + SVN_ERR(svn_client_conflict_text_get_resolution_options(&actual, conflict, + ctx, pool, pool)); + SVN_ERR(assert_conflict_options(actual, expected, pool)); + + return SVN_NO_ERROR; +} + /* * The following tests verify resolution of "incoming file add vs. * local file obstruction upon merge" tree conflicts. @@ -61,15 +162,28 @@ status_func(void *baton, const char *pat static const char *trunk_path = "A"; static const char *branch_path = "A_branch"; static const char *new_file_name = "newfile.txt"; +static const char *new_file_name_branch = "newfile-on-branch.txt"; static const char *deleted_file_name = "mu"; +static const char *deleted_dir_name = "B"; +static const char *deleted_dir_child = "lambda"; +static const char *new_dir_name = "newdir"; /* File property content. */ static const char *propval_trunk = "This is a property on the trunk."; static const char *propval_branch = "This is a property on the branch."; +/* File content. */ +static const char *modified_file_on_branch_content = + "This is a modified file on the branch\n"; +static const char *added_file_on_branch_content = + "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"; + /* A helper function which prepares a working copy for the tests below. */ static svn_error_t * -create_wc_with_file_add_vs_file_add_merge_conflict(svn_test__sandbox_t *b) +create_wc_with_file_add_vs_file_add_merge_conflict(svn_test__sandbox_t *b, + svn_boolean_t do_switch) { static const char *new_file_path; svn_client_ctx_t *ctx; @@ -102,25 +216,42 @@ create_wc_with_file_add_vs_file_add_merg "This is a new file on the branch\n")); SVN_ERR(sbox_wc_add(b, new_file_path)); SVN_ERR(sbox_wc_propset(b, "prop", propval_branch, new_file_path)); - SVN_ERR(sbox_wc_commit(b, "")); - /* Run a merge from the trunk to the branch. */ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); - SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); - trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, SVN_VA_NULL); - opt_rev.kind = svn_opt_revision_head; opt_rev.value.number = SVN_INVALID_REVNUM; - /* This should raise an "incoming add vs local obstruction" tree conflict. */ - SVN_ERR(svn_client_merge_peg5(trunk_url, NULL, &opt_rev, - sbox_wc_path(b, branch_path), - svn_depth_infinity, - FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, - NULL, ctx, b->pool)); + trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, SVN_VA_NULL); + + if (do_switch) + { + svn_revnum_t result_rev; + + /* This should raise an "incoming add vs local add" conflict. */ + SVN_ERR(svn_client_switch3(&result_rev, sbox_wc_path(b, branch_path), + trunk_url, &opt_rev, &opt_rev, + svn_depth_infinity, TRUE, TRUE, FALSE, FALSE, + ctx, b->pool)); + + opt_rev.kind = svn_opt_revision_head; + } + else + { + SVN_ERR(sbox_wc_commit(b, "")); + SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); + + /* Run a merge from the trunk to the branch. + * This should raise an "incoming add vs local obstruction" conflict. */ + SVN_ERR(svn_client_merge_peg5(trunk_url, NULL, &opt_rev, + sbox_wc_path(b, branch_path), + svn_depth_infinity, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + NULL, ctx, b->pool)); + + opt_rev.kind = svn_opt_revision_working; + } /* 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, @@ -130,9 +261,18 @@ create_wc_with_file_add_vs_file_add_merg 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_normal); + if (do_switch) + { + SVN_TEST_ASSERT(status->node_status == svn_wc_status_replaced); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified); + } + else + { + 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_normal); + } SVN_TEST_ASSERT(!status->copied); SVN_TEST_ASSERT(!status->switched); SVN_TEST_ASSERT(!status->file_external); @@ -146,8 +286,12 @@ create_wc_with_file_add_vs_file_add_merg SVN_ERR(svn_client_conflict_get_conflicted(NULL, NULL, &tree_conflicted, conflict, b->pool, b->pool)); SVN_TEST_ASSERT(tree_conflicted); - SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) == - svn_wc_conflict_reason_obstructed); + if (do_switch) + SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) == + svn_wc_conflict_reason_added); + else + SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) == + svn_wc_conflict_reason_obstructed); SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) == svn_wc_conflict_action_add); @@ -155,81 +299,8 @@ create_wc_with_file_add_vs_file_add_merg } static svn_error_t * -test_option_merge_incoming_added_file_ignore(const svn_test_opts_t *opts, - apr_pool_t *pool) -{ - svn_client_ctx_t *ctx; - svn_client_conflict_t *conflict; - const char *new_file_path; - svn_boolean_t text_conflicted; - apr_array_header_t *props_conflicted; - svn_boolean_t tree_conflicted; - struct status_baton sb; - struct svn_client_status_t *status; - svn_opt_revision_t opt_rev; - const svn_string_t *propval; - - svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); - - SVN_ERR(svn_test__sandbox_create(b, "incoming_added_file_ignore", - opts, pool)); - - SVN_ERR(create_wc_with_file_add_vs_file_add_merge_conflict(b)); - - /* Resolve the tree conflict. */ - SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); - new_file_path = svn_relpath_join(branch_path, new_file_name, b->pool); - SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path), - ctx, b->pool, b->pool)); - SVN_ERR(svn_client_conflict_tree_resolve_by_id( - conflict, - svn_client_conflict_option_merge_incoming_add_ignore, - 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_normal); - 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); - - SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path), - ctx, b->pool, b->pool)); - - /* The file should not be in conflict. */ - SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted, - &props_conflicted, - &tree_conflicted, - conflict, b->pool, b->pool)); - SVN_TEST_ASSERT(!text_conflicted && - props_conflicted->nelts == 0 && - !tree_conflicted); - - /* Verify the merged property value. */ - SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx, - sbox_wc_path(b, new_file_path), - "prop", b->pool, b->pool)); - SVN_TEST_STRING_ASSERT(propval->data, propval_branch); - - return SVN_NO_ERROR; -} - -static svn_error_t * -test_option_merge_incoming_added_file_text_merge(const svn_test_opts_t *opts, - apr_pool_t *pool) +test_merge_incoming_added_file_text_merge(const svn_test_opts_t *opts, + apr_pool_t *pool) { svn_client_ctx_t *ctx; svn_client_conflict_t *conflict; @@ -244,10 +315,10 @@ test_option_merge_incoming_added_file_te svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); - SVN_ERR(svn_test__sandbox_create(b, "incoming_added_file_text_merge", + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_added_file_text_merge", opts, pool)); - SVN_ERR(create_wc_with_file_add_vs_file_add_merge_conflict(b)); + SVN_ERR(create_wc_with_file_add_vs_file_add_merge_conflict(b, FALSE)); /* Resolve the tree conflict. */ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); @@ -256,8 +327,8 @@ test_option_merge_incoming_added_file_te ctx, b->pool, b->pool)); SVN_ERR(svn_client_conflict_tree_resolve_by_id( conflict, - svn_client_conflict_option_merge_incoming_added_file_text_merge, - b->pool)); + 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; @@ -302,8 +373,8 @@ test_option_merge_incoming_added_file_te } static svn_error_t * -test_option_merge_incoming_added_file_replace(const svn_test_opts_t *opts, - apr_pool_t *pool) +test_merge_incoming_added_file_replace_and_merge(const svn_test_opts_t *opts, + apr_pool_t *pool) { svn_client_ctx_t *ctx; svn_client_conflict_t *conflict; @@ -318,20 +389,21 @@ test_option_merge_incoming_added_file_re svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); - SVN_ERR(svn_test__sandbox_create(b, "incoming_added_file_replace", - opts, pool)); + SVN_ERR(svn_test__sandbox_create( + b, "merge_incoming_added_file_replace_and_merge", opts, pool)); - SVN_ERR(create_wc_with_file_add_vs_file_add_merge_conflict(b)); + SVN_ERR(create_wc_with_file_add_vs_file_add_merge_conflict(b, FALSE)); /* Resolve the tree conflict. */ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); new_file_path = svn_relpath_join(branch_path, new_file_name, b->pool); SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path), ctx, b->pool, b->pool)); - SVN_ERR(svn_client_conflict_tree_resolve_by_id( - conflict, - svn_client_conflict_option_merge_incoming_added_file_replace, - b->pool)); + SVN_ERR( + svn_client_conflict_tree_resolve_by_id( + conflict, + svn_client_conflict_option_incoming_added_file_replace_and_merge, + ctx, b->pool)); /* Ensure that the file has the expected status. */ opt_rev.kind = svn_opt_revision_working; @@ -343,9 +415,10 @@ test_option_merge_incoming_added_file_re 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_replaced); - SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); + 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); + /* ### Shouldn't there be a property conflict? The trunk wins. */ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_normal); SVN_TEST_ASSERT(status->copied); SVN_TEST_ASSERT(!status->switched); @@ -356,12 +429,12 @@ test_option_merge_incoming_added_file_re SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path), ctx, b->pool, b->pool)); - /* The file should not be in conflict. */ + /* We should have a text conflict instead of a tree conflict. */ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted, &props_conflicted, &tree_conflicted, conflict, b->pool, b->pool)); - SVN_TEST_ASSERT(!text_conflicted && + SVN_TEST_ASSERT(text_conflicted && props_conflicted->nelts == 0 && !tree_conflicted); @@ -374,42 +447,48 @@ test_option_merge_incoming_added_file_re return SVN_NO_ERROR; } +/* A helper function which prepares a working copy for the tests below. */ static svn_error_t * -test_option_merge_incoming_added_file_replace_and_merge( - const svn_test_opts_t *opts, - apr_pool_t *pool) +create_wc_with_file_add_vs_file_add_update_conflict(svn_test__sandbox_t *b) { + static const char *new_file_path; svn_client_ctx_t *ctx; + svn_opt_revision_t opt_rev; + svn_client_status_t *status; + struct status_baton sb; svn_client_conflict_t *conflict; - const char *new_file_path; - svn_boolean_t text_conflicted; - apr_array_header_t *props_conflicted; svn_boolean_t tree_conflicted; - struct status_baton sb; - struct svn_client_status_t *status; - svn_opt_revision_t opt_rev; - const svn_string_t *propval; - svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); + SVN_ERR(sbox_add_and_commit_greek_tree(b)); - SVN_ERR(svn_test__sandbox_create(b, "incoming_added_file_replace_and_merge", - opts, pool)); + /* Add and commit a new file. */ + new_file_path = svn_relpath_join(trunk_path, new_file_name, b->pool); + SVN_ERR(sbox_file_write(b, new_file_path, + "This is a new file on the trunk\n")); + SVN_ERR(sbox_wc_add(b, new_file_path)); + SVN_ERR(sbox_wc_propset(b, "prop", propval_trunk, new_file_path)); + SVN_ERR(sbox_wc_commit(b, "")); - SVN_ERR(create_wc_with_file_add_vs_file_add_merge_conflict(b)); + /* Back-date the WC. */ + SVN_ERR(sbox_wc_update(b, "", 1)); - /* Resolve the tree conflict. */ - SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); - new_file_path = svn_relpath_join(branch_path, new_file_name, b->pool); - SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path), - ctx, b->pool, b->pool)); - SVN_ERR( - svn_client_conflict_tree_resolve_by_id( - conflict, - svn_client_conflict_option_merge_incoming_added_file_replace_and_merge, - b->pool)); + /* Add a file which occupies the same path but has different content + * and properties. */ + SVN_ERR(sbox_file_write(b, new_file_path, + /* NB: Ensure that the file content's length differs! + * Tests are run without sleep for timestamps. */ + "This is a new file on the branch\n")); + SVN_ERR(sbox_wc_add(b, new_file_path)); + SVN_ERR(sbox_wc_propset(b, "prop", propval_branch, new_file_path)); + + /* Update the WC. + * This should raise an "incoming add vs local add" tree conflict because + * the sbox test code runs updates with 'adds_as_modifications == FALSE'. */ + SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); /* Ensure that the file has the expected status. */ - opt_rev.kind = svn_opt_revision_working; + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + opt_rev.kind = svn_opt_revision_head; 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, @@ -419,11 +498,10 @@ test_option_merge_incoming_added_file_re 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); - /* ### Shouldn't there be a property conflict? The trunk wins. */ - SVN_TEST_ASSERT(status->prop_status == svn_wc_status_normal); - SVN_TEST_ASSERT(status->copied); + SVN_TEST_ASSERT(status->node_status == svn_wc_status_replaced); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified); + SVN_TEST_ASSERT(!status->copied); SVN_TEST_ASSERT(!status->switched); SVN_TEST_ASSERT(!status->file_external); SVN_TEST_ASSERT(status->moved_from_abspath == NULL); @@ -432,20 +510,14 @@ test_option_merge_incoming_added_file_re SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path), ctx, b->pool, b->pool)); - /* We should have a text conflict instead of a tree conflict. */ - SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted, - &props_conflicted, - &tree_conflicted, + /* Ensure that the expected tree conflict is present. */ + SVN_ERR(svn_client_conflict_get_conflicted(NULL, NULL, &tree_conflicted, conflict, b->pool, b->pool)); - SVN_TEST_ASSERT(text_conflicted && - props_conflicted->nelts == 0 && - !tree_conflicted); - - /* Verify the merged property value. */ - SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx, - sbox_wc_path(b, new_file_path), - "prop", b->pool, b->pool)); - SVN_TEST_STRING_ASSERT(propval->data, propval_trunk); + SVN_TEST_ASSERT(tree_conflicted); + SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) == + svn_wc_conflict_reason_added); + SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) == + svn_wc_conflict_action_add); return SVN_NO_ERROR; } @@ -454,7 +526,6 @@ test_option_merge_incoming_added_file_re * The following tests verify resolution of "incoming dir add vs. * local dir obstruction upon merge" tree conflicts. */ -static const char *new_dir_name = "newdir"; /* A helper function which prepares a working copy for the tests below. */ static svn_error_t * @@ -591,8 +662,8 @@ create_wc_with_dir_add_vs_dir_add_merge_ } static svn_error_t * -test_option_merge_incoming_added_dir_ignore(const svn_test_opts_t *opts, - apr_pool_t *pool) +test_merge_incoming_added_dir_ignore(const svn_test_opts_t *opts, + apr_pool_t *pool) { svn_client_ctx_t *ctx; svn_client_conflict_t *conflict; @@ -605,7 +676,7 @@ test_option_merge_incoming_added_dir_ign svn_opt_revision_t opt_rev; svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); - SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_ignore", + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_added_dir_ignore", opts, pool)); SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE, @@ -617,8 +688,7 @@ test_option_merge_incoming_added_dir_ign SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path), ctx, b->pool, b->pool)); SVN_ERR(svn_client_conflict_tree_resolve_by_id( - conflict, - svn_client_conflict_option_merge_incoming_add_ignore, + conflict, svn_client_conflict_option_incoming_add_ignore, ctx, b->pool)); /* Ensure that the directory has the expected status. */ @@ -656,12 +726,9 @@ test_option_merge_incoming_added_dir_ign return SVN_NO_ERROR; } -/* This test currently fails to meet expectations. Our merge code doesn't - * support a merge of files which were added in the same revision as their - * parent directory and were not modified since. */ static svn_error_t * -test_option_merge_incoming_added_dir_merge(const svn_test_opts_t *opts, - apr_pool_t *pool) +test_merge_incoming_added_dir_merge(const svn_test_opts_t *opts, + apr_pool_t *pool) { svn_client_ctx_t *ctx; svn_client_conflict_t *conflict; @@ -676,7 +743,7 @@ test_option_merge_incoming_added_dir_mer const svn_string_t *propval; svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); - SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_merge", + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_added_dir_merge", opts, pool)); SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE, @@ -687,10 +754,10 @@ test_option_merge_incoming_added_dir_mer new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool); SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path), ctx, b->pool, b->pool)); - SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool)); + SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool)); SVN_ERR(svn_client_conflict_tree_resolve_by_id( conflict, - svn_client_conflict_option_merge_incoming_added_dir_merge, + svn_client_conflict_option_incoming_added_dir_merge, ctx, b->pool)); /* Ensure that the directory has the expected status. */ @@ -725,24 +792,42 @@ test_option_merge_incoming_added_dir_mer props_conflicted->nelts == 0 && !tree_conflicted); - /* XFAIL: Currently, no text conflict is raised since the file is not merged. - * We should have a text conflict in the file. */ new_file_path = svn_relpath_join(branch_path, svn_relpath_join(new_dir_name, new_file_name, b->pool), b->pool); - SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path), - ctx, b->pool, b->pool)); - SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted, - &props_conflicted, - &tree_conflicted, - conflict, b->pool, b->pool)); - SVN_TEST_ASSERT(text_conflicted && - props_conflicted->nelts == 0 && - !tree_conflicted); - /* Verify the file's merged property value. */ - SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx, + /* Ensure that the file has the expected status. */ + 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_conflicted); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified); + 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); + + /* The file should now have a text conflict. */ + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path), + ctx, b->pool, b->pool)); + SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted, + &props_conflicted, + &tree_conflicted, + conflict, b->pool, b->pool)); + SVN_TEST_ASSERT(text_conflicted && + props_conflicted->nelts == 0 && + !tree_conflicted); + + /* Verify the file's merged property value. */ + SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx, sbox_wc_path(b, new_file_path), "prop", b->pool, b->pool)); SVN_TEST_STRING_ASSERT(propval->data, propval_trunk); @@ -750,11 +835,10 @@ test_option_merge_incoming_added_dir_mer return SVN_NO_ERROR; } -/* Same test as above, but with an additional file change on the trunk - * which makes resolution work as expected. */ +/* Same test as above, but with an additional file change on the trunk. */ static svn_error_t * -test_option_merge_incoming_added_dir_merge2(const svn_test_opts_t *opts, - apr_pool_t *pool) +test_merge_incoming_added_dir_merge2(const svn_test_opts_t *opts, + apr_pool_t *pool) { svn_client_ctx_t *ctx; svn_client_conflict_t *conflict; @@ -769,7 +853,7 @@ test_option_merge_incoming_added_dir_mer const svn_string_t *propval; svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); - SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_merge2", + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_added_dir_merge2", opts, pool)); SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, TRUE, FALSE, @@ -780,11 +864,11 @@ test_option_merge_incoming_added_dir_mer new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool); SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path), ctx, b->pool, b->pool)); - SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool)); + SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool)); SVN_ERR(svn_client_conflict_tree_resolve_by_id( conflict, - svn_client_conflict_option_merge_incoming_added_dir_merge, - b->pool)); + 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; @@ -818,7 +902,30 @@ test_option_merge_incoming_added_dir_mer props_conflicted->nelts == 0 && !tree_conflicted); - /* We should have a text conflict in the file. */ + new_file_path = svn_relpath_join(branch_path, + svn_relpath_join(new_dir_name, + new_file_name, b->pool), + b->pool); + + /* Ensure that the file has the expected status. */ + 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_conflicted); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified); + 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); + + /* The file should now have a text conflict. */ new_file_path = svn_relpath_join(branch_path, svn_relpath_join(new_dir_name, new_file_name, b->pool), @@ -834,19 +941,19 @@ test_option_merge_incoming_added_dir_mer !tree_conflicted); /* Verify the file's merged property value. */ - /* ### Shouldn't there be a property conflict? The branch wins. */ + /* ### Shouldn't there be a property conflict? The trunk wins. */ SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx, sbox_wc_path(b, new_file_path), "prop", b->pool, b->pool)); - SVN_TEST_STRING_ASSERT(propval->data, propval_branch); + SVN_TEST_STRING_ASSERT(propval->data, propval_trunk); return SVN_NO_ERROR; } /* Same test as above, but with an additional move operation on the trunk. */ static svn_error_t * -test_option_merge_incoming_added_dir_merge3(const svn_test_opts_t *opts, - apr_pool_t *pool) +test_merge_incoming_added_dir_merge3(const svn_test_opts_t *opts, + apr_pool_t *pool) { svn_client_ctx_t *ctx; svn_client_conflict_t *conflict; @@ -861,7 +968,7 @@ test_option_merge_incoming_added_dir_mer const svn_string_t *propval; svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); - SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_merge3", + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_added_dir_merge3", opts, pool)); SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, TRUE, TRUE, @@ -872,11 +979,11 @@ test_option_merge_incoming_added_dir_mer new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool); SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path), ctx, b->pool, b->pool)); - SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool)); + SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool)); SVN_ERR(svn_client_conflict_tree_resolve_by_id( conflict, - svn_client_conflict_option_merge_incoming_added_dir_merge, - b->pool)); + 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; @@ -910,7 +1017,31 @@ test_option_merge_incoming_added_dir_mer props_conflicted->nelts == 0 && !tree_conflicted); - /* We should have a text conflict in the file. */ + /* There should now be an 'add vs add' conflict on the new file. */ + new_file_path = svn_relpath_join(branch_path, + svn_relpath_join(new_dir_name, + new_file_name, b->pool), + b->pool); + + /* Ensure that the file has the expected status. */ + 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_conflicted); + SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted); + SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified); + 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); + + /* We should now have a text conflict in the file. */ new_file_path = svn_relpath_join(branch_path, svn_relpath_join(new_dir_name, new_file_name, b->pool), @@ -926,40 +1057,18 @@ test_option_merge_incoming_added_dir_mer !tree_conflicted); /* Verify the file's merged property value. */ - /* ### Shouldn't there be a property conflict? The branch wins. */ + /* ### Shouldn't there be a property conflict? The trunk wins. */ SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx, sbox_wc_path(b, new_file_path), "prop", b->pool, b->pool)); - SVN_TEST_STRING_ASSERT(propval->data, propval_branch); + SVN_TEST_STRING_ASSERT(propval->data, propval_trunk); - /* XFAIL: Currently, no subtree mergeinfo is created. - * - * Verify the directory's subtree mergeinfo. It should mention both - * location segments of ^/A/newdir's history, shouldn't it? Like this: - * - * /A/newdir:2-6 - * /newdir.orig:4 - * - * ### /newdir.orig was created in r3 and moved to /A/newdir in r5. - * ### Should the second line say "/newdir.orig:3-4" instead? */ - SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx, - sbox_wc_path(b, new_dir_path), - "svn:mergeinfo", b->pool, b->pool)); - SVN_TEST_ASSERT(propval != NULL); - SVN_TEST_STRING_ASSERT(propval->data, - apr_psprintf(b->pool, "/%s:2-6\n/%s:4", - svn_relpath_join(trunk_path, - new_dir_name, - b->pool), - apr_pstrcat(b->pool, - new_dir_name, ".orig", - SVN_VA_NULL))); return SVN_NO_ERROR; } static svn_error_t * -test_option_merge_incoming_added_dir_replace(const svn_test_opts_t *opts, - apr_pool_t *pool) +test_merge_incoming_added_dir_replace(const svn_test_opts_t *opts, + apr_pool_t *pool) { svn_client_ctx_t *ctx; svn_client_conflict_t *conflict; @@ -972,7 +1081,7 @@ test_option_merge_incoming_added_dir_rep svn_opt_revision_t opt_rev; svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); - SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_replace", + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_added_dir_replace", opts, pool)); SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE, @@ -983,11 +1092,11 @@ test_option_merge_incoming_added_dir_rep new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool); SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path), ctx, b->pool, b->pool)); - SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool)); + SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool)); SVN_ERR(svn_client_conflict_tree_resolve_by_id( conflict, - svn_client_conflict_option_merge_incoming_added_dir_replace, - b->pool)); + svn_client_conflict_option_incoming_added_dir_replace, + ctx, b->pool)); /* Ensure that the directory has the expected status. */ opt_rev.kind = svn_opt_revision_working; @@ -1028,8 +1137,8 @@ test_option_merge_incoming_added_dir_rep * support a merge of files which were added in the same revision as their * parent directory and were not modified since. */ static svn_error_t * -test_option_merge_incoming_added_dir_replace_and_merge( - const svn_test_opts_t *opts, apr_pool_t *pool) +test_merge_incoming_added_dir_replace_and_merge(const svn_test_opts_t *opts, + apr_pool_t *pool) { svn_client_ctx_t *ctx; svn_client_conflict_t *conflict; @@ -1043,7 +1152,8 @@ test_option_merge_incoming_added_dir_rep svn_opt_revision_t opt_rev; svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); - SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_replace_and_merge", + SVN_ERR(svn_test__sandbox_create(b, + "merge_incoming_added_dir_replace_and_merge", opts, pool)); SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE, @@ -1054,11 +1164,11 @@ test_option_merge_incoming_added_dir_rep new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool); SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path), ctx, b->pool, b->pool)); - SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool)); + SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool)); SVN_ERR(svn_client_conflict_tree_resolve_by_id( conflict, - svn_client_conflict_option_merge_incoming_added_dir_replace_and_merge, - b->pool)); + svn_client_conflict_option_incoming_added_dir_replace_and_merge, + ctx, b->pool)); /* Ensure that the directory has the expected status. */ opt_rev.kind = svn_opt_revision_working; @@ -1113,8 +1223,8 @@ test_option_merge_incoming_added_dir_rep /* Same test as above, but with an additional file change on the branch * which makes resolution work as expected. */ static svn_error_t * -test_option_merge_incoming_added_dir_replace_and_merge2( - const svn_test_opts_t *opts, apr_pool_t *pool) +test_merge_incoming_added_dir_replace_and_merge2(const svn_test_opts_t *opts, + apr_pool_t *pool) { svn_client_ctx_t *ctx; svn_client_conflict_t *conflict; @@ -1128,7 +1238,8 @@ test_option_merge_incoming_added_dir_rep svn_opt_revision_t opt_rev; svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); - SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_replace_and_merge", + SVN_ERR(svn_test__sandbox_create(b, + "merge_incoming_added_dir_replace_and_merge", opts, pool)); SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE, @@ -1139,11 +1250,11 @@ test_option_merge_incoming_added_dir_rep new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool); SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path), ctx, b->pool, b->pool)); - SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool)); + SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool)); SVN_ERR(svn_client_conflict_tree_resolve_by_id( conflict, - svn_client_conflict_option_merge_incoming_added_dir_replace_and_merge, - b->pool)); + svn_client_conflict_option_incoming_added_dir_replace_and_merge, + ctx, b->pool)); /* Ensure that the directory has the expected status. */ opt_rev.kind = svn_opt_revision_working; @@ -1197,7 +1308,9 @@ test_option_merge_incoming_added_dir_rep /* A helper function which prepares a working copy for the tests below. */ static svn_error_t * -create_wc_with_incoming_delete_merge_conflict(svn_test__sandbox_t *b) +create_wc_with_incoming_delete_merge_conflict(svn_test__sandbox_t *b, + svn_boolean_t move, + svn_boolean_t do_switch) { svn_client_ctx_t *ctx; static const char *trunk_url; @@ -1210,39 +1323,63 @@ create_wc_with_incoming_delete_merge_con SVN_ERR(sbox_wc_copy(b, trunk_path, branch_path)); SVN_ERR(sbox_wc_commit(b, "")); - /* Delete a file on the trunk. */ - deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool); - SVN_ERR(sbox_wc_delete(b, deleted_path)); - SVN_ERR(sbox_wc_commit(b, "")); + if (move) + { + const char *move_target_path; + + /* Move a file on the trunk. */ + deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool); + move_target_path = svn_relpath_join(trunk_path, new_file_name, b->pool); + SVN_ERR(sbox_wc_move(b, deleted_path, move_target_path)); + SVN_ERR(sbox_wc_commit(b, "")); + } + else + { + /* Delete a file on the trunk. */ + deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool); + SVN_ERR(sbox_wc_delete(b, deleted_path)); + SVN_ERR(sbox_wc_commit(b, "")); + } /* Modify a file on the branch. */ deleted_path = svn_relpath_join(branch_path, deleted_file_name, b->pool); - SVN_ERR(sbox_file_write(b, deleted_path, - "This is a modified file on the branch\n")); - SVN_ERR(sbox_wc_commit(b, "")); + SVN_ERR(sbox_file_write(b, deleted_path, modified_file_on_branch_content)); - /* Run a merge from the trunk to the branch. */ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); - - SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); - trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, SVN_VA_NULL); - opt_rev.kind = svn_opt_revision_head; opt_rev.value.number = SVN_INVALID_REVNUM; - /* This should raise an "incoming delete vs local edit" tree conflict. */ - SVN_ERR(svn_client_merge_peg5(trunk_url, NULL, &opt_rev, - sbox_wc_path(b, branch_path), - svn_depth_infinity, - FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, - NULL, ctx, b->pool)); + trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, + SVN_VA_NULL); + if (do_switch) + { + /* Switch the branch working copy to trunk. */ + svn_revnum_t result_rev; + + /* This should raise an "incoming delete vs local edit" tree conflict. */ + 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)); + } + else + { + /* Commit modifcation 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. */ + SVN_ERR(svn_client_merge_peg5(trunk_url, NULL, &opt_rev, + sbox_wc_path(b, branch_path), + svn_depth_infinity, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + NULL, ctx, b->pool)); + } return SVN_NO_ERROR; } /* Test 'incoming delete ignore' option. */ static svn_error_t * -test_option_merge_incoming_delete_ignore( - const svn_test_opts_t *opts, apr_pool_t *pool) +test_merge_incoming_delete_ignore(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; @@ -1255,20 +1392,46 @@ test_option_merge_incoming_delete_ignore struct svn_client_status_t *status; svn_opt_revision_t opt_rev; - SVN_ERR(svn_test__sandbox_create(b, "incoming_delete_ignore", + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_delete_ignore", opts, pool)); - SVN_ERR(create_wc_with_incoming_delete_merge_conflict(b)); + SVN_ERR(create_wc_with_incoming_delete_merge_conflict(b, FALSE, FALSE)); /* Resolve the tree conflict. */ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); deleted_path = svn_relpath_join(branch_path, deleted_file_name, b->pool); SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path), ctx, b->pool, b->pool)); - SVN_ERR(svn_client_conflict_tree_get_details(conflict, 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_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_resolve_by_id( conflict, svn_client_conflict_option_incoming_delete_ignore, - b->pool)); + ctx, b->pool)); /* Ensure that the deleted file has the expected status. */ opt_rev.kind = svn_opt_revision_working; @@ -1307,8 +1470,7 @@ test_option_merge_incoming_delete_ignore /* Test 'incoming delete accept' option. */ static svn_error_t * -test_option_merge_incoming_delete_accept( - const svn_test_opts_t *opts, apr_pool_t *pool) +test_merge_incoming_delete_accept(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; @@ -1321,20 +1483,46 @@ test_option_merge_incoming_delete_accept struct svn_client_status_t *status; svn_opt_revision_t opt_rev; - SVN_ERR(svn_test__sandbox_create(b, "incoming_delete_accept", + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_delete_accept", opts, pool)); - SVN_ERR(create_wc_with_incoming_delete_merge_conflict(b)); + SVN_ERR(create_wc_with_incoming_delete_merge_conflict(b, FALSE, FALSE)); /* Resolve the tree conflict. */ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); deleted_path = svn_relpath_join(branch_path, deleted_file_name, b->pool); SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path), ctx, b->pool, b->pool)); - SVN_ERR(svn_client_conflict_tree_get_details(conflict, 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_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_resolve_by_id( conflict, svn_client_conflict_option_incoming_delete_accept, - b->pool)); + ctx, b->pool)); /* Ensure that the deleted file has the expected status. */ opt_rev.kind = svn_opt_revision_working; @@ -1371,6 +1559,1671 @@ test_option_merge_incoming_delete_accept return SVN_NO_ERROR; } +/* Test 'incoming move file text merge' option for merge. */ +static svn_error_t * +test_merge_incoming_move_file_text_merge(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; + const char *deleted_path; + const char *new_file_path; + svn_client_conflict_t *conflict; + svn_boolean_t tree_conflicted; + svn_boolean_t text_conflicted; + apr_array_header_t *props_conflicted; + struct status_baton sb; + struct svn_client_status_t *status; + svn_opt_revision_t opt_rev; + svn_stringbuf_t *buf; + svn_node_kind_t kind; + + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_move_file_text_merge", + opts, pool)); + + SVN_ERR(create_wc_with_incoming_delete_merge_conflict(b, TRUE, FALSE)); + + /* Resolve the tree conflict. */ + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + deleted_path = svn_relpath_join(branch_path, deleted_file_name, b->pool); + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path), + 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_incoming_move_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_move_file_text_merge, + ctx, b->pool)); + + /* Ensure that the deleted 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, deleted_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_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); + new_file_path = svn_relpath_join(branch_path, new_file_name, b->pool); + SVN_TEST_STRING_ASSERT(status->moved_to_abspath, + sbox_wc_path(b, new_file_path)); + + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path), + ctx, b->pool, b->pool)); + + /* The file should not be in conflict. */ + SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted, + &props_conflicted, + &tree_conflicted, + conflict, b->pool, b->pool)); + SVN_TEST_ASSERT(!text_conflicted && + props_conflicted->nelts == 0 && + !tree_conflicted); + + /* Ensure that the moved 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_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, deleted_path)); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Ensure that the original file was removed. */ + SVN_ERR(svn_io_check_path(sbox_wc_path(b, deleted_path), &kind, b->pool)); + SVN_TEST_ASSERT(kind == svn_node_none); + + /* Ensure that the moved 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, modified_file_on_branch_content); + + return SVN_NO_ERROR; +} + +/* A helper function which prepares a working copy for the tests below. */ +static svn_error_t * +create_wc_with_incoming_delete_update_conflict(svn_test__sandbox_t *b, + svn_boolean_t move) +{ + const char *deleted_path; + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); + + if (move) + { + const char *move_target_path; + + /* Move a file on the trunk. */ + deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool); + move_target_path = svn_relpath_join(trunk_path, new_file_name, b->pool); + SVN_ERR(sbox_wc_move(b, deleted_path, move_target_path)); + SVN_ERR(sbox_wc_commit(b, "")); + } + else + { + /* Delete a file on the trunk. */ + deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool); + SVN_ERR(sbox_wc_delete(b, deleted_path)); + SVN_ERR(sbox_wc_commit(b, "")); + } + + /* Update into the past. */ + SVN_ERR(sbox_wc_update(b, "", 1)); + + /* Modify a file in the working copy. */ + deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool); + SVN_ERR(sbox_file_write(b, deleted_path, modified_file_on_branch_content)); + + /* Update to HEAD. + * This should raise an "incoming delete vs local edit" tree conflict. */ + SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); + + return SVN_NO_ERROR; +} + +/* Test 'incoming move file text merge' option for update. */ +static svn_error_t * +test_update_incoming_move_file_text_merge(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; + const char *deleted_path; + const char *new_file_path; + svn_client_conflict_t *conflict; + struct status_baton sb; + struct svn_client_status_t *status; + svn_opt_revision_t opt_rev; + svn_node_kind_t node_kind; + svn_stringbuf_t *buf; + + SVN_ERR(svn_test__sandbox_create(b, "update_incoming_move_file_text_merge", + opts, pool)); + + SVN_ERR(create_wc_with_incoming_delete_update_conflict(b, TRUE)); + + /* Resolve the tree conflict. */ + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool); + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path), + 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_incoming_move_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_move_file_text_merge, + ctx, b->pool)); + + /* Ensure that the deleted file is gone. */ + SVN_ERR(svn_io_check_path(sbox_wc_path(b, deleted_path), &node_kind, + b->pool)); + SVN_TEST_ASSERT(node_kind == svn_node_none); + + /* Ensure that the moved file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + new_file_path = svn_relpath_join(trunk_path, new_file_name, 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 moved 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, modified_file_on_branch_content); + + return SVN_NO_ERROR; +} + +/* Test 'incoming move file text merge' option for switch. */ +static svn_error_t * +test_switch_incoming_move_file_text_merge(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; + const char *deleted_path; + const char *new_file_path; + svn_client_conflict_t *conflict; + struct status_baton sb; + struct svn_client_status_t *status; + svn_opt_revision_t opt_rev; + svn_node_kind_t node_kind; + svn_stringbuf_t *buf; + + SVN_ERR(svn_test__sandbox_create(b, "switch_incoming_move_file_text_merge", + opts, pool)); + + SVN_ERR(create_wc_with_incoming_delete_merge_conflict(b, TRUE, TRUE)); + + /* Resolve the tree conflict. */ + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + deleted_path = svn_relpath_join(branch_path, deleted_file_name, b->pool); + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path), + 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_incoming_move_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_move_file_text_merge, + ctx, b->pool)); + + /* Ensure that the deleted file is gone. */ + SVN_ERR(svn_io_check_path(sbox_wc_path(b, deleted_path), &node_kind, + b->pool)); + SVN_TEST_ASSERT(node_kind == svn_node_none); + + /* Ensure that the moved file has the expected status. */ + opt_rev.kind = svn_opt_revision_working; + sb.result_pool = b->pool; + new_file_path = svn_relpath_join(branch_path, new_file_name, 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 moved 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, modified_file_on_branch_content); + + return SVN_NO_ERROR; +} + +/* A helper function which prepares a working copy for the tests below. */ +static svn_error_t * +create_wc_with_incoming_delete_dir_conflict(svn_test__sandbox_t *b, + svn_boolean_t move, + svn_boolean_t do_switch, + svn_boolean_t local_edit, + svn_boolean_t local_add) +{ + svn_client_ctx_t *ctx; + static const char *trunk_url; + svn_opt_revision_t opt_rev; + const char *deleted_path; + const char *deleted_child_path; + const char *new_file_path; + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); + + /* Create a branch of node "A". */ + SVN_ERR(sbox_wc_copy(b, trunk_path, branch_path)); + SVN_ERR(sbox_wc_commit(b, "")); + + /* On the trunk, add a file inside the dir about to be moved/deleted. */ + new_file_path = svn_relpath_join(trunk_path, + svn_relpath_join(deleted_dir_name, + new_file_name, b->pool), + b->pool); + SVN_ERR(sbox_file_write(b, new_file_path, + "This is a new file on the trunk\n")); + SVN_ERR(sbox_wc_add(b, new_file_path)); + SVN_ERR(sbox_wc_commit(b, "")); + + SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); + if (move) + { + const char *move_target_path; + + /* Move a directory on the trunk. */ + deleted_path = svn_relpath_join(trunk_path, deleted_dir_name, b->pool); + move_target_path = svn_relpath_join(trunk_path, new_dir_name, b->pool); + SVN_ERR(sbox_wc_move(b, deleted_path, move_target_path)); + SVN_ERR(sbox_wc_commit(b, "")); + } + else + { + /* Delete a directory on the trunk. */ + deleted_path = svn_relpath_join(trunk_path, deleted_dir_name, b->pool); + SVN_ERR(sbox_wc_delete(b, deleted_path)); + SVN_ERR(sbox_wc_commit(b, "")); + } + + 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, + b->pool), + b->pool); + /* Add new file on the branch. */ + SVN_ERR(sbox_file_write(b, new_child_path, added_file_on_branch_content)); + SVN_ERR(sbox_wc_add(b, new_child_path)); + } + else + { + /* Modify a file on the branch. */ + deleted_child_path = svn_relpath_join(branch_path, + svn_relpath_join(deleted_dir_name, + deleted_dir_child, + b->pool), + b->pool); + SVN_ERR(sbox_file_write(b, deleted_child_path, + modified_file_on_branch_content)); + } + + 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; + trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, + SVN_VA_NULL); + if (do_switch) + { + /* Switch the branch working copy to trunk. */ + svn_revnum_t result_rev; + + /* This should raise an "incoming delete vs local edit" tree conflict. */ + 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)); + } + else + { + /* 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)); + + if (local_edit) + { + /* Modify the file in the working copy. */ + SVN_ERR(sbox_file_write(b, deleted_child_path, + modified_file_in_working_copy_content)); + } + + /* This should raise an "incoming delete vs local edit" tree conflict. */ + SVN_ERR(svn_client_merge_peg5(trunk_url, NULL, &opt_rev, + sbox_wc_path(b, branch_path), + svn_depth_infinity, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + NULL, ctx, b->pool)); + } + + return SVN_NO_ERROR; +} + +/* Test 'incoming move dir merge' resolution option. */ +static svn_error_t * +test_merge_incoming_move_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; + const char *deleted_path; + const char *moved_to_path; + const char *child_path; + svn_client_conflict_t *conflict; + struct status_baton sb; + struct svn_client_status_t *status; + svn_stringbuf_t *buf; + svn_opt_revision_t opt_rev; + apr_array_header_t *options; + svn_client_conflict_option_t *option; + apr_array_header_t *possible_moved_to_abspaths; + + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_move_dir", opts, pool)); + + SVN_ERR(create_wc_with_incoming_delete_dir_conflict(b, TRUE, FALSE, FALSE, + FALSE)); + + deleted_path = svn_relpath_join(branch_path, deleted_dir_name, b->pool); + moved_to_path = svn_relpath_join(branch_path, new_dir_name, b->pool); + + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path), + ctx, b->pool, b->pool)); + SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool)); + + /* Check possible move destinations for the directory. */ + 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_incoming_move_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)); + + /* XFAIL: Currently, the resolver finds two possible destinations for + * the moved folder: + * + * Possible working copy destinations for moved-away 'A_branch/B' are: + * (1): 'A_branch/newdir' + * (2): 'A/newdir' + * Only one destination can be a move; the others are copies. + */ + SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 1); + SVN_TEST_STRING_ASSERT( + APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *), + sbox_wc_path(b, moved_to_path)); + + /* Resolve the tree conflict. */ + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, svn_client_conflict_option_incoming_move_dir_merge, + ctx, b->pool)); + + /* Ensure that the moved-away directory has the expected status. */ + sb.result_pool = b->pool; + opt_rev.kind = svn_opt_revision_working; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, deleted_path), + &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, moved_to_path)); + + /* Ensure that the moved-here directory has the expected status. */ + sb.result_pool = b->pool; + opt_rev.kind = svn_opt_revision_working; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, moved_to_path), + &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, deleted_path)); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Ensure that the edited file has the expected content. */ + child_path = svn_relpath_join(moved_to_path, deleted_dir_child, + b->pool); + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, child_path), + b->pool)); + SVN_TEST_STRING_ASSERT(buf->data, modified_file_on_branch_content); + + return SVN_NO_ERROR; +} + +/* Test 'incoming move dir merge' resolution option with local mods. */ +static svn_error_t * +test_merge_incoming_move_dir2(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; + const char *deleted_path; + const char *moved_to_path; + const char *child_path; + svn_client_conflict_t *conflict; + struct status_baton sb; + struct svn_client_status_t *status; + svn_stringbuf_t *buf; + svn_opt_revision_t opt_rev; + + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_move_dir2", opts, pool)); + + SVN_ERR(create_wc_with_incoming_delete_dir_conflict(b, TRUE, FALSE, TRUE, + FALSE)); + + deleted_path = svn_relpath_join(branch_path, deleted_dir_name, b->pool); + moved_to_path = svn_relpath_join(branch_path, new_dir_name, b->pool); + + /* Resolve the tree conflict. */ + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path), + ctx, b->pool, b->pool)); + SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool)); + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, svn_client_conflict_option_incoming_move_dir_merge, + ctx, b->pool)); + + /* Ensure that the moved-away directory has the expected status. */ + sb.result_pool = b->pool; + opt_rev.kind = svn_opt_revision_working; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, deleted_path), + &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, moved_to_path)); + + /* Ensure that the moved-here directory has the expected status. */ + sb.result_pool = b->pool; + opt_rev.kind = svn_opt_revision_working; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, moved_to_path), + &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, deleted_path)); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Ensure that the edited file has the expected content. */ + child_path = svn_relpath_join(moved_to_path, deleted_dir_child, + b->pool); + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, child_path), + b->pool)); + SVN_TEST_STRING_ASSERT(buf->data, modified_file_in_working_copy_content); + + return SVN_NO_ERROR; +} + +static svn_error_t * +test_merge_incoming_move_dir3(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; + const char *deleted_path; + const char *moved_to_path; + const char *child_path; + const char *child_url; + svn_client_conflict_t *conflict; + struct status_baton sb; + struct info_baton ib; + struct svn_client_status_t *status; + svn_stringbuf_t *buf; + svn_opt_revision_t opt_rev; + + SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_move_dir3", opts, pool)); + + SVN_ERR(create_wc_with_incoming_delete_dir_conflict(b, TRUE, FALSE, FALSE, + TRUE)); + + deleted_path = svn_relpath_join(branch_path, deleted_dir_name, b->pool); + moved_to_path = svn_relpath_join(branch_path, new_dir_name, b->pool); + + /* Resolve the tree conflict. */ + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path), + ctx, b->pool, b->pool)); + SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool)); + SVN_ERR(svn_client_conflict_tree_resolve_by_id( + conflict, svn_client_conflict_option_incoming_move_dir_merge, + ctx, b->pool)); + + /* Ensure that the moved-away directory has the expected status. */ + sb.result_pool = b->pool; + opt_rev.kind = svn_opt_revision_working; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, deleted_path), + &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, moved_to_path)); + + /* Ensure that the moved-here directory has the expected status. */ + sb.result_pool = b->pool; + opt_rev.kind = svn_opt_revision_working; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, moved_to_path), + &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, deleted_path)); + SVN_TEST_ASSERT(status->moved_to_abspath == NULL); + + /* Ensure that the file added on the branch has the expected content. */ + child_path = svn_relpath_join(branch_path, + svn_relpath_join(new_dir_name, + new_file_name_branch, + b->pool), + b->pool); + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, child_path), + b->pool)); + SVN_TEST_STRING_ASSERT(buf->data, added_file_on_branch_content); + + /* Ensure that the file added on the branch has the expected status. */ + sb.result_pool = b->pool; + opt_rev.kind = svn_opt_revision_working; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, child_path), + &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_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 added on the trunk has the expected content. */ + child_path = svn_relpath_join(trunk_path, + svn_relpath_join(new_dir_name, + new_file_name, + b->pool), + b->pool); + SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, child_path), + b->pool)); + SVN_TEST_STRING_ASSERT(buf->data, "This is a new file on the trunk\n"); + + /* Ensure that the file added on the trunk has the expected status. */ + sb.result_pool = b->pool; + opt_rev.kind = svn_opt_revision_working; + SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, child_path), + &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_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); + + /* Commit and make sure both files are present in the resulting revision. */ + SVN_ERR(sbox_wc_commit(b, "")); + + ib.result_pool = b->pool; + opt_rev.kind = svn_opt_revision_head; + + /* The file added on the branch should be present. */ + child_url = apr_pstrcat(b->pool, b->repos_url, "/", branch_path, "/", + new_dir_name, "/", new_file_name_branch, SVN_VA_NULL); + SVN_ERR(svn_client_info4(child_url, &opt_rev, &opt_rev, svn_depth_empty, + TRUE, TRUE, TRUE, NULL, + info_func, &ib, ctx, b->pool)); + + /* The file added on the trunk should be present. */ + child_url = apr_pstrcat(b->pool, b->repos_url, "/", branch_path, "/", + new_dir_name, "/", new_file_name, SVN_VA_NULL); + SVN_ERR(svn_client_info4(child_url, &opt_rev, &opt_rev, svn_depth_empty, + TRUE, TRUE, TRUE, NULL, + info_func, &ib, ctx, b->pool)); + + return SVN_NO_ERROR; +} + +/* A helper function which prepares a working copy for the tests below. */ +static svn_error_t * +create_wc_with_incoming_delete_vs_local_delete(svn_test__sandbox_t *b) +{ + svn_client_ctx_t *ctx; + static const char *trunk_url; + svn_opt_revision_t opt_rev; + const char *copy_src_path; + const char *copy_dst_name; + const char *copy_dst_path; + const char *deleted_file_path; + + SVN_ERR(sbox_add_and_commit_greek_tree(b)); + + /* Create a branch of node "A". */ + SVN_ERR(sbox_wc_copy(b, trunk_path, branch_path)); + SVN_ERR(sbox_wc_commit(b, "")); + + /* On the trunk, copy "mu" to "mu-copied". */ + copy_src_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool); + copy_dst_name = apr_pstrcat(b->pool, deleted_file_name, "-copied", + SVN_VA_NULL); + copy_dst_path = svn_relpath_join(trunk_path, copy_dst_name, b->pool); + SVN_ERR(sbox_wc_copy(b, copy_src_path, copy_dst_path)); + SVN_ERR(sbox_wc_commit(b, "")); + + /* Merge the file copy to the branch. */ + trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, SVN_VA_NULL); + opt_rev.kind = svn_opt_revision_head; + opt_rev.value.number = SVN_INVALID_REVNUM; + SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool)); + SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); + SVN_ERR(svn_client_merge_peg5(trunk_url, NULL, &opt_rev, + sbox_wc_path(b, branch_path), + svn_depth_infinity, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + NULL, ctx, b->pool)); + SVN_ERR(sbox_wc_commit(b, "")); + + /* Now delete the copied file on the trunk. */ + deleted_file_path = svn_relpath_join(trunk_path, copy_dst_name, b->pool); + SVN_ERR(sbox_wc_delete(b, deleted_file_path)); + SVN_ERR(sbox_wc_commit(b, "")); + + /* Delete the corresponding file on the branch. */ + deleted_file_path = svn_relpath_join(branch_path, copy_dst_name, + b->pool); + SVN_ERR(sbox_wc_delete(b, deleted_file_path)); + SVN_ERR(sbox_wc_commit(b, "")); + + /* Run a merge from the trunk to the branch. + * This should raise an "incoming delete vs local delete" tree conflict. */ + SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM)); + SVN_ERR(svn_client_merge_peg5(trunk_url, NULL, &opt_rev, + sbox_wc_path(b, branch_path), + svn_depth_infinity, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
[... 877 lines stripped ...]
