You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2013/02/23 02:25:44 UTC

svn commit: r1449262 [20/25] - in /subversion/branches/ev2-export: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ build/win32/ contrib/server-side/fsfsfixer/fixer/ contrib/server-side/svncutter/ notes/ note...

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/merge_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/merge_tests.py?rev=1449262&r1=1449261&r2=1449262&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/merge_tests.py Sat Feb 23 01:25:38 2013
@@ -32,6 +32,8 @@ import time
 import svntest
 from svntest import main, wc, verify, actions
 
+from prop_tests import binary_mime_type_on_text_file_warning
+
 # (abbreviation)
 Item = wc.StateItem
 Skip = svntest.testcase.Skip_deco
@@ -47,11 +49,12 @@ from svntest.main import server_has_merg
 from svntest.actions import fill_file_with_lines
 from svntest.actions import make_conflict_marker_text
 from svntest.actions import inject_conflict_into_expected_state
+from svntest.verify import RegexListOutput
 
-def expected_merge_output(rev_ranges, additional_lines=None, foreign=False,
+def expected_merge_output(rev_ranges, additional_lines=[], foreign=False,
                           elides=False, two_url=False, target=None,
                           text_conflicts=0, prop_conflicts=0, tree_conflicts=0,
-                          resolved=[]):
+                          skipped_paths=0):
   """Generate an (inefficient) regex representing the expected merge
   output and mergeinfo notifications from REV_RANGES and ADDITIONAL_LINES.
 
@@ -63,7 +66,8 @@ def expected_merge_output(rev_ranges, ad
 
   ADDITIONAL_LINES is a list of strings to match the other lines of output;
   these are basically regular expressions except that backslashes will be
-  escaped herein.
+  escaped herein.  If ADDITIONAL_LINES is a single string, it is interpreted
+  the same as a list containing that string.
 
   If ELIDES is true, add to the regex an expression representing elision
   notification.  If TWO_URL is true, tweak the regex to expect the
@@ -72,11 +76,9 @@ def expected_merge_output(rev_ranges, ad
   TARGET is the local path to the target, as it should appear in
   notifications; if None, it is not checked.
 
-  TEXT_CONFLICTS, PROP_CONFLICTS and TREE_CONFLICTS specify the number of
-  each kind of conflict to expect.
-
-  RESOLVED contains a list of target paths of which conflicts are resolved
-  during merging"""
+  TEXT_CONFLICTS, PROP_CONFLICTS, TREE_CONFLICTS and SKIPPED_PATHS specify
+  the number of each kind of conflict to expect.
+  """
 
   if rev_ranges is None:
     lines = [svntest.main.merge_notify_line(None, None, False, foreign)]
@@ -98,34 +100,21 @@ def expected_merge_output(rev_ranges, ad
   if (two_url):
     lines += ["--- Recording mergeinfo for merge between repository URLs .*\n"]
 
-  if isinstance(additional_lines, list):
-    # Address "The Backslash Plague"
-    #
-    # If ADDITIONAL_LINES are present there are possibly paths in it with
-    # multiple components and on Windows these components are separated with
-    # '\'.  These need to be escaped properly in the regexp for the match to
-    # work correctly.  See http://aspn.activestate.com/ASPN/docs/ActivePython
-    # /2.2/howto/regex/regex.html#SECTION000420000000000000000.
-    if sys.platform == 'win32':
-      for i in range(0, len(additional_lines)):
-        additional_lines[i] = additional_lines[i].replace("\\", "\\\\")
-    lines.extend(additional_lines)
-  else:
-    if sys.platform == 'win32' and additional_lines != None:
-      additional_lines = additional_lines.replace("\\", "\\\\")
-    lines.append(str(additional_lines))
-
-  for rslv in resolved:
-    lines.append("Resolved conflicted state of '%s'" % re.escape(rslv))
-
-  if text_conflicts or prop_conflicts or tree_conflicts:
-    lines.append("Summary of conflicts:\n")
-    if text_conflicts:
-      lines.append("  Text conflicts: %d\n" % text_conflicts)
-    if prop_conflicts:
-      lines.append("  Property conflicts: %d\n" % prop_conflicts)
-    if tree_conflicts:
-      lines.append("  Tree conflicts: %d\n" % tree_conflicts)
+  # Address "The Backslash Plague"
+  #
+  # If ADDITIONAL_LINES are present there are possibly paths in it with
+  # multiple components and on Windows these components are separated with
+  # '\'.  These need to be escaped properly in the regexp for the match to
+  # work correctly.  See http://aspn.activestate.com/ASPN/docs/ActivePython
+  # /2.2/howto/regex/regex.html#SECTION000420000000000000000.
+  if isinstance(additional_lines, str):
+    additional_lines = [additional_lines]
+  if sys.platform == 'win32':
+    additional_lines = [line.replace("\\", "\\\\") for line in additional_lines]
+  lines += additional_lines
+
+  lines += svntest.main.summary_of_conflicts(text_conflicts, prop_conflicts,
+                                             tree_conflicts, skipped_paths)
 
   return "|".join(lines)
 
@@ -1401,6 +1390,13 @@ def merge_in_new_file_and_diff(sbox):
 
   # Finally, run diff.
   expected_output = [
+    "Index: " + url_branch_path + "/newfile\n",
+    "===================================================================\n",
+    "--- "+ url_branch_path + "/newfile	(revision 0)\n",
+    "+++ "+ url_branch_path + "/newfile	(working copy)\n",
+    "@@ -0,0 +1 @@\n",
+    "+newfile\n",
+
     "Index: " + url_branch_path + "\n",
     "===================================================================\n",
     "--- "+ url_branch_path + "\t(revision 2)\n",
@@ -1410,12 +1406,7 @@ def merge_in_new_file_and_diff(sbox):
     "___________________________________________________________________\n",
     "Added: " + SVN_PROP_MERGEINFO + "\n",
     "   Merged /A/B/E:r2-3\n",
-    "Index: " + url_branch_path + "/newfile\n",
-    "===================================================================\n",
-    "--- "+ url_branch_path + "/newfile	(revision 0)\n",
-    "+++ "+ url_branch_path + "/newfile	(working copy)\n",
-    "@@ -0,0 +1 @@\n",
-    "+newfile\n"]
+  ]
   svntest.actions.run_and_verify_svn(None, expected_output, [], 'diff',
                                      '--show-copies-as-adds', branch_path)
 
@@ -1518,6 +1509,7 @@ def merge_skips_obstructions(sbox):
 
   expected_output = wc.State(C_path, {
     'foo'    : Item(status='A '),
+    'Q/bar'  : Item(status='  ', treeconflict='A'), # Skipped
     })
   expected_mergeinfo_output = wc.State(C_path, {
     '' : Item(status=' U'),
@@ -1535,7 +1527,6 @@ def merge_skips_obstructions(sbox):
     })
   expected_skip = wc.State(C_path, {
     'Q'     : Item(verb='Skipped'),
-    'Q/bar' : Item(verb='Skipped'),
     })
 
   svntest.actions.run_and_verify_merge(C_path, '1', '2', F_url, None,
@@ -7023,8 +7014,7 @@ def single_file_replace_style_merge_capa
   # Merge the file mu alone to rev1
   svntest.actions.run_and_verify_svn(None,
                                      expected_merge_output(None,
-                                       ['D    ' + mu_path + '\n',
-                                        'A    ' + mu_path + '\n']),
+                                       ['R    ' + mu_path + '\n']),
                                      [],
                                      'merge',
                                      mu_path + '@2',
@@ -7803,6 +7793,9 @@ def merge_to_sparse_directories(sbox):
     'D'   : Item(status=' U'),
     'mu'  : Item(status='U '),
     ''    : Item(status=' U'),
+    # Shadowed below skips
+    'D/H/omega' : Item(status='  ', treeconflict='U'),
+    'B/E/beta'  : Item(status='  ', treeconflict='U'),
     })
   expected_mergeinfo_output = wc.State(immediates_dir, {
     ''  : Item(status=' U'),
@@ -7828,8 +7821,8 @@ def merge_to_sparse_directories(sbox):
                               "prop:name" : "propval"}),
     })
   expected_skip = svntest.wc.State(immediates_dir, {
-    'D/H/omega' : Item(verb="Skipped"),
-    'B/E/beta'  : Item(verb="Skipped"),
+    'D/H'       : Item(verb='Skipped missing target'),
+    'B/E'       : Item(verb='Skipped missing target'),
     })
   svntest.actions.run_and_verify_merge(immediates_dir, '4', '9',
                                        sbox.repo_url + '/A', None,
@@ -7864,6 +7857,9 @@ def merge_to_sparse_directories(sbox):
   expected_output = wc.State(files_dir, {
     'mu' : Item(status='U '),
     ''   : Item(status=' U'),
+    # Below the skips
+    'D/H/omega' : Item(status='  ', treeconflict='U'),
+    'B/E/beta'  : Item(status='  ', treeconflict='U'),
     })
   expected_mergeinfo_output = wc.State(files_dir, {
     ''   : Item(status=' U'),
@@ -7882,9 +7878,8 @@ def merge_to_sparse_directories(sbox):
                        props={SVN_PROP_MERGEINFO : '/A/mu:5-9'}),
     })
   expected_skip = svntest.wc.State(files_dir, {
-    'D'         : Item(verb="Skipped"),
-    'D/H/omega' : Item(verb="Skipped"),
-    'B/E/beta'  : Item(verb="Skipped"),
+    'D'         : Item(verb='Skipped missing target'),
+    'B'         : Item(verb='Skipped missing target'),
     })
   svntest.actions.run_and_verify_merge(files_dir, '4', '9',
                                        sbox.repo_url + '/A', None,
@@ -7912,6 +7907,9 @@ def merge_to_sparse_directories(sbox):
   # the one change that affects it directly (the prop add from r9).
   expected_output = wc.State(empty_dir, {
     ''   : Item(status=' U'),
+    # Below the skips
+    'B/E/beta'  : Item(status='  ', treeconflict='U'),
+    'D/H/omega' : Item(status='  ', treeconflict='U'),
     })
   expected_mergeinfo_output = wc.State(empty_dir, {
     '' : Item(status=' U'),
@@ -7926,10 +7924,9 @@ def merge_to_sparse_directories(sbox):
                               "prop:name" : "propval"}),
     })
   expected_skip = svntest.wc.State(empty_dir, {
-    'mu'        : Item(verb="Skipped missing target"),
-    'D'         : Item(verb="Skipped"),
-    'D/H/omega' : Item(verb="Skipped"),
-    'B/E/beta'  : Item(verb="Skipped"),
+    'mu'        : Item(verb='Skipped missing target'),
+    'D'         : Item(verb='Skipped missing target'),
+    'B'         : Item(verb='Skipped missing target'),
     })
   svntest.actions.run_and_verify_merge(empty_dir, '4', '9',
                                        sbox.repo_url + '/A', None,
@@ -12545,8 +12542,7 @@ def svn_copy(s_rev, path1, path2):
                                      '-r', s_rev, path1, path2)
 
 def svn_merge(rev_range, source, target, lines=None, elides=[],
-              text_conflicts=0, prop_conflicts=0, tree_conflicts=0, args=[],
-              resolved=[]):
+              text_conflicts=0, prop_conflicts=0, tree_conflicts=0, args=[]):
   """Merge a single change from path SOURCE to path TARGET and verify the
   output and that there is no error.  (The changes made are not verified.)
 
