You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2022/01/14 14:01:51 UTC

svn commit: r1897034 [32/37] - in /subversion/branches/multi-wc-format: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/ contrib/client-side/svn_load_dirs/ contrib/hook-scripts/ contrib/s...

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(&copy_root, &copy_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(&copy_root, &copy_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
   };