@@ -12562,9 +12558,7 @@ def svn_merge(rev_range, source, target,
   each kind of conflict to expect.
 
   ARGS are additional arguments passed to svn merge.
-
-  RESOLVED contains a list of targets of which conflicts are resolved
-  during merging"""
+  """
 
   source = local_path(source)
   target = local_path(target)
@@ -12585,8 +12579,7 @@ def svn_merge(rev_range, source, target,
                                   elides=elides,
                                   text_conflicts=text_conflicts,
                                   prop_conflicts=prop_conflicts,
-                                  tree_conflicts=tree_conflicts,
-                                  resolved=resolved)
+                                  tree_conflicts=tree_conflicts)
   svntest.actions.run_and_verify_svn(None, exp_out, [],
                                      'merge', rev_arg, source, target, *args)
 
@@ -13057,6 +13050,8 @@ def merge_target_and_subtrees_need_nonin
                                        None, 1)
 
 #----------------------------------------------------------------------
+# Part of this test is a regression test for issue #3250 "Repeated merging
+# of conflicting properties fails".
 @Issue(3250)
 def merge_two_edits_to_same_prop(sbox):
   "merge two successive edits to the same property"
@@ -13103,12 +13098,21 @@ def merge_two_edits_to_same_prop(sbox):
   # some other target within the same merge requiring only a part of the
   # revision range.
 
-  # We test issue #3250 here
-  # Revert changes to target branch wc
+  # ====================================================================
+
+  # We test issue #3250 here: that is, test that we can make two successive
+  # conflicting changes to the same property on the same node (here a file;
+  # in #3250 it was on a dir).
+  #
+  # ### But we no longer support merging into a node that's already in
+  #     conflict, and the 'rev3' merge here has been tweaked to resolve
+  #     the conflict, so it no longer tests the original #3250 scenario.
+  #
+  # Revert changes to branch wc
   svntest.actions.run_and_verify_svn(None, None, [],
                                      'revert', '--recursive', A_COPY_path)
 
-  # In the target branch, make two successive changes to the same property
+  # In the branch, make two successive changes to the same property
   sbox.simple_propset('p', 'new-val-3', 'A_COPY/mu')
   sbox.simple_commit('A_COPY/mu')
   rev3 = initial_rev + 3
@@ -13116,22 +13120,21 @@ def merge_two_edits_to_same_prop(sbox):
   sbox.simple_commit('A_COPY/mu')
   rev4 = initial_rev + 4
 
-  # Merge the two changes together to source.
+  # Merge the two changes together to trunk.
   svn_merge([rev3, rev4], A_COPY_path, A_path, [
       " C   %s\n" % mu_path,
       ], prop_conflicts=1, args=['--allow-mixed-revisions'])
 
-  # Revert changes to source wc, to test next scenario of #3250
+  # Revert changes to trunk wc, to test next scenario of #3250
   svntest.actions.run_and_verify_svn(None, None, [],
                                      'revert', '--recursive', A_path)
 
-  # Merge the first change, then the second, to source.
+  # Merge the first change, then the second, to trunk.
   svn_merge(rev3, A_COPY_path, A_path, [
-      " C   %s\n" % mu_path,
-      ], prop_conflicts=1,
+      " G   %s\n" % mu_path,
+      ],
       args=['--allow-mixed-revisions',
-            '--accept=working'],
-      resolved=[mu_path])
+            '--accept=working'])
   svn_merge(rev4, A_COPY_path, A_path, [
       " C   %s\n" % mu_path,
       ], prop_conflicts=1, args=['--allow-mixed-revisions'])
@@ -13955,9 +13958,17 @@ def no_self_referential_filtering_on_add
     'C'         : Item(status='D '),
     'C_MOVED'   : Item(status='A '),
     })
+  # Why is C_MOVED notified as ' G' rather than ' U'?  C_MOVED was
+  # added by the merge and there is only a single editor drive, so
+  # how can any prop changes be merged to it?  The answer is that
+  # the merge code does some quiet housekeeping, merging C_MOVED's
+  # inherited mergeinfo into its incoming mergeinfo, see
+  # http://subversion.tigris.org/issues/show_bug.cgi?id=4309
+  # This test is not covering issue #4309 so we let the current
+  # behavior pass.
   expected_mergeinfo_output = wc.State(A_COPY_2_path, {
     ''        : Item(status=' G'),
-    'C_MOVED' : Item(status=' G', prev_status=' G'),
+    'C_MOVED' : Item(status=' G'),
     })
   expected_elision_output = wc.State(A_COPY_2_path, {
     })
@@ -13992,6 +14003,7 @@ def no_self_referential_filtering_on_add
     'B/E/beta'  : Item("New content"),
     'B/lambda'  : Item("This is the file 'lambda'.\n"),
     'B/F'       : Item(),
+  # What's up with the mergeinfo
     'C_MOVED'   : Item(props={SVN_PROP_MERGEINFO : '/A/C_MOVED:10\n' +
                               '/A_COPY/C:8\n' +
                               '/A_COPY/C_MOVED:8',
@@ -15134,8 +15146,6 @@ def record_only_merge(sbox):
 #----------------------------------------------------------------------
 # Test for issue #3514 'svn merge --accept [ base | theirs-full ]
 # doesn't work'
-#
-# This test is marked as XFail until issue #3514 is fixed.
 @Issue(3514)
 def merge_automatic_conflict_resolution(sbox):
   "automatic conflict resolutions work with merge"
@@ -15233,7 +15243,7 @@ def merge_automatic_conflict_resolution(
                                      'revert', '--recursive', wc_dir)
 
   # Test --accept mine-conflict and mine-full
-  expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')})
+  expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='U ')})
   expected_disk.tweak('D/H/psi', contents="BASE.\n")
   expected_status.tweak('D/H/psi', status='  ')
   svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
@@ -15268,7 +15278,7 @@ def merge_automatic_conflict_resolution(
                                      'revert', '--recursive', wc_dir)
 
   # Test --accept theirs-conflict and theirs-full
-  expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')})
+  expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='U ')})
   expected_disk.tweak('D/H/psi', contents="New content")
   expected_status.tweak('D/H/psi', status='M ')
   svntest.actions.run_and_verify_merge(A_COPY_path, '2', '3',
@@ -15302,7 +15312,7 @@ def merge_automatic_conflict_resolution(
   svntest.actions.run_and_verify_svn(None, None, [],
                                      'revert', '--recursive', wc_dir)
   # Test --accept base
-  expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='C ')})
+  expected_output = wc.State(A_COPY_path, {'D/H/psi' : Item(status='U ')})
   expected_elision_output = wc.State(A_COPY_path, {
     })
   expected_disk.tweak('D/H/psi', contents="This is the file 'psi'.\n")
@@ -16511,7 +16521,8 @@ def merge_change_to_file_with_executable
   beta_path = sbox.ospath('A/B/E/beta')
 
   # Force one of the files to be a binary type
-  svntest.actions.run_and_verify_svn(None, None, [],
+  svntest.actions.run_and_verify_svn2(None, None,
+                                      binary_mime_type_on_text_file_warning, 0,
                                      'propset', 'svn:mime-type',
                                      'application/octet-stream',
                                      alpha_path)
@@ -17487,6 +17498,13 @@ def merge_source_with_replacement(sbox):
                                      'merge', sbox.repo_url + '/A',
                                      A_COPY_path)
 
+  # Misleading notifications are one thing, incorrect mergeinfo is quite
+  # another.
+  svntest.actions.run_and_verify_svn(None,
+                                     [A_COPY_path + ' - /A:2-5,7-8\n'],
+                                     [], 'pg', SVN_PROP_MERGEINFO,
+                                     '-R', A_COPY_path)
+
 #----------------------------------------------------------------------
 # Test for issue #4144 'Reverse merge with replace in source applies
 # diffs in forward order'.
@@ -17747,8 +17765,8 @@ def merge_with_added_subtrees_with_merge
     })
   expected_mergeinfo_output = wc.State(A_COPY2_path, {
     ''      : Item(status=' U'),
-    'C/X/Y' : Item(status=' G'), # Added with explicit mergeinfo so mergeinfo
-    })                           # describing the merge shows as mer'G'ed.
+    'C/X/Y' : Item(status=' U'), # Added with explicit mergeinfo
+    })
   expected_elision_output = wc.State(A_COPY2_path, {
     })
   expected_status = wc.State(A_COPY2_path, {
@@ -17902,7 +17920,9 @@ def merge_binary_file_with_keywords(sbox
   # make some 'binary' files with keyword expansion enabled
   for f in files:
     svntest.main.file_append(sbox.ospath(f), "With $Revision: $ keyword.\n")
-    sbox.simple_propset('svn:mime-type', 'application/octet-stream', f)
+    svntest.main.run_svn(binary_mime_type_on_text_file_warning,
+                         'propset', 'svn:mime-type',
+                         'application/octet-stream', sbox.ospath(f))
     sbox.simple_propset('svn:keywords', 'Revision', f)
   sbox.simple_commit()
 
@@ -18069,10 +18089,11 @@ def merge_target_selection(sbox):
 
   # Merge the file (wrong target)
   expected_output = [
-    'Skipped missing target: \'.\'\n',
-    'Summary of conflicts:\n',
-    '  Skipped paths: 1\n',
-  ]
+    '--- Merging r4 into \'.\':\n',
+    '   C .\n',
+    '--- Recording mergeinfo for merge of r4 into \'.\':\n',
+    ' U   .\n',
+  ] + svntest.main.summary_of_conflicts(tree_conflicts=1)
   svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'merge', '^/dir/binary-file', '-c', '4', '.')
 
@@ -18080,15 +18101,613 @@ def merge_target_selection(sbox):
 
   # Merge the dir (wrong target)
   expected_output = [
-    'Skipped \'%s\'\n' % os.path.join('binary-file', 'binary-file'),
+    '--- Merging r4 into \'binary-file\':\n',
+    '   C %s\n' % os.path.join('binary-file'),
     '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n',
     ' U   binary-file\n',
-    'Summary of conflicts:\n',
-    '  Skipped paths: 1\n',
-  ]
+  ] + svntest.main.summary_of_conflicts(tree_conflicts=1)
   svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'merge', '^/dir', '-c', '4', 'binary-file')
 
+@Issue(3405)
+def merge_properties_on_adds(sbox):
+  "merged directory properties are added"
+
+  sbox.build()
+
+  sbox.simple_copy('A/D/G', 'G')
+
+  sbox.simple_mkdir('A/D/G/M')
+  sbox.simple_mkdir('A/D/G/M/N')
+  sbox.simple_add_text('QQ', 'A/D/G/file', 'A/D/G/M/file')
+  sbox.simple_propset('key', 'value',
+                      'A/D/G/M', 'A/D/G/file', 'A/D/G/M/N', 'A/D/G/M/file')
+  sbox.simple_commit()
+  sbox.simple_update()
+
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'merge', '^/A/D/G', sbox.ospath('G'))
+
+  expected_output = svntest.verify.UnorderedOutput([
+    'Properties on \'%s\':\n' % sbox.ospath('G'),
+     '  svn:mergeinfo\n',
+     'Properties on \'%s\':\n' % sbox.ospath('G/M'),
+     '  key\n',
+     'Properties on \'%s\':\n' % sbox.ospath('G/file'),
+     '  key\n',
+     'Properties on \'%s\':\n' % sbox.ospath('G/M/N'),
+     '  key\n',
+     'Properties on \'%s\':\n' % sbox.ospath('G/M/file'),
+     '  key\n',
+  ])
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'proplist', '-R', sbox.ospath('G'))
+
+  expected_output = svntest.verify.UnorderedOutput([
+     'Properties on \'%s\':\n' % sbox.ospath('G/M'),
+     '  key\n',
+     'Properties on \'%s\':\n' % sbox.ospath('G/file'),
+     '  key\n',
+     'Properties on \'%s\':\n' % sbox.ospath('G/M/N'),
+     '  key\n',
+     'Properties on \'%s\':\n' % sbox.ospath('G/M/file'),
+     '  key\n',
+  ])
+
+  # I merged the tree, which should include history but only the files have
+  # the properties stored in PRISTINE. All directories have the properties
+  # as local changes in ACTUAL.
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'proplist', '-R', sbox.ospath('G'),
+                                     '-r', 'BASE')
+
+  # Note that this is not a regression. This has been the case since 1.0.
+  # ### We just made status, update and merge handle this without users
+  # ### knowing about this limitation.
+
+  # ### My guess is that the base merge support on svn_wc_merge_props()
+  # ### was originally designed to resolve this problem, but I can't
+  # ### find a released version where this was actually implemented.
+
+  # For fun, also check the status: 'svn status' suppresses the M from AM.
+
+  # G = sbox.ospath('G')
+  # 
+  # expected_status = wc.State('G', {
+  #   ''           : Item(status=' M', wc_rev='2'),
+  #   'pi'         : Item(status='  ', wc_rev='2'),
+  #   'tau'        : Item(status='  ', wc_rev='2'),
+  #   'file'       : Item(status='A ', copied='+', wc_rev='-'), # Copied, no changes
+  #   'M'          : Item(status='A ', copied='+', wc_rev='-'), # Copied, changes
+  #   'M/file'     : Item(status='  ', copied='+', wc_rev='-'), # Copied, no changes
+  #   'M/N'        : Item(status=' M', copied='+', wc_rev='-'), # Local changes
+  #   'rho'        : Item(status='  ', wc_rev='2'),
+  # })
+  # svntest.actions.run_and_verify_status(G, expected_status)
+
+
+# ======================================================================
+# Functions for parsing mergeinfo
+
+def parse_changes_list(changes_string):
+  """Parse a string containing a list of revision numbers in the form
+     of the '--change' command-line argument (e.g. '1,3,-5,7-10').
+     Return a list of elements of the form [[1], [3], [-5], [7,10]].
+  """
+  rev_ranges = []
+  for rr in changes_string.split(','):
+    if '-' in rr[1:]:
+      revs = rr.split('-')
+      rev_ranges.append([int(revs[0]), int(revs[1])])
+    else:
+      rev_ranges.append([int(rr)])
+  return rev_ranges
+
+def parse_rev_args(arg_list):
+  """Return a list of [rX:rY] or [rZ] elements representing ARG_LIST
+     whose elements are strings in the form '-rX:Y' or '-cZ,X-Y,...'.
+  """
+  rev_ranges = []
+  for arg in arg_list:
+    kind = arg[:2]
+    val = arg[2:]
+    if kind == '-r':
+      if ':' in val:
+        revs = map(int, val.split(':'))
+        if revs[0] < revs[1]:
+          rev_ranges.append([revs[0] + 1, revs[1]])
+        else:
+          rev_ranges.append([revs[0], revs[1] + 1])
+      else:
+        rev_ranges.append([int(val)])
+    elif kind == '-c':
+      rev_ranges.extend(parse_changes_list(val))
+    else:
+      raise ValueError("revision arg '" + arg + "' in '" + arg_list +
+                       "' does not start with -r or -c")
+  return rev_ranges
+
+class RangeList(list):
+  """Represents of a list of revision ranges, as a list of one- or
+     two-element lists, each of the form [X] meaning "--revision (X-1):X"
+     or [X,Y] meaning "--revision (X-1):Y".
+  """
+  def __init__(self, arg):
+    """
+    """
+    self.as_given = arg
+    if isinstance(arg, str):
+      list.__init__(self, parse_changes_list(arg))
+    elif isinstance(arg, list):
+      list.__init__(self, parse_rev_args(arg))
+    else:
+      raise ValueError("RangeList needs a string or a list, not '" + str(arg) + "'")
+
+def expected_merge_output2(tgt_ospath,
+                           recorded_ranges,
+                           merged_ranges=None,
+                           prop_conflicts=0):
+  """Return an ExpectedOutput instance corresponding to the expected
+     output of a merge into TGT_OSPATH, with one 'recording
+     mergeinfo...' notification per specified revision range in
+     RECORDED_RANGES and one 'merging...' notification per revision
+     range in MERGED_RANGES.
+
+     RECORDED_RANGES is a mergeinfo-string or a RangeList.
+
+     MERGED_RANGES is a list of mergeinfo-strings or a list of
+     RangeLists.  If None, it means [[r] for r in RECORDED_RANGES].
+  """
+  # Convert RECORDED_RANGES to a RangeList.
+  if isinstance(recorded_ranges, str):
+    recorded_ranges = RangeList(recorded_ranges)
+  # Convert MERGED_RANGES to a list of RangeLists.
+  if merged_ranges is None:
+    merged_ranges = [[r] for r in recorded_ranges]
+  elif len(merged_ranges) > 0 and isinstance(merged_ranges[0], str):
+    # List of mergeinfo-strings => list of rangelists
+    merged_ranges = [RangeList(r) for r in merged_ranges]
+
+  status_letters_re = prop_conflicts and ' [UC]' or ' U'
+  status_letters_mi = ' [UG]'
+  lines = []
+  for i, rr in enumerate(recorded_ranges):
+    # Merging ...
+    for sr in merged_ranges[i]:
+      revstart = sr[0]
+      revend = len(sr) > 1 and sr[1] or None
+      lines += [svntest.main.merge_notify_line(revstart, revend,
+                                               target=tgt_ospath)]
+      lines += [status_letters_re + '   ' + re.escape(tgt_ospath) + '\n']
+    # Recording mergeinfo ...
+    revstart = rr[0]
+    revend = len(rr) > 1 and rr[1] or None
+    lines += [svntest.main.mergeinfo_notify_line(revstart, revend,
+                                                 target=tgt_ospath)]
+    lines += [status_letters_mi + '   ' + re.escape(tgt_ospath) + '\n']
+
+  # Summary of conflicts
+  lines += svntest.main.summary_of_conflicts(prop_conflicts=prop_conflicts)
+
+  return RegexListOutput(lines)
+
+def expected_out_and_err(tgt_ospath,
+                           recorded_ranges,
+                           merged_ranges=None,
+                           prop_conflicts=0,
+                           expect_error=True):
+  """Return a tuple (expected_out, expected_err) giving the expected
+     output and expected error output for a merge into TGT_OSPATH. See
+     expected_merge_output2() for details of RECORDED_RANGES and
+     MERGED_RANGES and PROP_CONFLICTS.  EXPECT_ERROR should be true iff
+     we expect the merge to abort with an error about conflicts being
+     raised.
+  """
+  expected_out = expected_merge_output2(tgt_ospath, recorded_ranges,
+                                        merged_ranges, prop_conflicts)
+  if expect_error:
+    expected_err = RegexListOutput([
+                     '^svn: E155015: .* conflicts were produced .* into$',
+                     "^'.*" + re.escape(tgt_ospath) + "' --$",
+                     '^resolve all conflicts .* remaining$',
+                     '^unmerged revisions$'],
+                     match_all=False)
+  else:
+    expected_err = []
+
+  return expected_out, expected_err
+
+def check_mergeinfo(expected_mergeinfo, tgt_ospath):
+  """Read the mergeinfo on TGT_OSPATH; verify that it matches
+     EXPECTED_MERGEINFO (list of lines).
+  """
+  svntest.actions.run_and_verify_svn(
+    None, expected_mergeinfo, [], 'pg', SVN_PROP_MERGEINFO, tgt_ospath)
+
+def simple_merge(src_path, tgt_ospath, rev_args):
+  """Merge from ^/SRC_PATH to TGT_OSPATH using revision arguments REV_ARGS
+     (list of '-r...' or '-c...' strings); expect a single-target merge
+     with no conflicts or errors.
+  """
+  rev_ranges = RangeList(rev_args)
+
+  expected_out = expected_merge_output(rev_ranges,
+                                       [' U   ' + tgt_ospath + '\n',
+                                        ' [UG]   ' + tgt_ospath + '\n'],
+                                       target=tgt_ospath)
+  src_url = '^/' + src_path
+  svntest.actions.run_and_verify_svn(
+    None, expected_out, [],
+    'merge', src_url, tgt_ospath, '--accept', 'postpone', *rev_args)
+
+@SkipUnless(server_has_mergeinfo)
+@Issue(4306)
+# Test for issue #4306 'multiple editor drive file merges record wrong
+# mergeinfo during conflicts'
+def conflict_aborted_mergeinfo_described_partial_merge(sbox):
+  "conflicted split merge can be repeated"
+
+  sbox.build()
+
+  trunk = 'A'
+  branch = 'A2'
+  file = 'mu'
+  dir = 'B'
+  trunk_file = 'A/mu'
+  trunk_dir = 'A/B'
+
+  # r2: initial state
+  for rev in range(4, 11):
+    sbox.simple_propset('prop-' + str(rev), 'Old pval ' + str(rev),
+                        trunk_file, trunk_dir)
+  sbox.simple_commit()
+
+  # r3: branch
+  sbox.simple_copy(trunk, branch)
+  sbox.simple_commit()
+
+  zero_rev = 3
+
+  def edit_file_or_dir(path, rev, val):
+    """Make a local edit to the file at PATH."""
+    sbox.simple_propset('prop-' + str(rev), val + ' pval ' + str(rev), path)
+
+  # r4 through r10: simple edits
+  for rev in range(4, 11):
+    edit_file_or_dir(trunk_file, rev, 'Edited')
+    edit_file_or_dir(trunk_dir, rev, 'Edited')
+    sbox.simple_commit()
+
+  # r14: merge some changes to the branch so that later merges will be split
+  svntest.actions.run_and_verify_svn(None, None, [], 'merge', '-c5,9',
+                                     '^/' + trunk, sbox.ospath(branch),
+                                     '--accept', 'theirs-conflict')
+  sbox.simple_commit()
+  sbox.simple_update()
+
+  def revert_branch():
+    svntest.actions.run_and_verify_svn(None, None, [], 'revert', '-R',
+                                       sbox.ospath(branch))
+
+  def try_merge(relpath, conflict_rev, rev_args,
+                expected_out_err, expected_mi):
+    """Revert RELPATH in the branch; make a change that will conflict
+       with CONFLICT_REV if not None; merge RELPATH in the trunk
+       to RELPATH in the branch using revision arguments REV_ARGS (list of
+       '-r...' or '-c...' strings).
+
+       EXPECTED_OUT_ERR_MI is a tuple: (expected_out, expected_err,
+       expected_mi).  EXPECTED_OUT and EXPECTED_ERR are instances of
+       ExpectedOutput.
+
+       Expect to find mergeinfo EXPECTED_MI if not None.  EXPECTED_MI is
+       a single mergeinfo-string.
+    """
+    src_path = trunk + '/' + relpath
+    tgt_path = branch + '/' + relpath
+    tgt_ospath = sbox.ospath(tgt_path)
+
+    expected_out, expected_err = expected_out_err
+
+    revert_branch()
+
+    # Arrange for the merge to conflict at CONFLICT_REV.
+    if conflict_rev:
+      edit_file_or_dir(tgt_path, conflict_rev, 'Conflict')
+
+    src_url = '^/' + src_path
+    svntest.actions.run_and_verify_svn(
+                      None, expected_out, expected_err,
+                      'merge', src_url, tgt_ospath, '--accept', 'postpone',
+                      *rev_args)
+
+    if expected_mi is not None:
+      expected_mergeinfo = ['/' + src_path + ':' + expected_mi + '\n']
+      check_mergeinfo(expected_mergeinfo, tgt_ospath)
+
+  # In a mergeinfo-aware merge, each specified revision range is split
+  # internally into sub-ranges, to avoid any already-merged revisions.
+  #
+  # From white-box inspection, we see there are code paths that treat
+  # the last specified range and the last sub-range specially.  The
+  # first specified range or sub-range is not treated specially in terms
+  # of the code paths, although it might be in terms of data flow.
+  #
+  # We test merges that raise a conflict in the first and last sub-range
+  # of the first and last specified range.
+
+  for target in [file, dir]:
+
+    tgt_ospath = sbox.ospath(branch + '/' + target)
+
+    # First test: Merge "everything" to the branch.
+    #
+    # This merge is split into three sub-ranges: r3-4, r6-8, r10-head.
+    # We have arranged that the merge will raise a conflict in the first
+    # sub-range.  Since we are postponing conflict resolution, the merge
+    # should stop after the first sub-range, allowing us to resolve and
+    # repeat the merge at which point the next sub-range(s) can be merged.
+    # The mergeinfo on the target then should only reflect that the first
+    # sub-range (r3-4) has been merged.
+    #
+    # Previously the merge failed after merging only r3-4 (as it should)
+    # but mergeinfo for the whole range was recorded, preventing subsequent
+    # repeat merges from applying the rest of the source changes.
+    expect = expected_out_and_err(tgt_ospath,
+                                  '3-4', ['3-4'],
+                                  prop_conflicts=1)
+    try_merge(target, 4, [], expect, '3-5,9')
+
+    # Try a multiple-range merge that raises a conflict in the
+    # first sub-range in the first specified range;
+    expect = expected_out_and_err(tgt_ospath,
+                                  '4', ['4'],
+                                  prop_conflicts=1)
+    try_merge(target, 4, ['-c4-6,8-10'], expect, '4-5,9')
+    # last sub-range in the first specified range;
+    expect = expected_out_and_err(tgt_ospath,
+                                  '4-6', ['4,6'],
+                                  prop_conflicts=1)
+    try_merge(target, 6, ['-c4-6,8-10'], expect, '4-6,9')
+    # first sub-range in the last specified range;
+    expect = expected_out_and_err(tgt_ospath,
+                                  '4-6,8', ['4,6', '8'],
+                                  prop_conflicts=1)
+    try_merge(target, 8, ['-c4-6,8-10'], expect, '4-6,8-9')
+    # last sub-range in the last specified range.
+    # (Expect no error, because 'svn merge' does not throw an error if
+    # there is no more merging to do when a conflict occurs.)
+    expect = expected_out_and_err(tgt_ospath,
+                                  '4-6,8-10', ['4,6', '8,10'],
+                                  prop_conflicts=1, expect_error=False)
+    try_merge(target, 10, ['-c4-6,8-10'], expect, '4-6,8-10')
+
+    # Try similar merges but involving ranges in reverse order.
+    expect = expected_out_and_err(tgt_ospath,
+                                  '8', ['8'],
+                                  prop_conflicts=1)
+    try_merge(target, 8,  ['-c8-10,4-6'], expect, '5,8-9')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '8-10', ['8,10'],
+                                  prop_conflicts=1)
+    try_merge(target, 10, ['-c8-10,4-6'], expect, '5,8-10')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '8-10,4', ['8,10', '4'],
+                                  prop_conflicts=1)
+    try_merge(target, 4,  ['-c8-10,4-6'], expect, '4-5,8-10')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '8-10,4-6', ['8,10', '4,6'],
+                                  prop_conflicts=1, expect_error=False)
+    try_merge(target, 6,  ['-c8-10,4-6'], expect, '4-6,8-10')
+
+    # Try some reverse merges, with ranges in forward and reverse order.
+    #
+    # Reverse merges start with all source changes merged except 5 and 9.
+    revert_branch()
+    simple_merge(trunk + '/' + target, sbox.ospath(branch + '/' + target),
+                 ['-c-5,-9,4,6-8,10'])
+    sbox.simple_commit()
+    sbox.simple_update()
+
+    expect = expected_out_and_err(tgt_ospath,
+                                  '6-4,10-8', ['-6,-4', '-10,-8'],
+                                  expect_error=False)
+    try_merge(target, None, ['-r6:3', '-r10:7'], expect, '7')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '-6', ['-6'],
+                                  prop_conflicts=1)
+    try_merge(target, 6,  ['-r6:3', '-r10:7'], expect, '4,7-8,10')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '6-4', ['-6,-4'],
+                                  prop_conflicts=1)
+    try_merge(target, 4,  ['-r6:3', '-r10:7'], expect, '7-8,10')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '6-4,-10', ['-6,-4', '-10'],
+                                  prop_conflicts=1)
+    try_merge(target, 10, ['-r6:3', '-r10:7'], expect, '7-8')
+    expect = expected_out_and_err(tgt_ospath,
+                                  '6-4,10-8', ['-6,-4', '-10,-8'],
+                                  prop_conflicts=1, expect_error=False)
+    try_merge(target, 8,  ['-r6:3', '-r10:7'], expect, '7')
+
+@SkipUnless(server_has_mergeinfo)
+@Issue(4310)
+# Test for issue #4310 "each editor drive gets its own notification
+# during 'svn merge'"
+def multiple_editor_drive_merge_notifications(sbox):
+  "each editor drive gets its own notification"
+
+  sbox.build()
+
+  iota_branch_path = sbox.ospath('iota-copy')
+  C_branch_path = sbox.ospath('branch')
+
+  # Branch a file and a directory:
+
+  # r2
+  sbox.simple_copy('iota', 'iota-copy')
+  sbox.simple_commit()
+
+  # r3
+  sbox.simple_copy('A/C', 'branch')
+  sbox.simple_commit()
+
+  # r4-8 - Set five non-conflicting properties on the branch parents.
+  for i in range(0,5):
+    sbox.simple_propset('foo' + str(i) , 'bar', 'iota')
+    sbox.simple_propset('foo' + str(i) , 'bar', 'A/C')
+    sbox.simple_commit()
+
+  # Cherry pick merge r5 and r6 to each branch and commit.
+  svntest.actions.run_and_verify_svn(None, None, [], 'merge', '^/iota',
+                                     '-c', '5,7', iota_branch_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'merge', '^/A/C',
+                                     '-c', '5,7', C_branch_path)
+  sbox.simple_commit()
+
+  # Now auto merge all eligible revisions to each branch.
+  # First the directory target:
+  #
+  # TODO: We don't use run_and_verify_merge here because it has limitations
+  # re checking the merge notification headers -- which need to be improved
+  # at some point.
+  svntest.actions.run_and_verify_svn(
+    None,
+    ["--- Merging r2 through r4 into '" + C_branch_path + "':\n",
+     " U   " + C_branch_path + "\n",
+     "--- Merging r6 into '" + C_branch_path + "':\n",
+     " U   " + C_branch_path + "\n",
+     "--- Merging r8 through r9 into '" + C_branch_path + "':\n",
+     " U   " + C_branch_path + "\n",
+     "--- Recording mergeinfo for merge of r2 through r9 into '" +
+     C_branch_path + "':\n",
+     " U   " + C_branch_path + "\n"],
+    [], 'merge', sbox.repo_url + '/A/C', C_branch_path)
+
+  # Then the file target:
+  # Previously this failed because only the first range notification was
+  # printed:
+  #
+  #   >svn merge ^/iota iota-copy
+  #   --- Merging r2 through r4 into 'iota-copy':
+  #    U   iota-copy
+  #    U   iota-copy
+  #    U   iota-copy
+  #   --- Recording mergeinfo for merge of r2 through r9 into 'iota-copy':
+  #    U   iota-copy
+  #
+  # This is what we expect:
+  #
+  #   --- Merging r2 through r4 into 'iota-copy':
+  #    U   iota-copy
+  #   --- Merging r6 into 'iota-copy': <-- 2nd editor drive
+  #    U   iota-copy
+  #   --- Merging r8 through r9 into 'iota-copy': <-- 3rd editor drive
+  #    U   iota-copy
+  #   --- Recording mergeinfo for merge of r2 through r9 into 'iota-copy':
+  #    U   iota-copy
+  svntest.actions.run_and_verify_svn(
+    None,
+    ["--- Merging r2 through r4 into '" + iota_branch_path + "':\n",
+     " U   " + iota_branch_path + "\n",
+     "--- Merging r6 into '" + iota_branch_path + "':\n",
+     " U   " + iota_branch_path + "\n",
+     "--- Merging r8 through r9 into '" + iota_branch_path + "':\n",
+     " U   " + iota_branch_path + "\n",
+     "--- Recording mergeinfo for merge of r2 through r9 into '" +
+     iota_branch_path + "':\n",
+     " U   " + iota_branch_path + "\n"],
+    [], 'merge', sbox.repo_url + '/iota', iota_branch_path)
+
+#----------------------------------------------------------------------
+@SkipUnless(server_has_mergeinfo)
+@Issue(4317)
+# Test for issue #4317 "redundant notifications in single editor drive merge".
+def single_editor_drive_merge_notifications(sbox):
+  "single editor drive merge notifications"
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  A_copy_path = sbox.ospath('A_COPY')
+  D_copy_path = sbox.ospath('A_COPY/D')
+  psi_copy_path = sbox.ospath('A_COPY/D/H/psi')
+  omega_copy_path = sbox.ospath('A_COPY/D/H/omega')
+  beta_copy_path = sbox.ospath('A_COPY/B/E/beta')
+
+  # r2 - r6: Copy A to A_COPY and then make some text changes under A.
+  set_up_branch(sbox)
+
+  # r7 - Subtree merge
+  svntest.actions.run_and_verify_svn(None, None, [], 'merge', '^/A/D',
+                                     '-c4', D_copy_path)
+  sbox.simple_commit()
+  sbox.simple_update()
+
+  # Previously this failed because of redundant merge notifications
+  # for r4-7:
+  #
+  #   >svn merge ^/A A_COPY
+  #   --- Merging r2 through r3 into 'A_COPY\D':
+  #   U    A_COPY\D\H\psi
+  #   --- Merging r5 through r7 into 'A_COPY\D':
+  #   U    A_COPY\D\H\omega
+  #   --- Merging r4 through r7 into 'A_COPY':
+  #   U    A_COPY\B\E\beta
+  #   --- Recording mergeinfo for merge of r2 through r7 into 'A_COPY':
+  #    U   A_COPY
+  #   --- Recording mergeinfo for merge of r2 through r7 into 'A_COPY\D':
+  #    U   A_COPY\D
+  #   --- Eliding mergeinfo from 'A_COPY\D':
+  #    U   A_COPY\D
+  #
+  # The order of 'beta' and 'omega' can vary, so use UnorderedOutput.  This
+  # raises the possibility that the test could spuriously pass if the 'U'pdate
+  # notifications aren't grouped with the correct headers, but that's not what
+  # is being tested here.
+  expected_output = svntest.verify.UnorderedOutput(
+    ["--- Merging r2 through r3 into '" + A_copy_path + "':\n",
+     "U    " + psi_copy_path + "\n",
+     "--- Merging r4 through r7 into '" + A_copy_path + "':\n",
+     "U    " + omega_copy_path + "\n",
+     "U    " + beta_copy_path + "\n",
+     "--- Recording mergeinfo for merge of r2 through r7 into '" +
+     A_copy_path + "':\n",
+     " U   " + A_copy_path + "\n",
+     "--- Recording mergeinfo for merge of r2 through r7 into '" +
+     D_copy_path + "':\n",
+     " U   " + D_copy_path + "\n",
+     "--- Eliding mergeinfo from '" + D_copy_path + "':\n",
+     " U   " + D_copy_path + "\n"])
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge',
+                                     sbox.repo_url + '/A', A_copy_path)
+
+  # r8 and r9 - Commit and do reverse subtree merge.
+  sbox.simple_commit()
+  sbox.simple_update()
+  svntest.actions.run_and_verify_svn(None, None, [], 'merge', '^/A/D',
+                                     '-c-4', D_copy_path)
+  sbox.simple_commit()
+
+  # Now try a reverse merge.  There should only be one notification for
+  # r7-5:
+  sbox.simple_update()
+  expected_output = svntest.verify.UnorderedOutput(
+    ["--- Reverse-merging r7 through r5 into '" + A_copy_path + "':\n",
+     "U    " + beta_copy_path + "\n",
+     "U    " + omega_copy_path + "\n",
+     "--- Reverse-merging r4 through r3 into '" + A_copy_path + "':\n",
+     "U    " + psi_copy_path + "\n",
+     "--- Recording mergeinfo for reverse merge of r7 through r3 into '" +
+     A_copy_path + "':\n",
+     " U   " + A_copy_path + "\n",
+     "--- Recording mergeinfo for reverse merge of r7 through r3 into '" +
+     D_copy_path + "':\n",
+     " U   " + D_copy_path + "\n",
+     "--- Eliding mergeinfo from '" + D_copy_path + "':\n",
+     " U   " + D_copy_path + "\n"])     
+  svntest.actions.run_and_verify_svn(None, expected_output, [], 'merge',
+                                     '-r9:2', sbox.repo_url + '/A',
+                                     A_copy_path)
 
 ########################################################################
 # Run the tests
@@ -18229,6 +18848,10 @@ test_list = [ None,
               merge_binary_file_with_keywords,
               merge_conflict_when_keywords_removed,
               merge_target_selection,
+              merge_properties_on_adds,
+              conflict_aborted_mergeinfo_described_partial_merge,
+              multiple_editor_drive_merge_notifications,
+              single_editor_drive_merge_notifications,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/merge_tree_conflict_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/merge_tree_conflict_tests.py?rev=1449262&r1=1449261&r2=1449262&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/merge_tree_conflict_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/merge_tree_conflict_tests.py Sat Feb 23 01:25:38 2013
@@ -30,7 +30,7 @@ import time
 
 # Our testing module
 import svntest
-from svntest import main, wc, verify, actions
+from svntest import main, wc, verify, actions, deeptrees
 
 # (abbreviation)
 Item = wc.StateItem
@@ -789,21 +789,21 @@ def tree_conflicts_and_obstructions(sbox
 # parent directory.
 
 # convenience definitions
-leaf_edit = svntest.actions.deep_trees_leaf_edit
-tree_del = svntest.actions.deep_trees_tree_del
-leaf_del = svntest.actions.deep_trees_leaf_del
-
-disk_after_leaf_edit = svntest.actions.deep_trees_after_leaf_edit
-disk_after_leaf_del = svntest.actions.deep_trees_after_leaf_del
-disk_after_tree_del = svntest.actions.deep_trees_after_tree_del
-disk_after_leaf_del_no_ci = svntest.actions.deep_trees_after_leaf_del_no_ci
-disk_after_tree_del_no_ci = svntest.actions.deep_trees_after_tree_del_no_ci
+leaf_edit = svntest.deeptrees.deep_trees_leaf_edit
+tree_del = svntest.deeptrees.deep_trees_tree_del
+leaf_del = svntest.deeptrees.deep_trees_leaf_del
+
+disk_after_leaf_edit = svntest.deeptrees.deep_trees_after_leaf_edit
+disk_after_leaf_del = svntest.deeptrees.deep_trees_after_leaf_del
+disk_after_tree_del = svntest.deeptrees.deep_trees_after_tree_del
+disk_after_leaf_del_no_ci = svntest.deeptrees.deep_trees_after_leaf_del_no_ci
+disk_after_tree_del_no_ci = svntest.deeptrees.deep_trees_after_tree_del_no_ci
 
-deep_trees_conflict_output = svntest.actions.deep_trees_conflict_output
+deep_trees_conflict_output = svntest.deeptrees.deep_trees_conflict_output
 
 j = os.path.join
 
-DeepTreesTestCase = svntest.actions.DeepTreesTestCase
+DeepTreesTestCase = svntest.deeptrees.DeepTreesTestCase
 
 alpha_beta_gamma = svntest.wc.State('', {
   'F/alpha'           : Item(),
@@ -840,7 +840,7 @@ def tree_conflicts_on_merge_local_ci_4_1
 
   expected_skip = svntest.wc.State('', { })
 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase("local_tree_del_incoming_leaf_edit",
                         tree_del,
                         leaf_edit,
@@ -878,7 +878,7 @@ def tree_conflicts_on_merge_local_ci_4_2
   expected_skip = svntest.wc.State('', {
     })
 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase("local_tree_del_incoming_leaf_del",
                         tree_del,
                         leaf_del,
@@ -888,7 +888,6 @@ def tree_conflicts_on_merge_local_ci_4_2
                         expected_skip) ], True)
 
 #----------------------------------------------------------------------
-@XFail()
 @Issue(2282)
 def tree_conflicts_on_merge_local_ci_5_1(sbox):
   "tree conflicts 5.1: leaf edit, tree del"
@@ -906,23 +905,23 @@ def tree_conflicts_on_merge_local_ci_5_1
     ''                  : Item(status=' M', wc_rev='3'),
     'D'                 : Item(status='  ', wc_rev='3'),
     'D/D1'              : Item(status='  ', treeconflict='C', wc_rev='4'),
-    'D/D1/delta'        : Item(status='D ', wc_rev='4'),
+    'D/D1/delta'        : Item(status='  ', wc_rev='4'),
     'DD'                : Item(status='  ', wc_rev='3'),
-    'DD/D1'             : Item(status='  ', treeconflict='C', wc_rev='4'),
-    'DD/D1/D2'          : Item(status='D ', wc_rev='3'),
-    'DD/D1/D2/epsilon'  : Item(status='D ', wc_rev='4'),
+    'DD/D1'             : Item(status='  ', treeconflict='C', wc_rev='3'),
+    'DD/D1/D2'          : Item(status='  ', wc_rev='4'),
+    'DD/D1/D2/epsilon'  : Item(status='  ', wc_rev='4'),
     'DDD'               : Item(status='  ', wc_rev='3'),
-    'DDD/D1'            : Item(status='  ', treeconflict='C', wc_rev='4'),
-    'DDD/D1/D2'         : Item(status='D ', wc_rev='3'),
-    'DDD/D1/D2/D3'      : Item(status='D ', wc_rev='3'),
-    'DDD/D1/D2/D3/zeta' : Item(status='D ', wc_rev='4'),
+    'DDD/D1'            : Item(status='  ', treeconflict='C', wc_rev='3'),
+    'DDD/D1/D2'         : Item(status='  ', wc_rev='3'),
+    'DDD/D1/D2/D3'      : Item(status='  ', wc_rev='4'),
+    'DDD/D1/D2/D3/zeta' : Item(status='  ', wc_rev='4'),
     'DDF'               : Item(status='  ', wc_rev='3'),
-    'DDF/D1'            : Item(status='  ', treeconflict='C', wc_rev='4'),
-    'DDF/D1/D2'         : Item(status='D ', wc_rev='3'),
-    'DDF/D1/D2/gamma'   : Item(status='D ', wc_rev='4'),
+    'DDF/D1'            : Item(status='  ', treeconflict='C', wc_rev='3'),
+    'DDF/D1/D2'         : Item(status='  ', wc_rev='3'),
+    'DDF/D1/D2/gamma'   : Item(status='  ', wc_rev='4'),
     'DF'                : Item(status='  ', wc_rev='3'),
-    'DF/D1'             : Item(status='  ', treeconflict='C', wc_rev='4'),
-    'DF/D1/beta'        : Item(status='D ', wc_rev='4'),
+    'DF/D1'             : Item(status='  ', treeconflict='C', wc_rev='3'),
+    'DF/D1/beta'        : Item(status='  ', wc_rev='4'),
     'F'                 : Item(status='  ', wc_rev='3'),
     'F/alpha'           : Item(status='  ', treeconflict='C', wc_rev='4'),
 
@@ -931,7 +930,7 @@ def tree_conflicts_on_merge_local_ci_5_1
   expected_skip = svntest.wc.State('', {
     })
 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase("local_leaf_edit_incoming_tree_del",
                         leaf_edit,
                         tree_del,
@@ -941,7 +940,6 @@ def tree_conflicts_on_merge_local_ci_5_1
                         expected_skip) ], True)
 
 #----------------------------------------------------------------------
-@XFail()
 @Issue(2282)
 def tree_conflicts_on_merge_local_ci_5_2(sbox):
   "tree conflicts 5.2: leaf del, tree del"
@@ -957,15 +955,15 @@ def tree_conflicts_on_merge_local_ci_5_2
     'D'                 : Item(status='  ', wc_rev='3'),
     'F'                 : Item(status='  ', wc_rev='3'),
     'DD'                : Item(status='  ', wc_rev='3'),
-    'DD/D1'             : Item(status='! ', treeconflict='C'),
+    'DD/D1'             : Item(status='  ', wc_rev='3', treeconflict='C'),
     'DF'                : Item(status='  ', wc_rev='3'),
-    'DF/D1'             : Item(status='! ', treeconflict='C'),
+    'DF/D1'             : Item(status='  ', wc_rev='3', treeconflict='C'),
     'DDD'               : Item(status='  ', wc_rev='3'),
-    'DDD/D1'            : Item(status='! ', treeconflict='C'),
-    'DDD/D1/D2'         : Item(status='D ', wc_rev='3'),
+    'DDD/D1'            : Item(status='  ', wc_rev='3', treeconflict='C'),
+    'DDD/D1/D2'         : Item(status='  ', wc_rev='3'),
     'DDF'               : Item(status='  ', wc_rev='3'),
-    'DDF/D1'            : Item(status='! ', treeconflict='C'),
-    'DDF/D1/D2'         : Item(status='D ', wc_rev='3'),
+    'DDF/D1'            : Item(status='  ', wc_rev='3', treeconflict='C'),
+    'DDF/D1/D2'         : Item(status='  ', wc_rev='3'),
     'D/D1'              : Item(status='! ', treeconflict='C'),
     'F/alpha'           : Item(status='! ', treeconflict='C'),
     })
@@ -973,7 +971,7 @@ def tree_conflicts_on_merge_local_ci_5_2
   expected_skip = svntest.wc.State('', {
     })
 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase("local_leaf_del_incoming_tree_del",
                         leaf_del,
                         tree_del,
@@ -1012,7 +1010,7 @@ def tree_conflicts_on_merge_local_ci_6(s
   expected_skip = svntest.wc.State('', {
     })
 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase("local_tree_del_incoming_tree_del",
                         tree_del,
                         tree_del,
@@ -1059,7 +1057,7 @@ def tree_conflicts_on_merge_no_local_ci_
   expected_skip = svntest.wc.State('', {
     })
 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase(
                "local_tree_del_incoming_leaf_edit",
                tree_del,
@@ -1107,7 +1105,7 @@ def tree_conflicts_on_merge_no_local_ci_
   expected_skip = svntest.wc.State('', {
     })
 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase(
                "local_tree_del_incoming_leaf_del",
                tree_del,
@@ -1158,7 +1156,7 @@ def tree_conflicts_on_merge_no_local_ci_
   expected_skip = svntest.wc.State('', {
     })
 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase(
                "local_leaf_edit_incoming_tree_del",
                leaf_edit,
@@ -1170,7 +1168,6 @@ def tree_conflicts_on_merge_no_local_ci_
              ) ], False)
 
 #----------------------------------------------------------------------
-@XFail()
 @Issue(2282)
 def tree_conflicts_on_merge_no_local_ci_5_2(sbox):
   "tree conflicts 5.2: leaf del (no ci), tree del"
@@ -1188,25 +1185,25 @@ def tree_conflicts_on_merge_no_local_ci_
     'F'                 : Item(status='  ', wc_rev='3'),
     'F/alpha'           : Item(status='D ', wc_rev='3', treeconflict='C'),
     'DD'                : Item(status='  ', wc_rev='3'),
-    'DD/D1'             : Item(status='D ', wc_rev='3', treeconflict='C'),
+    'DD/D1'             : Item(status='  ', wc_rev='3', treeconflict='C'),
     'DD/D1/D2'          : Item(status='D ', wc_rev='3'),
     'DF'                : Item(status='  ', wc_rev='3'),
-    'DF/D1'             : Item(status='D ', wc_rev='3', treeconflict='C'),
+    'DF/D1'             : Item(status='  ', wc_rev='3', treeconflict='C'),
     'DF/D1/beta'        : Item(status='D ', wc_rev='3'),
     'DDD'               : Item(status='  ', wc_rev='3'),
-    'DDD/D1'            : Item(status='D ', wc_rev='3', treeconflict='C'),
-    'DDD/D1/D2'         : Item(status='D ', wc_rev='3'),
+    'DDD/D1'            : Item(status='  ', wc_rev='3', treeconflict='C'),
+    'DDD/D1/D2'         : Item(status='  ', wc_rev='3'),
     'DDD/D1/D2/D3'      : Item(status='D ', wc_rev='3'),
     'DDF'               : Item(status='  ', wc_rev='3'),
-    'DDF/D1'            : Item(status='D ', wc_rev='3', treeconflict='C'),
-    'DDF/D1/D2'         : Item(status='D ', wc_rev='3'),
+    'DDF/D1'            : Item(status='  ', wc_rev='3', treeconflict='C'),
+    'DDF/D1/D2'         : Item(status='  ', wc_rev='3'),
     'DDF/D1/D2/gamma'   : Item(status='D ', wc_rev='3'),
     })
 
   expected_skip = svntest.wc.State('', {
     })
 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase(
                "local_leaf_del_incoming_tree_del",
                leaf_del,
@@ -1255,7 +1252,7 @@ def tree_conflicts_on_merge_no_local_ci_
   expected_skip = svntest.wc.State('', {
     })
 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase(
                "local_tree_del_incoming_tree_del",
                tree_del,
@@ -1284,6 +1281,14 @@ def tree_conflicts_merge_edit_onto_missi
 
   sbox.build()
   expected_output = wc.State('', {
+  # Below the skips
+  'DD/D1/D2'          : Item(status='  ', treeconflict='U'),
+  'DD/D1/D2/epsilon'  : Item(status='  ', treeconflict='A'),
+  'DDD/D1/D2/D3'      : Item(status='  ', treeconflict='U'),
+  'DDD/D1/D2/D3/zeta' : Item(status='  ', treeconflict='A'),
+  'DDF/D1/D2/gamma'   : Item(status='  ', treeconflict='U'),
+  'D/D1/delta'        : Item(status='  ', treeconflict='A'),
+  'DF/D1/beta'        : Item(status='  ', treeconflict='U'),
   })
 
   expected_disk = disk_after_tree_del
@@ -1315,22 +1320,20 @@ def tree_conflicts_merge_edit_onto_missi
   expected_skip = svntest.wc.State('', {
     'F/alpha'           : Item(verb='Skipped missing target'),
     # Obstruction handling improvements in 1.7 and 1.8 added
-    'DF/D1/beta'        : Item(verb='Skipped missing target'),
-    'DDD/D1/D2/D3/zeta' : Item(verb='Skipped'),
-    'DDD/D1/D2/D3'      : Item(verb='Skipped missing target'),
-    'DDF/D1/D2/gamma'   : Item(verb='Skipped missing target'),
-    'D/D1/delta'        : Item(verb='Skipped'),
+    'DDD/D1'            : Item(verb='Skipped missing target'),
+    'DF/D1'             : Item(verb='Skipped missing target'),
+    'DDF/D1'            : Item(verb='Skipped missing target'),
     'D/D1'              : Item(verb='Skipped missing target'),
-    'DD/D1/D2/epsilon'  : Item(verb='Skipped'),
-    'DD/D1/D2'          : Item(verb='Skipped missing target'),
-    })
+    'DD/D1'             : Item(verb='Skipped missing target'),
+    'F/alpha'           : Item(verb='Skipped missing target'),
+  })
 
   # Currently this test fails because some parts of the merge
   # start succeeding. 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase(
                "local_tree_missing_incoming_leaf_edit",
-               svntest.actions.deep_trees_rmtree,
+               svntest.deeptrees.deep_trees_rmtree,
                leaf_edit,
                expected_output,
                expected_disk,
@@ -1356,6 +1359,11 @@ def tree_conflicts_merge_del_onto_missin
 
   sbox.build()
   expected_output = wc.State('', {
+  # Below the skips
+    'DF/D1/beta'        : Item(status='  ', treeconflict='D'),
+    'DDD/D1/D2/D3'      : Item(status='  ', treeconflict='D'),
+    'DD/D1/D2'          : Item(status='  ', treeconflict='D'),
+    'DDF/D1/D2/gamma'   : Item(status='  ', treeconflict='D'),
   })
 
   expected_disk = disk_after_tree_del
@@ -1388,17 +1396,16 @@ def tree_conflicts_merge_del_onto_missin
     'F/alpha'           : Item(verb='Skipped missing target'),
     'D/D1'              : Item(verb='Skipped missing target'),
     # Obstruction handling improvements in 1.7 and 1.8 added
-    'D/D1'              : Item(verb='Skipped missing target'),
-    'DD/D1/D2'          : Item(verb='Skipped missing target'),
-    'DF/D1/beta'        : Item(verb='Skipped missing target'),
-    'DDD/D1/D2/D3'      : Item(verb='Skipped missing target'),
-    'DDF/D1/D2/gamma'   : Item(verb='Skipped missing target'),
-    })
+    'DDD/D1'            : Item(verb='Skipped missing target'),
+    'DD/D1'             : Item(verb='Skipped missing target'),
+    'DDF/D1'            : Item(verb='Skipped missing target'),
+    'DF/D1'             : Item(verb='Skipped missing target'),
+  })
 
-  svntest.actions.deep_trees_run_tests_scheme_for_merge(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_merge(sbox,
     [ DeepTreesTestCase(
                "local_tree_missing_incoming_leaf_del",
-               svntest.actions.deep_trees_rmtree,
+               svntest.deeptrees.deep_trees_rmtree,
                leaf_del,
                expected_output,
                expected_disk,
@@ -1714,7 +1721,6 @@ def merge_replace_causes_tree_conflict(s
   actions.run_and_verify_status(wc_dir, expected_status)
 
 #----------------------------------------------------------------------
-@XFail()
 @Issue(3806)
 def merge_replace_causes_tree_conflict2(sbox):
   "replace vs. delete tree-conflicts"
@@ -1775,13 +1781,18 @@ def merge_replace_causes_tree_conflict2(
                         'A/D/H', 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi',
                         status='D ')
 
+  # H is now a file. This hides the status of the descendants.
+  expected_status.remove('A/D/H/chi', 'A/D/H/psi', 'A/D/H/omega')
+
   # Merge them one by one to see all the errors.
 
   ### A file-with-file replacement onto a deleted file.
   # svn merge $URL/A/mu $URL/branch/mu A/mu
   expected_stdout = expected_merge_output(None, [
     '   C ' + A_mu + '\n',  # merge
+    'A    ' + A_mu + '\n',  # merge
     " U   " + A + "\n",     # mergeinfo
+    " U   " + A_mu + "\n",  # mergeinfo -> 'RM' status
   ], target=A, two_url=True, tree_conflicts=1)
 
   actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
@@ -1793,13 +1804,14 @@ def merge_replace_causes_tree_conflict2(
   #
   #  D     C merge_tree_conflict_tests-23\A\mu
   #      >   local delete, incoming replace upon merge
-  expected_status.tweak('A/mu', status='R ', wc_rev='-', copied='+',
+  expected_status.tweak('A/mu', status='RM', wc_rev='-', copied='+',
                         treeconflict='C')
 
   ### A dir-with-dir replacement onto a deleted directory.
   # svn merge $URL/A/B $URL/branch/B A/B
   expected_stdout = expected_merge_output(None, [
     '   C ' + A_B_E + '\n',   # merge
+    'A    ' + A_B_E + '\n',   # merge
     " U   " + A_B + "\n",     # mergeinfo
   ], target=A_B, two_url=True, tree_conflicts=1)
 
@@ -1819,8 +1831,8 @@ def merge_replace_causes_tree_conflict2(
   # svn merge --depth=immediates $URL/A/D $URL/branch/D A/D
   expected_stdout = expected_merge_output(None, [
     '   C ' + A_D_H + '\n',   # merge
+    'A    ' + A_D_H + '\n',   # merge
     " U   " + A_D + "\n",     # mergeinfo
-    " U   " + A_D_G + "\n",
   ], target=A_D, two_url=True, tree_conflicts=1)
 
   actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
@@ -1839,7 +1851,9 @@ def merge_replace_causes_tree_conflict2(
   # svn merge $URL/A/D/G $URL/branch/D/G A/D/G
   expected_stdout = expected_merge_output(None, [
     '   C ' + A_D_G_pi + '\n',  # merge
-  ], target=A_D_G, elides=[A_D_G_pi, A_D_G], two_url=True, tree_conflicts=1)
+    'A    ' + A_D_G_pi + '\n',  # merge
+    " U   " + A_D_G + "\n",     # mergeinfo
+  ], target=A_D_G, two_url=True, tree_conflicts=1)
 
   actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'merge',
     url_A_D_G, url_branch_D_G, A_D_G)
@@ -1858,7 +1872,8 @@ def merge_replace_causes_tree_conflict2(
 
   # Check the tree conflict types:
   expected_stdout = '(R.*)|(Summary of conflicts.*)|(  Tree conflicts.*)' \
-                    '|(.*local delete, incoming replace upon merge.*)'
+                    '|(.*local delete, incoming replace upon merge.*)' \
+                    '|(      \>.*)'
   tree_conflicted_path = [A_B_E, A_mu, A_D_G_pi, A_D_H]
   for path in tree_conflicted_path:
     actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'st',
@@ -1868,7 +1883,6 @@ def merge_replace_causes_tree_conflict2(
 # Test for issue #4011 'merge of replacement on local delete fails'
 @SkipUnless(server_has_mergeinfo)
 @Issue(4011)
-@XFail()
 def merge_replace_on_del_fails(sbox):
   "merge replace on local delete fails"
 

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/patch_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/patch_tests.py?rev=1449262&r1=1449261&r2=1449262&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/patch_tests.py Sat Feb 23 01:25:38 2013
@@ -34,6 +34,7 @@ import tempfile
 import textwrap
 import zlib
 import posixpath
+import filecmp
 
 # Our testing module
 import svntest
@@ -265,9 +266,7 @@ def patch_absolute_paths(sbox):
   expected_output = [
     'U         %s\n' % os.path.join('A', 'B', 'E', 'alpha'),
     'Skipped missing target: \'%s\'\n' % lambda_path,
-    'Summary of conflicts:\n',
-    '  Skipped paths: 1\n'
-  ]
+  ] + svntest.main.summary_of_conflicts(skipped_paths=1)
 
   alpha_contents = "This is the file 'alpha'.\nWhoooo whooooo whoooooooo!\n"
 
@@ -971,10 +970,9 @@ def patch_add_new_dir(sbox):
 
   C_path = sbox.ospath('A/C')
   E_path = sbox.ospath('A/B/E')
-  svntest.actions.run_and_verify_svn("Deleting C failed", None, [],
-                                     'rm', C_path)
-  svntest.actions.run_and_verify_svn("Deleting E failed", None, [],
-                                     'rm', E_path)
+
+  svntest.main.safe_rmtree(C_path)
+  svntest.main.safe_rmtree(E_path)
   svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
 
   A_B_E_Y_new_path = sbox.ospath('A/B/E/Y/new')
@@ -987,9 +985,7 @@ def patch_add_new_dir(sbox):
     'Skipped missing target: \'%s\'\n' % A_B_E_Y_new_path,
     'Skipped missing target: \'%s\'\n' % A_C_new_path,
     'Skipped missing target: \'%s\'\n' % A_Z_new_path,
-    'Summary of conflicts:\n',
-    '  Skipped paths: 3\n',
-  ]
+  ] + svntest.main.summary_of_conflicts(skipped_paths=3)
 
   # Create the unversioned obstructing directory
   os.mkdir(os.path.dirname(A_Z_new_path))
@@ -1003,13 +999,13 @@ def patch_add_new_dir(sbox):
 
   expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
   expected_status.add({
-           'X'         : Item(status='A ', wc_rev=0),
-           'X/Y'       : Item(status='A ', wc_rev=0),
-           'X/Y/new'   : Item(status='A ', wc_rev=0),
-           'A/B/E'     : Item(status='D ', wc_rev=1),
-           'A/B/E/alpha': Item(status='D ', wc_rev=1),
-           'A/B/E/beta': Item(status='D ', wc_rev=1),
-           'A/C'       : Item(status='D ', wc_rev=1),
+           'X'          : Item(status='A ', wc_rev=0),
+           'X/Y'        : Item(status='A ', wc_rev=0),
+           'X/Y/new'    : Item(status='A ', wc_rev=0),
+           'A/B/E'      : Item(status='! ', wc_rev=1),
+           'A/B/E/alpha': Item(status='! ', wc_rev=1),
+           'A/B/E/beta' : Item(status='! ', wc_rev=1),
+           'A/C'        : Item(status='! ', wc_rev=1),
   })
 
   expected_skip = wc.State(
@@ -1173,9 +1169,7 @@ def patch_reject(sbox):
   expected_output = [
     'C         %s\n' % sbox.ospath('A/D/gamma'),
     '>         rejected hunk @@ -1,1 +1,1 @@\n',
-    'Summary of conflicts:\n',
-    '  Text conflicts: 1\n',
-  ]
+  ] + svntest.main.summary_of_conflicts(text_conflicts=1)
 
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.tweak('A/D/gamma', contents=gamma_contents)
@@ -2455,9 +2449,7 @@ def patch_same_twice(sbox):
     '>         hunk @@ -6,6 +6,9 @@ already applied\n',
     '>         hunk @@ -14,11 +17,8 @@ already applied\n',
     'Skipped \'%s\'\n' % beta_path,
-    'Summary of conflicts:\n',
-    '  Skipped paths: 1\n',
-  ]
+  ] + svntest.main.summary_of_conflicts(skipped_paths=1)
 
   expected_skip = wc.State('', {beta_path : Item(verb='Skipped')})
 
@@ -2542,9 +2534,7 @@ def patch_dir_properties(sbox):
   expected_output = [
     ' U        %s\n' % wc_dir,
     ' C        %s\n' % sbox.ospath('A/B'),
-    'Summary of conflicts:\n',
-    '  Property conflicts: 1\n',
-  ]
+  ] + svntest.main.summary_of_conflicts(prop_conflicts=1)
 
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.add({
@@ -3968,9 +3958,7 @@ def patch_delete_and_skip(sbox):
     'D         %s\n' % os.path.join('A', 'B', 'E', 'beta'),
     'D         %s\n' % os.path.join('A', 'B', 'E'),
     'Skipped missing target: \'%s\'\n' % skipped_path,
-    'Summary of conflicts:\n',
-    '  Skipped paths: 1\n'
-  ]
+  ] + svntest.main.summary_of_conflicts(skipped_paths=1)
 
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.remove('A/B/E/alpha')
@@ -4401,9 +4389,7 @@ def single_line_mismatch(sbox):
   expected_output = [
     'C         %s\n' % sbox.ospath('test'),
     '>         rejected hunk @@ -1,1 +1,1 @@\n',
-    'Summary of conflicts:\n',
-    '  Text conflicts: 1\n',
-  ]
+  ] + svntest.main.summary_of_conflicts(text_conflicts=1)
 
   svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'patch', patch_file_path, wc_dir)
@@ -4468,6 +4454,168 @@ def patch_empty_file(sbox):
 
   svntest.actions.verify_disk(wc_dir, expected_disk)
 
+@Issue(3362)
+def patch_apply_no_fuz(sbox):
+  "svn diff created patch should apply without fuz"
+
+  sbox.build(read_only=True)
+  wc_dir = sbox.wc_dir
+
+  svntest.main.file_write(sbox.ospath('test.txt'), '\n'.join([
+      "line_1",
+      "line_2",
+      "line_3",
+      "line_4",
+      "line_5",
+      "line_6",
+      "line_7",
+      "line_8",
+      "line_9",
+      "line_10",
+      "line_11",
+      "line_12",
+      "line_13",
+      "line_14",
+      "line_15",
+      "line_16",
+      "line_17",
+      "line_18",
+      "line_19",
+      "line_20",
+      "line_21",
+      "line_22",
+      "line_23",
+      "line_24",
+      "line_25",
+      "line_26",
+      "line_27",
+      "line_28",
+      "line_29",
+      "line_30",
+      ""
+    ]))
+  svntest.main.file_write(sbox.ospath('test_v2.txt'), '\n'.join([
+      "line_1a",
+      "line_1b",
+      "line_1c",
+      "line_1",
+      "line_2",
+      "line_3",
+      "line_4",
+      "line_5a",
+      "line_5b",
+      "line_5c",
+      "line_6",
+      "line_7",
+      "line_8",
+      "line_9",
+      "line_10",
+      "line_11a",
+      "line_11b",
+      "line_11c",
+      "line_12",
+      "line_13",
+      "line_14",
+      "line_15",
+      "line_16",
+      "line_17",
+      "line_18",
+      "line_19a",
+      "line_19b",
+      "line_19c",
+      "line_20",
+      "line_21",
+      "line_22",
+      "line_23",
+      "line_24",
+      "line_25",
+      "line_26",
+      "line_27a",
+      "line_27b",
+      "line_27c",
+      "line_28",
+      "line_29",
+      "line_30",
+      ""
+    ]))
+
+  sbox.simple_add('test.txt', 'test_v2.txt')
+
+  result, out_text, err_text = svntest.main.run_svn(None,
+                                                    'diff',
+                                                    '--old',
+                                                    sbox.ospath('test.txt'),
+                                                    '--new',
+                                                    sbox.ospath('test_v2.txt'))
+
+  patch_path = sbox.ospath('patch.diff')
+  svntest.main.file_write(patch_path, ''.join(out_text))
+
+  expected_output = [
+    'G         %s\n' % sbox.ospath('test.txt'),
+  ]
+
+  # Current result: lf.txt patched ok, new created, empty succeeds with offset.
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'patch', patch_path, wc_dir)
+
+  if not filecmp.cmp(sbox.ospath('test.txt'), sbox.ospath('test_v2.txt')):
+    raise svntest.Failure("Patch result not identical")
+
+@XFail()
+def patch_lacking_trailing_eol_on_context(sbox):
+  "patch file lacking trailing eol on context"
+
+  # Apply a patch where a hunk (the only hunk, in this case) ends with a
+  # context line that has no EOL, where this context line is going to
+  # match an existing line that *does* have an EOL.
+  #
+  # Around trunk@1443700, 'svn patch' wrongly removed an EOL from the
+  # target file at that position.
+
+  sbox.build(read_only = True)
+  wc_dir = sbox.wc_dir
+
+  patch_file_path = make_patch_path(sbox)
+
+  # Prepare
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_disk = svntest.main.greek_state.copy()
+
+  # Prepare the patch
+  unidiff_patch = [
+    "Index: iota\n",
+    "===================================================================\n",
+    "--- iota\t(revision 1)\n",
+    "+++ iota\t(working copy)\n",
+    # TODO: -1 +1
+    "@@ -1 +1,2 @@\n",
+    "+Some more bytes\n",
+    " This is the file 'iota'.", # No trailing \n on this context line!
+  ]
+  svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
+
+  iota_contents = "This is the file 'iota'.\n"
+
+  expected_output = [ 'U         %s\n' % sbox.ospath('iota') ]
+
+  # Test where the no-EOL context line is the last line in the target.
+  expected_disk.tweak('iota', contents="Some more bytes\n" + iota_contents)
+  expected_status.tweak('iota', status='M ')
+  expected_skip = wc.State('', { })
+  svntest.actions.run_and_verify_patch(wc_dir, os.path.abspath(patch_file_path),
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip)
+
+  # Test where the no-EOL context line is a non-last line in the target.
+  sbox.simple_revert('iota')
+  sbox.simple_append('iota', "Another line.\n")
+  expected_disk.tweak('iota', contents="Some more bytes\n" + iota_contents +
+                                       "Another line.\n")
+  expected_output = [ 'G         %s\n' % sbox.ospath('iota') ]
+  svntest.actions.run_and_verify_patch(wc_dir, os.path.abspath(patch_file_path),
+                                       expected_output, expected_disk,
+                                       expected_status, expected_skip)
 
 
 ########################################################################
@@ -4519,6 +4667,8 @@ test_list = [ None,
               patch_replace_dir_with_file_and_vv,
               single_line_mismatch,
               patch_empty_file,
+              patch_apply_no_fuz,
+              patch_lacking_trailing_eol_on_context,
             ]
 
 if __name__ == '__main__':

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/prop_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/prop_tests.py?rev=1449262&r1=1449261&r2=1449262&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/prop_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/prop_tests.py Sat Feb 23 01:25:38 2013
@@ -49,6 +49,10 @@ def is_non_posix_and_non_windows_os():
   """lambda function to skip revprop_change test"""
   return (not svntest.main.is_posix_os()) and sys.platform != 'win32'
 
+# this is global so other test files can use it
+binary_mime_type_on_text_file_warning = \
+  "svn: warning:.*is a binary mime-type but file.*looks like text.*"
+
 ######################################################################
 # Tests
 
@@ -612,7 +616,9 @@ def inappropriate_props(sbox):
   svntest.main.file_append(path, "binary")
   sbox.simple_add('binary')
 
-  sbox.simple_propset('svn:mime-type', 'application/octet-stream', 'binary')
+  svntest.main.run_svn(binary_mime_type_on_text_file_warning,
+                       'propset', 'svn:mime-type', 'application/octet-stream',
+                       sbox.ospath('binary'))
 
   svntest.actions.run_and_verify_svn('Illegal target', None,
                                      svntest.verify.AnyOutput,
@@ -1790,7 +1796,7 @@ def rm_of_replaced_file(sbox):
   exit_code, output, errput = svntest.main.run_svn(None,
                                                    'proplist', '-v',
                                                    mu_path + '@base')
-  expected_output = svntest.verify.UnorderedRegexOutput([
+  expected_output = svntest.verify.UnorderedRegexListOutput([
       'Properties on',
       '  yellow',
       '    submarine',
@@ -2124,6 +2130,8 @@ def propget_redirection(sbox):
   # it refers to non-existent path-revs, but that is not relevant to
   # this test.  What matters is that it is a realistic 'big' mergeinfo
   # value (it is from Subversion's own 1.6.x branch in fact).
+  # Also, the syntax is wrong: every path should start with '/';
+  # Subversion currently silently corrects this.
   big_prop_val = "subversion/branches/1.5.x:872364-874936\n"           + \
   "subversion/branches/1.5.x-34184:874657-874741\n"                    + \
   "subversion/branches/1.5.x-34432:874744-874798\n"                    + \
@@ -2263,131 +2271,26 @@ def propget_redirection(sbox):
 
   # Check if the redirected output of svn pg -vR on the root of the WC
   # is what we expect.
+  expected_mergeinfo_displayed = [
+    '    /' + line for line in big_prop_val.splitlines(True) ]
   expected_output = [
     "Properties on '" + B_path +  "':\n", # Should ocur only once!
+    "  svn:mergeinfo\n",
+    ] + expected_mergeinfo_displayed + [
     "Properties on '" + C_path +  "':\n", # Should ocur only once!
+    "  svn:mergeinfo\n",
+    ] + expected_mergeinfo_displayed + [
     "Properties on '" + D_path +  "':\n", # Should ocur only once!
-    # Everything below should appear three times since this same
-    # mergeinfo value is set on three paths in the WC.
     "  svn:mergeinfo\n",
-    "    /subversion/branches/1.5.x:872364-874936\n",
-    "    /subversion/branches/1.5.x-34184:874657-874741\n",
-    "    /subversion/branches/1.5.x-34432:874744-874798\n",
-    "    /subversion/branches/1.5.x-issue3067:872184-872314\n",
-    "    /subversion/branches/1.5.x-issue3157:872165-872175\n",
-    "    /subversion/branches/1.5.x-issue3174:872178-872348\n",
-    "    /subversion/branches/1.5.x-r30215:870310,870312,870319,870362\n",
-    "    /subversion/branches/1.5.x-r30756:874853-874870\n",
-    "    /subversion/branches/1.5.x-r30868:870951-870970\n",
-    "    /subversion/branches/1.5.x-r31314:874476-874605\n",
-    "    /subversion/branches/1.5.x-r31516:871592-871649\n",
-    "    /subversion/branches/1.5.x-r32470:872546-872676\n",
-    "    /subversion/branches/1.5.x-r32968:873773-873872\n",
-    "    /subversion/branches/1.5.x-r33447:873527-873547\n",
-    "    /subversion/branches/1.5.x-r33465:873541-873549\n",
-    "    /subversion/branches/1.5.x-r33641:873880-873883\n",
-    "    /subversion/branches/1.5.x-r34050-followups:874639-874686\n",
-    "    /subversion/branches/1.5.x-r34487:874562-874581\n",
-    "    /subversion/branches/1.5.x-ra_serf-backports:872354-872626\n",
-    "    /subversion/branches/1.5.x-rb-test-fix:874916-874919\n",
-    "    /subversion/branches/1.5.x-reintegrate-improvements:874586-874922\n",
-    "    /subversion/branches/1.5.x-tests-pass:870925-870973\n",
-    "    /subversion/branches/dont-save-plaintext-passwords-by-default:"
-    "870728-871118\n",
-    "    /subversion/branches/gnome-keyring:870558-871410\n",
-    "    /subversion/branches/issue-3220-dev:872210-872226\n",
-    "    /subversion/branches/kwallet:870785-871314\n",
-    "    /subversion/branches/log-g-performance:870941-871032\n",
-    "    /subversion/branches/r30963-1.5.x:871056-871076\n",
-    "    /subversion/branches/reintegrate-improvements:873853-874164\n",
-    "    /subversion/branches/svn-mergeinfo-enhancements:870196\n",
-    "    /subversion/branches/svnpatch-diff:871905\n",
-    "    /subversion/trunk:869159-869165,869168-869181,869185,869188,869191,"
-    "869200-869201,869203-869207,869209-869224,869227-869238,869240-869244,"
-    "869248,869250-869260,869262-869263,869265,869267-869268,869272-869280,"
-    "869282-869325,869328-869330,869335,869341-869347,869351,869354-869355,"
-    "869358,869361-869377,869379-869381,869383-869417,869419-869422,869432-"
-    "869453,869455-869466,869471-869473,869475,869483,869486,869488-869489,"
-    "869491-869497,869499-869500,869503,869506-869508,869510-869521,869523-"
-    "869540,869542-869552,869556,869558,869560-869561,869563,869565,869567,"
-    "869570,869572,869582,869601-869602,869605,869607,869613-869614,869616,"
-    "869618,869620,869625,869627,869630,869633,869639,869641-869643,869645-"
-    "869652,869655,869657,869665,869668,869674,869677,869681,869685,869687-"
-    "869688,869693,869697,869699-869700,869704-869708,869716,869719,869722,"
-    "869724,869730,869733-869734,869737-869740,869745-869746,869751-869754,"
-    "869766,869812-869813,869815-869818,869820,869825,869837,869841,869843-"
-    "869844,869858,869860-869861,869871,869875,869889,869895,869898,869902,"
-    "869907,869909,869926,869928-869929,869931-869933,869942-869943,869950,"
-    "869952,869957-869958,869969,869972,869974,869988,869994,869996,869999,"
-    "870004,870013-870014,870016,870024,870032,870036,870039,870041-870043,"
-    "870054,870060,870068-870071,870078,870083,870094,870104,870124,870127-"
-    "870128,870133,870135-870136,870141,870144,870148,870160,870172,870175,"
-    "870191,870198,870203-870204,870211,870219,870225,870233,870235-870236,"
-    "870254-870255,870259,870307,870311,870313,870320,870323,870330-870331,"
-    "870352-870353,870355,870359-870360,870371,870373,870378,870393-870395,"
-    "870402,870409-870410,870414,870416,870421,870436,870442,870447,870449,"
-    "870452,870454,870466,870476,870481-870483,870486,870500,870502,870505,"
-    "870513-870518,870522-870523,870527,870529,870534,870536-870538,870540-"
-    "870541,870543-870548,870554,870556,870561,870563,870584,870590-870592,"
-    "870594-870595,870597,870618,870620,870622,870625-870626,870641,870647,"
-    "870657,870665,870671,870681,870702-870703,870706-870708,870717-870718,"
-    "870727,870730,870737,870740,870742,870752,870758,870800,870809,870815,"
-    "870817,870820-870825,870830,870834-870836,870850-870851,870853,870859,"
-    "870861,870886,870894,870916-870918,870942,870945,870957,870962,870970,"
-    "870979,870981,870989,870996,871003,871005,871009,871011,871023,871033,"
-    "871035-871038,871041,871060,871078,871080,871092,871097,871099,871105,"
-    "871107,871120,871123-871127,871130,871133-871135,871140,871149,871155-"
-    "871156,871160,871162,871164,871181,871191,871199-871200,871205,871211-"
-    "871212,871215,871219,871225,871227,871229,871231,871236,871270,871273,"
-    "871277,871283,871297,871302,871306,871308,871315-871320,871323-871325,"
-    "871333-871335,871345,871347-871350,871354,871357,871361,871363-871366,"
-    "871374-871375,871377,871382,871385-871388,871391,871408,871411,871422,"
-    "871435,871441,871443-871444,871465,871470,871472-871476,871481,871489,"
-    "871499,871501-871502,871505,871508,871520,871523,871525-871527,871538,"
-    "871542,871544,871547-871549,871556,871559,871562-871563,871578,871581,"
-    "871587,871589-871597,871608,871613,871616-871617,871620,871624,871649,"
-    "871668,871675,871677,871693-871694,871696,871704,871732-871733,871744,"
-    "871747,871759,871762,871766,871769,871793,871796,871799,871801,871811,"
-    "871813,871821-871826,871831,871843,871860,871880,871891,871894,871899,"
-    "871907,871911,871926,871928,871933,871935,871941-871942,871947-871949,"
-    "871958,871974,872000-872001,872003,872005,872018,872022,872038,872065,"
-    "872068,872086,872091,872093,872097,872103,872112,872130,872154,872157,"
-    "872206,872216,872218-872219,872227,872234,872238,872243,872253,872255,"
-    "872259,872261,872278-872279,872281,872310-872311,872362,872404,872416-"
-    "872417,872429,872431,872434,872439,872450-872453,872468,872470,872477-"
-    "872478,872483,872490-872491,872495,872515-872516,872518-872519,872537,"
-    "872541,872544,872565,872568,872571-872573,872584,872596-872597,872612,"
-    "872619,872624,872632,872656,872670,872706,872710,872713,872717,872746-"
-    "872748,872777,872780-872782,872791,872804,872813,872845,872864,872870,"
-    "872872,872947-872948,872961,872974,872981,872985-872987,873004,873042,"
-    "873049,873051,873076,873087,873090,873096,873098,873100,873183,873186,"
-    "873192,873195,873210-873211,873247,873252,873256,873259,873275,873286,"
-    "873288,873343,873379-873381,873443,873521,873538-873539,873714-873715,"
-    "873718,873733,873745,873751,873767,873778,873781,873849,873856,873862,"
-    "873914,873940,873947-873948,873975-873976,873987,873998,874026-874027,"
-    "874075,874077-874078,874124-874125,874127,874156,874159,874161,874165,"
-    "874168,874170,874184,874189,874204,874223-874224,874245,874258,874262,"
-    "874270,874292-874297,874300-874301,874303,874305,874316-874318,874330,"
-    "874363,874380,874405,874421,874441,874459,874467,874473,874497,874506,"
-    "874545-874546,874561,874566,874568,874580,874619,874621,874634,874636,"
-    "874659,874673,874681,874727,874730,874743,874765-874767,874806,874816,"
-    "874848,874868,874888,874896,874909,874912,874996,875051,875069,875129,"
-    "875132,875134,875137,875151-875153,875186-875188,875190,875235-875237,"
-    "875242-875243,875249,875388,875393,875406,875411\n"]
+    ] + expected_mergeinfo_displayed
   svntest.verify.verify_outputs(
     "Redirected pg -vR doesn't match pg -vR stdout",
     pg_stdout_redir, None,
     svntest.verify.UnorderedOutput(expected_output), None)
-  # Because we are using UnorderedOutput above, this test would spuriously
-  # pass if the redirected pg output contained duplicates.  This hasn't been
-  # observed as part of issue #3721, but we might as well be thorough...
-  #
-  # ...Since we've set the same mergeinfo prop on A/B, A/C, and A/D, this
-  # means the number of lines in the redirected output of svn pg -vR should
-  # be three times the number of lines in EXPECTED_OUTPUT, adjusted for the
-  # fact the "Properties on '[A/B | A/C | A/D]'" headers  appear only once.
-  if ((len(expected_output) * 3) - 6) != len(pg_stdout_redir):
-    raise svntest.Failure("Redirected pg -vR has unexpected duplicates")
+  # (We want this check to fail if the redirected pg output contains
+  # unexpected duplicate lines, although this hasn't been observed as
+  # part of issue #3721.  We used to check separately here because the old
+  # UnorderedOutput class ignored duplicates but now it detects them.)
 
 @Issue(3852)
 def file_matching_dir_prop_reject(sbox):

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/resolve_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/resolve_tests.py?rev=1449262&r1=1449261&r2=1449262&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/resolve_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/resolve_tests.py Sat Feb 23 01:25:38 2013
@@ -584,7 +584,6 @@ def theirs_conflict_in_subdir(sbox):
 # Regression test for issue #4238 "merge -cA,B with --accept option aborts
 # if rA conflicts".
 @Issue(4238)
-@XFail()
 def multi_range_merge_with_accept(sbox):
   "multi range merge with --accept keeps going"
 

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/revert_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/revert_tests.py?rev=1449262&r1=1449261&r2=1449262&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/revert_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/revert_tests.py Sat Feb 23 01:25:38 2013
@@ -1444,7 +1444,7 @@ def revert_tree_conflicts_with_replaceme
   # Revert everything (i.e., accept "theirs-full").
   svntest.actions.run_and_verify_revert([
     wc('A/B/E'),
-    wc('A/B/E/alpha'),   # incoming
+    wc('A/B/E/alpha'),   # incoming & local
     wc('A/B/E/beta'),
     wc('A/B/E/loc_beta'),
     wc('A/B/lambda'),
@@ -1459,7 +1459,6 @@ def revert_tree_conflicts_with_replaceme
     wc('A/D/H/loc_psi'),
     wc('A/D/gamma'),
     wc('A/mu'),
-    wc('A/B/E/alpha'),
     ], '-R', wc_dir)
 
   # Remove a few unversioned files that revert left behind.