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/01/22 00:37:04 UTC

svn commit: r1436688 [10/12] - in /subversion/branches/ev2-export: ./ contrib/client-side/emacs/ notes/commit-access-templates/ subversion/bindings/javahl/native/ subversion/bindings/swig/perl/native/t/ subversion/bindings/swig/ruby/test/ subversion/in...

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=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/merge_tests.py Mon Jan 21 23:37:01 2013
@@ -1831,7 +1831,7 @@ def merge_into_missing(sbox):
   expected_status = wc.State(F_path, {
     ''      : Item(status='  ', wc_rev=1),
     'foo'   : Item(status='! ', wc_rev=2),
-    'Q'     : Item(status='! ', entry_rev='?', wc_rev='2'),
+    'Q'     : Item(status='! ', wc_rev='2'),
     # Revision is known and we can record mergeinfo
     'Q/R'      : Item(status='! ', wc_rev='3'),
     'Q/R/bar'  : Item(status='! ', wc_rev='3'),
@@ -7420,16 +7420,16 @@ def merge_away_subtrees_noninheritable_r
 
   # Now merge -c10 from A to A_COPY.
   svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
-  expected_output = wc.State('.', {
+  expected_output = wc.State('', {
     'nu': Item(status='A '),
     })
-  expected_mergeinfo_output = wc.State('.', {
+  expected_mergeinfo_output = wc.State('', {
     ''   : Item(status=' U'),
     'nu' : Item(status=' U'),
     })
-  expected_elision_output = wc.State('.', {
+  expected_elision_output = wc.State('', {
     })
-  expected_status = wc.State('.', {
+  expected_status = wc.State('', {
     ''          : Item(status=' M'),
     'nu'        : Item(status='A ', copied='+'),
     'B'         : Item(status='  '),
@@ -7479,7 +7479,7 @@ def merge_away_subtrees_noninheritable_r
   expected_skip = wc.State('.', { })
   saved_cwd = os.getcwd()
   os.chdir(A_COPY_path)
-  svntest.actions.run_and_verify_merge('.', '9', '10',
+  svntest.actions.run_and_verify_merge('', '9', '10',
                                        sbox.repo_url + '/A', None,
                                        expected_output,
                                        expected_mergeinfo_output,
@@ -7527,7 +7527,7 @@ def merge_away_subtrees_noninheritable_r
     })
   expected_elision_output = wc.State('.', {
     })
-  expected_status = wc.State('.', {
+  expected_status = wc.State('', {
     ''          : Item(status=' M'),
     'B'         : Item(status='  '),
     'mu'        : Item(status='MM'),
@@ -8100,10 +8100,10 @@ def merge_old_and_new_revs_from_renamed_
   # because /A_MOVED has renames in its history between the boundaries
   # of the requested merge range.
   expected_output = wc.State(A_COPY_path, {
-    'mu' : Item(status='G '), # mu gets touched twice
+    'mu' : Item(status='G ', prev_status='U '), # mu gets touched twice
     })
   expected_mergeinfo_output = wc.State(A_COPY_path, {
-    '' : Item(status=' G'),
+    '' : Item(status=' G', prev_status=' U'),
     })
   expected_elision_output = wc.State(A_COPY_path, {
     })
@@ -8266,7 +8266,7 @@ def merge_with_child_having_different_re
   expected_skip = wc.State(A_COPY_path, {})
   expected_output = wc.State(A_COPY_path, {
     ''   : Item(status=' U'),
-    'mu' : Item(status='G '),
+    'mu' : Item(status='G ', prev_status='G '), # Updated twice
     })
   expected_mergeinfo_output = wc.State(A_COPY_path, {
     ''   : Item(status=' U'),
@@ -8399,7 +8399,7 @@ def merge_with_child_having_different_re
   svntest.main.file_write(A_COPY_mu_path, tweaked_17th_line_1)
   expected_output = wc.State(A_COPY_path, {
     ''   : Item(status=' G'),
-    'mu' : Item(status='G '),
+    'mu' : Item(status='G ', prev_status='G '),
     })
   expected_mergeinfo_output = wc.State(A_COPY_path, {
     ''   : Item(status=' G'),
@@ -10907,9 +10907,7 @@ def merge_added_subtree(sbox):
   svntest.actions.run_and_verify_svn("", None, [],
                                      "cp", A_COPY_url + '/D2',
                                      os.path.join(A_path, "D2"))
-  actual_tree = svntest.tree.build_tree_from_wc(A_path, 0)
-  svntest.tree.compare_trees("expected disk",
-                             actual_tree, expected_disk.old_tree())
+  svntest.actions.verify_disk(A_path, expected_disk)
   svntest.actions.run_and_verify_status(A_path, expected_status)
 
   # Remove the copy artifacts
@@ -12143,7 +12141,7 @@ def subtree_source_missing_in_requested_
   expected_output = wc.State(A_COPY_path, {
     'D/H/omega' : Item(status='U '),
     'D/H/psi'   : Item(status='U '),
-    'D/H/omega' : Item(status='G '),
+    'D/H/omega' : Item(status='G ', prev_status='G '),
     })
   expected_mergeinfo_output = wc.State(A_COPY_path, {
     ''          : Item(status=' U'),
@@ -13797,11 +13795,10 @@ def subtree_gets_changes_even_if_ultimat
   # r9: Merge r3,7 from A/D/H to A_COPY/D/H, then reverse merge r7 from
   # A/D/H/psi to A_COPY/D/H/psi.
   expected_output = wc.State(H_COPY_path, {
-    'psi' : Item(status='U '),
-    'psi' : Item(status='G '),
+    'psi' : Item(status='G ', prev_status='U '), # Touched twice
     })
   expected_mergeinfo_output = wc.State(H_COPY_path, {
-    '' : Item(status=' G'),
+    '' : Item(status=' G', prev_status=' U'),
     })
   expected_elision_output = wc.State(H_COPY_path, {
     })
@@ -13849,7 +13846,7 @@ def subtree_gets_changes_even_if_ultimat
   svntest.main.run_svn(None, 'up', wc_dir)
   expected_output = wc.State(H_COPY_path, {
     'omega' : Item(status='U '),
-    'psi'   : Item(status='D '),
+    'psi'   : Item(status='D ', prev_status='U '),
     })
   expected_mergeinfo_output = wc.State(H_COPY_path, {
     '' : Item(status=' U'),
@@ -13960,7 +13957,7 @@ def no_self_referential_filtering_on_add
     })
   expected_mergeinfo_output = wc.State(A_COPY_2_path, {
     ''        : Item(status=' G'),
-    'C_MOVED' : Item(status=' G'),
+    'C_MOVED' : Item(status=' G', prev_status=' G'),
     })
   expected_elision_output = wc.State(A_COPY_2_path, {
     })
@@ -17873,11 +17870,11 @@ def merge_with_externals_with_mergeinfo(
     A_path)
 
 #----------------------------------------------------------------------
-# Test for issue #4221 'Trivial merge of a binary file with svn:keywords
-# raises a conflict'.
+# Test merging 'binary' files with keyword expansion enabled.
+# Tests issue #4221 'Trivial merge of a binary file with svn:keywords
+# raises a conflict', among other cases.
 @SkipUnless(server_has_mergeinfo)
 @Issue(4221)
-@XFail()
 def merge_binary_file_with_keywords(sbox):
   "merge binary file with keywords"
 
@@ -17885,32 +17882,66 @@ def merge_binary_file_with_keywords(sbox
   os.chdir(sbox.wc_dir)
   sbox.wc_dir = ''
 
-  # make a 'binary' file with keyword expansion enabled
-  svntest.main.file_write('foo', "Line 1 with $Revision: $ keyword.\n")
-  sbox.simple_add('foo')
-  sbox.simple_propset('svn:mime-type', 'application/octet-stream', 'foo')
-  sbox.simple_propset('svn:keywords', 'Revision', 'foo')
+  # Some binary files, and some binary files that will become text files.
+  # 'mod_src' means a content change on the branch (the merge source);
+  # 'mod_tgt' means a content change on the original (the merge target);
+  # 'to_txt' means svn:mime-type removed on the branch (the merge source).
+  file_mod_both           = 'A/B/E/alpha'
+  file_mod_src            = 'A/D/G/pi'
+  file_mod_tgt            = 'A/D/G/rho'
+  file_mod_none           = 'A/D/G/tau'
+  file_mod_both_to_txt    = 'A/B/E/beta'
+  file_mod_src_to_txt     = 'A/D/H/chi'
+  file_mod_tgt_to_txt     = 'A/D/H/psi'
+  file_mod_none_to_txt    = 'A/D/H/omega'
+  files_bin = [ file_mod_both, file_mod_src, file_mod_tgt, file_mod_none ]
+  files_txt = [ file_mod_both_to_txt, file_mod_src_to_txt,
+                file_mod_tgt_to_txt, file_mod_none_to_txt ]
+  files = files_bin + files_txt
+
+  # 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)
+    sbox.simple_propset('svn:keywords', 'Revision', f)
   sbox.simple_commit()
 
-  # branch the file
-  sbox.simple_copy('foo', 'bar')
-  sbox.simple_commit()
+  # branch the files
+  sbox.simple_repo_copy('A', 'A2')
+  sbox.simple_update()
 
-  # modify the branched version (although the bug shows even if we modify
-  # just the original version -- or neither, which is a completely
-  # degenerate 'merge' case)
-  # ### Perhaps a dir merge would behave differently from a single-file merge?
-  svntest.main.file_append('bar', "Line 2.\n")
+  # Modify the branched (source) and/or original (target) versions. Remove
+  # the svn:mime-type from the 'to_txt' files on the branch.
+  # The original bug in issue #4221 gave a conflict if we modified either
+  # version or neither (using a single-file merge test case).
+  for f in [ file_mod_both, file_mod_both_to_txt,
+             file_mod_src, file_mod_src_to_txt ]:
+    f_branch = 'A2' + f[1:]
+    svntest.main.file_append(sbox.ospath(f_branch), "Incoming mod.\n")
+  for f in [ file_mod_both, file_mod_both_to_txt,
+             file_mod_tgt, file_mod_tgt_to_txt ]:
+    svntest.main.file_append(sbox.ospath(f), "Mod on merge target.\n")
+  for f in files_txt:
+    f_branch = 'A2' + f[1:]
+    sbox.simple_propdel('svn:mime-type', f_branch)
   sbox.simple_commit()
+  sbox.simple_update()
 
   # merge back
   svntest.actions.run_and_verify_svn(
     None,
     expected_merge_output([[3,4]],
-                          ['U    foo\n',
-                           ' U   foo\n'],
-                          target='foo'),
-    [], 'merge', '^/bar', 'foo')
+                          ['C    ' + sbox.ospath(file_mod_both) + '\n',
+                           'U    ' + sbox.ospath(file_mod_src) + '\n',
+                          #'     ' + sbox.ospath(file_mod_tgt) + '\n',
+                          #'     ' + sbox.ospath(file_mod_none) + '\n',
+                           'CU   ' + sbox.ospath(file_mod_both_to_txt) + '\n',
+                           'UU   ' + sbox.ospath(file_mod_src_to_txt) + '\n',
+                           ' U   ' + sbox.ospath(file_mod_tgt_to_txt) + '\n',
+                           ' U   ' + sbox.ospath(file_mod_none_to_txt) + '\n',
+                           ' U   A\n'],
+                          text_conflicts=2),
+    [], 'merge', '^/A2', 'A')
 
 #----------------------------------------------------------------------
 # Test for issue #4155 'Merge conflict text of expanded keyword incorrect
@@ -17962,6 +17993,102 @@ def merge_conflict_when_keywords_removed
                            ' U   A2\n']),
     [], 'merge', '--accept=postpone', '^/A', 'A2')
 
+@SkipUnless(server_has_mergeinfo)
+@Issue(4139, 3274, 3503)
+def merge_target_selection(sbox):
+  "merge target selection handling"
+
+  sbox.build()
+
+  # r2
+  sbox.simple_mkdir('dir')
+  sbox.simple_add_text('\1\2\3\4\5', 'dir/binary-file')
+  sbox.simple_add_text('abcde', 'dir/text-file')
+  sbox.simple_commit()
+
+  # r3
+  sbox.simple_copy('dir', 'branch')
+  sbox.simple_commit()
+
+  # r4
+  svntest.main.file_write(sbox.ospath('dir/binary-file'), '\5\4\3\2\1')
+  sbox.simple_commit()
+
+  sbox.simple_update()
+
+  os.chdir(sbox.ospath('branch'))
+
+  # Merge the directory (no target)
+  expected_output = [
+    '--- Merging r4 into \'.\':\n',
+    'U    binary-file\n',
+    '--- Recording mergeinfo for merge of r4 into \'.\':\n',
+    ' U   .\n',
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'merge', '^/dir', '-c', '4')
+
+  svntest.main.run_svn(None, 'revert', '-R', '.')
+
+  # Merge the file (no target)
+  expected_output = [
+    '--- Merging r4 into \'binary-file\':\n',
+    'U    binary-file\n',
+    '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n',
+    ' U   binary-file\n',
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'merge', '^/dir/binary-file', '-c', '4')
+
+  svntest.main.run_svn(None, 'revert', '-R', '.')
+
+  # Merge the directory (explicit target)
+  expected_output = [
+    '--- Merging r4 into \'.\':\n',
+    'U    binary-file\n',
+    '--- Recording mergeinfo for merge of r4 into \'.\':\n',
+    ' U   .\n',
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'merge', '^/dir', '-c', '4', '.')
+
+  svntest.main.run_svn(None, 'revert', '-R', '.')
+
+  # Merge the file (explicit target)
+  expected_output = [
+    '--- Merging r4 into \'binary-file\':\n',
+    'U    binary-file\n',
+    '--- Recording mergeinfo for merge of r4 into \'binary-file\':\n',
+    ' U   binary-file\n',
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'merge', '^/dir/binary-file', '-c', '4', 'binary-file')
+
+  svntest.main.run_svn(None, 'revert', '-R', '.')
+
+  # Merge the file (wrong target)
+  expected_output = [
+    'Skipped missing target: \'.\'\n',
+    'Summary of conflicts:\n',
+    '  Skipped paths: 1\n',
+  ]
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'merge', '^/dir/binary-file', '-c', '4', '.')
+
+  svntest.main.run_svn(None, 'revert', '-R', '.')
+
+  # Merge the dir (wrong target)
+  expected_output = [
+    'Skipped \'%s\'\n' % os.path.join('binary-file', '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.actions.run_and_verify_svn(None, expected_output, [],
+                                     'merge', '^/dir', '-c', '4', 'binary-file')
+
+
 ########################################################################
 # Run the tests
 
@@ -18100,6 +18227,7 @@ test_list = [ None,
               merge_with_externals_with_mergeinfo,
               merge_binary_file_with_keywords,
               merge_conflict_when_keywords_removed,
+              merge_target_selection,
              ]
 
 if __name__ == '__main__':

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=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/patch_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/patch_tests.py Mon Jan 21 23:37:01 2013
@@ -274,14 +274,14 @@ def patch_absolute_paths(sbox):
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.tweak('A/B/E/alpha', contents=alpha_contents)
 
-  expected_status = svntest.actions.get_virginal_state('.', 1)
+  expected_status = svntest.actions.get_virginal_state('', 1)
   expected_status.tweak('A/B/E/alpha', status='M ')
 
   expected_skip = wc.State('', {
     lambda_path:  Item(verb='Skipped missing target'),
   })
 
-  svntest.actions.run_and_verify_patch('.', os.path.abspath(patch_file_path),
+  svntest.actions.run_and_verify_patch('', os.path.abspath(patch_file_path),
                                        expected_output,
                                        expected_disk,
                                        expected_status,
@@ -488,13 +488,13 @@ def patch_offset(sbox):
   expected_disk.tweak('A/mu', contents=''.join(mu_contents))
   expected_disk.tweak('iota', contents=''.join(iota_contents))
 
-  expected_status = svntest.actions.get_virginal_state('.', 1)
+  expected_status = svntest.actions.get_virginal_state('', 1)
   expected_status.tweak('A/mu', status='M ', wc_rev=2)
   expected_status.tweak('iota', status='M ', wc_rev=2)
 
   expected_skip = wc.State('', { })
 
-  svntest.actions.run_and_verify_patch('.', os.path.abspath(patch_file_path),
+  svntest.actions.run_and_verify_patch('', os.path.abspath(patch_file_path),
                                        expected_output,
                                        expected_disk,
                                        expected_status,
@@ -1097,6 +1097,7 @@ def patch_remove_empty_dirs(sbox):
     'D         %s\n' % sbox.ospath('A/B/lambda'),
     'D         %s\n' % sbox.ospath('A/B/E/alpha'),
     'D         %s\n' % sbox.ospath('A/B/E/beta'),
+    'D         %s\n' % sbox.ospath('A/B/E'),
     'D         %s\n' % sbox.ospath('A/B'),
   ]
 
@@ -2835,12 +2836,12 @@ def patch_prop_offset(sbox):
   expected_disk.tweak('iota', props = {'prop1' : prop1_content,
                                        'prop2' : prop2_content})
 
-  expected_status = svntest.actions.get_virginal_state('.', 1)
+  expected_status = svntest.actions.get_virginal_state('', 1)
   expected_status.tweak('iota', status=' M', wc_rev=2)
 
   expected_skip = wc.State('', { })
 
-  svntest.actions.run_and_verify_patch('.', os.path.abspath(patch_file_path),
+  svntest.actions.run_and_verify_patch('', os.path.abspath(patch_file_path),
                                        expected_output,
                                        expected_disk,
                                        expected_status,
@@ -3965,8 +3966,8 @@ def patch_delete_and_skip(sbox):
   expected_output = [
     'D         %s\n' % os.path.join('A', 'B', 'E', 'alpha'),
     'D         %s\n' % os.path.join('A', 'B', 'E', 'beta'),
-    'Skipped missing target: \'%s\'\n' % skipped_path,
     'D         %s\n' % os.path.join('A', 'B', 'E'),
+    'Skipped missing target: \'%s\'\n' % skipped_path,
     'Summary of conflicts:\n',
     '  Skipped paths: 1\n'
   ]
@@ -3976,7 +3977,7 @@ def patch_delete_and_skip(sbox):
   expected_disk.remove('A/B/E/beta')
   expected_disk.remove('A/B/E')
 
-  expected_status = svntest.actions.get_virginal_state('.', 1)
+  expected_status = svntest.actions.get_virginal_state('', 1)
   expected_status.tweak('A/B/E', status='D ')
   expected_status.tweak('A/B/E/alpha', status='D ')
   expected_status.tweak('A/B/E/beta', status='D ')
@@ -3985,7 +3986,7 @@ def patch_delete_and_skip(sbox):
     '',
     {skipped_path: Item(verb='Skipped missing target')})
 
-  svntest.actions.run_and_verify_patch('.', os.path.abspath(patch_file_path),
+  svntest.actions.run_and_verify_patch('', os.path.abspath(patch_file_path),
                                        expected_output,
                                        expected_disk,
                                        expected_status,
@@ -4181,9 +4182,9 @@ def patch_git_with_index_line(sbox):
 
   expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
   expected_status.add({
-      'src/'                            : Item(status='A ', wc_rev=0),
+      'src'                             : Item(status='A ', wc_rev=0),
       'src/tools'                       : Item(status='A ', wc_rev=0),
-      'src/tools/ConsoleRunner/'        : Item(status='A ', wc_rev=0),
+      'src/tools/ConsoleRunner'         : Item(status='A ', wc_rev=0),
       'src/tools/ConsoleRunner/hi.txt'  : Item(status='A ', wc_rev=0),
   })
 
@@ -4206,7 +4207,6 @@ def patch_git_with_index_line(sbox):
                                        1, # check-props
                                        1) # dry-run
 
-@XFail()
 @Issue(4273)
 def patch_change_symlink_target(sbox):
   "patch changes symlink target"
@@ -4217,30 +4217,259 @@ def patch_change_symlink_target(sbox):
   svntest.main.file_write(patch_file_path, '\n'.join([
     "Index: link",
     "===================================================================",
-    "--- iota        (revision 1)",
-    "+++ iota        (working copy)",
+    "--- link\t(revision 1)",
+    "+++ link\t(working copy)",
     "@@ -1 +1 @@",
     "-link foo",
     "\\ No newline at end of file",
-    "+link bar",
+    "+link bardame",
     "\\ No newline at end of file",
     "",
     ]))
-  
-  # r2
-  sbox.simple_add_symlink('target', 'link')
-  sbox.simple_commit()
 
-  expected_output = [
-    'M         %s\n' % sbox.ospath('link'),
+  # r2 - Try as plain text with how we encode the symlink
+  svntest.main.file_write(sbox.ospath('link'), 'link foo')
+  sbox.simple_add('link')
+
+  expected_output = svntest.wc.State(wc_dir, {
+    'link'       : Item(verb='Adding'),
+  })
+  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+                                        None, None, wc_dir)
+
+  patch_output = [
+    'U         %s\n' % sbox.ospath('link'),
   ]
 
-  # This currently fails.
+  svntest.actions.run_and_verify_svn(None, patch_output, [],
+                                     'patch', patch_file_path, wc_dir)
+
+  # r3 - Store result
+  expected_output = svntest.wc.State(wc_dir, {
+    'link'       : Item(verb='Sending'),
+  })
+  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+                                        None, None, wc_dir)
+
+  # r4 - Now as symlink
+  sbox.simple_rm('link')
+  sbox.simple_add_symlink('foo', 'link')
+  expected_output = svntest.wc.State(wc_dir, {
+    'link'       : Item(verb='Replacing'),
+  })
+  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+                                        None, None, wc_dir)
+
+  svntest.actions.run_and_verify_svn(None, patch_output, [],
+                                     'patch', patch_file_path, wc_dir)
+
   # TODO: when it passes, verify that the on-disk 'link' is correct ---
   #       symlink to 'bar' (or "link bar" on non-HAVE_SYMLINK platforms)
-  svntest.actions.run_and_verify_svn(None, "U *link", [],
+
+  # BH: easy check for node type: a non symlink would show as obstructed
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({
+    'link'              : Item(status='M ', wc_rev='4'),
+  })
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
+def patch_replace_dir_with_file_and_vv(sbox):
+  "replace dir with file and file with dir"
+  sbox.build(read_only=True)
+
+  patch_file_path = make_patch_path(sbox)
+  svntest.main.file_write(patch_file_path, ''.join([
+  # Delete all files in D and descendants to delete D itself
+    "Index: A/D/G/pi\n",
+    "===================================================================\n",
+    "--- A/D/G/pi\t(revision 1)\n",
+    "+++ A/D/G/pi\t(working copy)\n",
+    "@@ -1 +0,0 @@\n",
+    "-This is the file 'pi'.\n",
+    "Index: A/D/G/rho\n",
+    "===================================================================\n",
+    "--- A/D/G/rho\t(revision 1)\n",
+    "+++ A/D/G/rho\t(working copy)\n",
+    "@@ -1 +0,0 @@\n",
+    "-This is the file 'rho'.\n",
+    "Index: A/D/G/tau\n",
+    "===================================================================\n",
+    "--- A/D/G/tau\t(revision 1)\n",
+    "+++ A/D/G/tau\t(working copy)\n",
+    "@@ -1 +0,0 @@\n",
+    "-This is the file 'tau'.\n",
+    "Index: A/D/H/chi\n",
+    "===================================================================\n",
+    "--- A/D/H/chi\t(revision 1)\n",
+    "+++ A/D/H/chi\t(working copy)\n",
+    "@@ -1 +0,0 @@\n",
+    "-This is the file 'chi'.\n",
+    "Index: A/D/H/omega\n",
+    "===================================================================\n",
+    "--- A/D/H/omega\t(revision 1)\n",
+    "+++ A/D/H/omega\t(working copy)\n",
+    "@@ -1 +0,0 @@\n",
+    "-This is the file 'omega'.\n",
+    "Index: A/D/H/psi\n",
+    "===================================================================\n",
+    "--- A/D/H/psi\t(revision 1)\n",
+    "+++ A/D/H/psi\t(working copy)\n",
+    "@@ -1 +0,0 @@\n",
+    "-This is the file 'psi'.\n",
+    "Index: A/D/gamma\n",
+    "===================================================================\n",
+    "--- A/D/gamma\t(revision 1)\n",
+    "+++ A/D/gamma\t(working copy)\n",
+    "@@ -1 +0,0 @@\n",
+    "-This is the file 'gamma'.\n",
+  # Delete iota
+    "Index: iota\n",
+    "===================================================================\n",
+    "--- iota\t(revision 1)\n",
+    "+++ iota\t(working copy)\n",
+    "@@ -1 +0,0 @@\n",
+    "-This is the file 'iota'.\n",
+
+  # Add A/D as file
+    "Index: A/D\n",
+    "===================================================================\n",
+    "--- A/D\t(revision 0)\n",
+    "+++ A/D\t(working copy)\n",
+    "@@ -0,0 +1 @@\n",
+    "+New file\n",
+    "\ No newline at end of file\n",
+
+  # Add iota as directory
+    "Index: iota\n",
+    "===================================================================\n",
+    "--- iota\t(revision 1)\n",
+    "+++ iota\t(working copy)\n",
+    "\n",
+    "Property changes on: iota\n",
+    "___________________________________________________________________\n",
+    "Added: k\n",
+    "## -0,0 +1 ##\n",
+    "+v\n",
+    "\ No newline at end of property\n",
+  ]))
+
+  expected_output = [
+    'D         %s\n' % sbox.ospath('A/D/G/pi'),
+    'D         %s\n' % sbox.ospath('A/D/G/rho'),
+    'D         %s\n' % sbox.ospath('A/D/G/tau'),
+    'D         %s\n' % sbox.ospath('A/D/G'),
+    'D         %s\n' % sbox.ospath('A/D/H/chi'),
+    'D         %s\n' % sbox.ospath('A/D/H/omega'),
+    'D         %s\n' % sbox.ospath('A/D/H/psi'),
+    'D         %s\n' % sbox.ospath('A/D/H'),
+    'D         %s\n' % sbox.ospath('A/D/gamma'),
+    'D         %s\n' % sbox.ospath('A/D'),
+    'D         %s\n' % sbox.ospath('iota'),
+    'A         %s\n' % sbox.ospath('A/D'),
+    'A         %s\n' % sbox.ospath('iota'),
+  ]
+
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'patch', patch_file_path, sbox.wc_dir)
+
+@Issue(4297)
+def single_line_mismatch(sbox):
+  "single line replacement mismatch"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  patch_file_path = make_patch_path(sbox)
+  svntest.main.file_write(patch_file_path, ''.join([
+    "Index: test\n",
+    "===================================================================\n",
+    "--- test\t(revision 1)\n",
+    "+++ test\t(working copy)\n",
+    "@@ -1 +1 @@\n",
+    "-foo\n",
+    "\\ No newline at end of file\n",
+    "+bar\n",
+    "\\ No newline at end of file\n"
+    ]))
+
+  # r2 - Try as plain text with how we encode the symlink
+  svntest.main.file_write(sbox.ospath('test'), 'line')
+  sbox.simple_add('test')
+  sbox.simple_commit()
+
+  # And now this patch should fail, as 'line' doesn't equal 'foo'
+  # But yet it shows up as deleted instead of conflicted
+  expected_output = [
+    'C         %s\n' % sbox.ospath('test'),
+    '>         rejected hunk @@ -1,1 +1,1 @@\n',
+    'Summary of conflicts:\n',
+    '  Text conflicts: 1\n',
+  ]
+
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'patch', patch_file_path, wc_dir)
+
+@Issue(3644)
+def patch_empty_file(sbox):
+  "apply a patch to an empty file"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  patch_file_path = make_patch_path(sbox)
+  svntest.main.file_write(patch_file_path, ''.join([
+  # patch a file containing just '\n' to 'replacement\n'
+    "Index: lf.txt\n",
+    "===================================================================\n",
+    "--- lf.txt\t(revision 2)\n",
+    "+++ lf.txt\t(working copy)\n",
+    "@@ -1 +1 @@\n",
+    "\n"
+    "+replacement\n",
+
+  # patch a new file 'new.txt\n'
+    "Index: new.txt\n",
+    "===================================================================\n",
+    "--- new.txt\t(revision 0)\n",
+    "+++ new.txt\t(working copy)\n",
+    "@@ -0,0 +1 @@\n",
+    "+new file\n",
+
+  # patch a file containing 0 bytes to 'replacement\n'
+    "Index: empty.txt\n",
+    "===================================================================\n",
+    "--- empty.txt\t(revision 2)\n",
+    "+++ empty.txt\t(working copy)\n",
+    "@@ -0,0 +1 @@\n",
+    "+replacement\n",
+  ]))
+
+  sbox.simple_add_text('', 'empty.txt')
+  sbox.simple_add_text('\n', 'lf.txt')
+  sbox.simple_commit()
+
+  expected_output = [
+    'U         %s\n' % sbox.ospath('lf.txt'),
+    'A         %s\n' % sbox.ospath('new.txt'),
+    'U         %s\n' % sbox.ospath('empty.txt'),
+    # Not sure if this line is necessary, but it doesn't hurt
+    '>         applied hunk @@ -0,0 +1,1 @@ with offset 0\n',
+  ]
+
+  # Current result: lf.txt patched ok, new created, empty succeeds with offset.
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
                                      'patch', patch_file_path, wc_dir)
 
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.add({
+    'lf.txt'            : Item(contents="\n"),
+    'new.txt'           : Item(contents="new file\n"),
+    'empty.txt'         : Item(contents="replacement\n"),
+  })
+
+  svntest.actions.verify_disk(wc_dir, expected_disk)
+
+
+
 ########################################################################
 #Run the tests
 
@@ -4287,6 +4516,9 @@ test_list = [ None,
               patch_add_and_delete,
               patch_git_with_index_line,
               patch_change_symlink_target,
+              patch_replace_dir_with_file_and_vv,
+              single_line_mismatch,
+              patch_empty_file,
             ]
 
 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=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/prop_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/prop_tests.py Mon Jan 21 23:37:01 2013
@@ -92,11 +92,7 @@ def make_local_props(sbox):
   # Read the real disk tree.  Notice we are passing the (normally
   # disabled) "load props" flag to this routine.  This will run 'svn
   # proplist' on every item in the working copy!
-  actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, 1)
-
-  # Compare actual vs. expected disk trees.
-  svntest.tree.compare_trees("disk", actual_disk_tree,
-                             expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk, True)
 
   # Edit without actually changing the property
   svntest.main.use_editor('identity')
@@ -1319,9 +1315,7 @@ def props_on_replaced_file(sbox):
   # check that the replaced file has no properties
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.tweak('iota', contents="some mod")
-  actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, 1)
-  svntest.tree.compare_trees("disk", actual_disk_tree,
-                             expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
 
   # now add a new property to iota
   sbox.simple_propset('red', 'mojo', 'iota')
@@ -1329,9 +1323,7 @@ def props_on_replaced_file(sbox):
 
   # What we expect the disk tree to look like:
   expected_disk.tweak('iota', props={'red' : 'mojo', 'groovy' : 'baby'})
-  actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, 1)
-  svntest.tree.compare_trees("disk", actual_disk_tree,
-                             expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
 
 #----------------------------------------------------------------------
 
@@ -1781,9 +1773,7 @@ def rm_of_replaced_file(sbox):
   expected_disk.tweak('iota', props={'red': 'rojo', 'blue': 'lagoon'})
   expected_disk.tweak('A/mu', props={'red': 'rojo', 'blue': 'lagoon'},
                       contents="This is the file 'iota'.\n")
-  actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, 1)
-  svntest.tree.compare_trees("disk", actual_disk_tree,
-                             expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
 
   # Remove the copy. This should leave the original locally-deleted mu,
   # which should have no properties.
@@ -2012,17 +2002,13 @@ def obstructed_subdirs(sbox):
 
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.tweak('A/C', props={'red': 'blue'})
-  actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, load_props=True)
-  svntest.tree.compare_trees("disk", actual_disk_tree,
-                             expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
 
   # Remove the subdir from disk, and validate the status
   svntest.main.safe_rmtree(C_path)
 
   expected_disk.remove('A/C')
-  actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, load_props=True)
-  svntest.tree.compare_trees("disk", actual_disk_tree,
-                             expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
 
   expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
   expected_status.tweak('A/C', status='!M', wc_rev='1')
@@ -2035,9 +2021,7 @@ def obstructed_subdirs(sbox):
   expected_disk.add({'A/C': Item(contents='', props={'red': 'blue'})})
   expected_status.tweak('A/C', status='~M', wc_rev='1')
 
-  actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, load_props=True)
-  svntest.tree.compare_trees("disk", actual_disk_tree,
-                             expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
 
 
   svntest.actions.run_and_verify_status(wc_dir, expected_status)

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=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/resolve_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/resolve_tests.py Mon Jan 21 23:37:01 2013
@@ -44,6 +44,13 @@ Wimp = svntest.testcase.Wimp_deco
 from merge_tests import set_up_branch
 from merge_tests import expected_merge_output
 
+
+######################################################################
+# Tests
+#
+#   Each test must return on success or raise on failure.
+
+#----------------------------------------------------------------------
 # 'svn resolve --accept [ base | mine-full | theirs-full ]' was segfaulting
 # on 1.6.x.  Prior to this test, the bug was only caught by the Ruby binding
 # tests, see http://svn.haxx.se/dev/archive-2010-01/0088.shtml.
@@ -243,6 +250,7 @@ def prop_conflict_resolution(sbox):
                                      [], # Prop deleted
                                      ['incoming-conflict\n'])
 
+#----------------------------------------------------------------------
 @SkipUnless(svntest.main.is_posix_os)
 def auto_resolve_executable_file(sbox):
   "resolve file with executable bit set"
@@ -271,6 +279,334 @@ def auto_resolve_executable_file(sbox):
   if mode != os.stat(sbox.ospath('iota'))[stat.ST_MODE]:
     raise svntest.Failure
 
+#----------------------------------------------------------------------
+def resolved_on_wc_root(sbox):
+  "resolved on working copy root"
+
+  sbox.build()
+  wc = sbox.wc_dir
+
+  i = os.path.join(wc, 'iota')
+  B = os.path.join(wc, 'A', 'B')
+  g = os.path.join(wc, 'A', 'D', 'gamma')
+
+  # Create some conflicts...
+  # Commit mods
+  svntest.main.file_append(i, "changed iota.\n")
+  svntest.main.file_append(g, "changed gamma.\n")
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'propset', 'foo', 'foo-val', B)
+
+  expected_output = svntest.wc.State(wc, {
+      'iota'              : Item(verb='Sending'),
+      'A/B'               : Item(verb='Sending'),
+      'A/D/gamma'         : Item(verb='Sending'),
+    })
+
+  expected_status = svntest.actions.get_virginal_state(wc, 1)
+  expected_status.tweak('iota', 'A/B', 'A/D/gamma', wc_rev = 2)
+
+  svntest.actions.run_and_verify_commit(wc,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        wc)
+
+  # Go back to rev 1
+  expected_output = svntest.wc.State(wc, {
+    'iota'              : Item(status='U '),
+    'A/B'               : Item(status=' U'),
+    'A/D/gamma'         : Item(status='U '),
+  })
+  expected_status = svntest.actions.get_virginal_state(wc, 1)
+  expected_disk = svntest.main.greek_state.copy()
+  svntest.actions.run_and_verify_update(wc,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None, None, None, False,
+                                        '-r1', wc)
+
+  # Deletions so that the item becomes unversioned and
+  # will have a tree-conflict upon update.
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'rm', i, B, g)
+
+  # Update so that conflicts appear
+  expected_output = svntest.wc.State(wc, {
+    'iota'              : Item(status='  ', treeconflict='C'),
+    'A/B'               : Item(status='  ', treeconflict='C'),
+    'A/D/gamma'         : Item(status='  ', treeconflict='C'),
+  })
+
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.remove('iota',
+                       'A/B',
+                       'A/B/lambda',
+                       'A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
+                       'A/B/F',
+                       'A/D/gamma')
+
+  expected_status = svntest.actions.get_virginal_state(wc, 2)
+  expected_status.tweak('iota', 'A/B', 'A/D/gamma',
+                        status='D ', treeconflict='C')
+  expected_status.tweak('A/B/lambda', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
+                        'A/B/F', status='D ')
+  svntest.actions.run_and_verify_update(wc,
+                                        expected_output,
+                                        expected_disk,
+                                        None,
+                                        None, None, None, None, None, False,
+                                        wc)
+  svntest.actions.run_and_verify_unquiet_status(wc, expected_status)
+
+  # Resolve recursively
+  svntest.actions.run_and_verify_resolved([i, B, g], '--depth=infinity', wc)
+
+  expected_status.tweak('iota', 'A/B', 'A/D/gamma', treeconflict=None)
+  svntest.actions.run_and_verify_unquiet_status(wc, expected_status)
+
+#----------------------------------------------------------------------
+def resolved_on_deleted_item(sbox):
+  "resolved on deleted item"
+
+  sbox.build()
+  wc = sbox.wc_dir
+
+  A = os.path.join(wc, 'A',)
+  B = os.path.join(wc, 'A', 'B')
+  g = os.path.join(wc, 'A', 'D', 'gamma')
+  A2 = os.path.join(wc, 'A2')
+  B2 = os.path.join(A2, 'B')
+  g2 = os.path.join(A2, 'D', 'gamma')
+
+  A_url = sbox.repo_url + '/A'
+  A2_url = sbox.repo_url + '/A2'
+
+  # make a copy of A
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'cp', A_url, A2_url, '-m', 'm')
+
+  expected_output = svntest.wc.State(wc, {
+    'A2'                : Item(status='A '),
+    'A2/B'              : Item(status='A '),
+    'A2/B/lambda'       : Item(status='A '),
+    'A2/B/E'            : Item(status='A '),
+    'A2/B/E/alpha'      : Item(status='A '),
+    'A2/B/E/beta'       : Item(status='A '),
+    'A2/B/F'            : Item(status='A '),
+    'A2/mu'             : Item(status='A '),
+    'A2/C'              : Item(status='A '),
+    'A2/D'              : Item(status='A '),
+    'A2/D/gamma'        : Item(status='A '),
+    'A2/D/G'            : Item(status='A '),
+    'A2/D/G/pi'         : Item(status='A '),
+    'A2/D/G/rho'        : Item(status='A '),
+    'A2/D/G/tau'        : Item(status='A '),
+    'A2/D/H'            : Item(status='A '),
+    'A2/D/H/chi'        : Item(status='A '),
+    'A2/D/H/omega'      : Item(status='A '),
+    'A2/D/H/psi'        : Item(status='A '),
+  })
+
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.add({
+    'A2/mu'             : Item(contents="This is the file 'mu'.\n"),
+    'A2/D/gamma'        : Item(contents="This is the file 'gamma'.\n"),
+    'A2/D/H/psi'        : Item(contents="This is the file 'psi'.\n"),
+    'A2/D/H/omega'      : Item(contents="This is the file 'omega'.\n"),
+    'A2/D/H/chi'        : Item(contents="This is the file 'chi'.\n"),
+    'A2/D/G/rho'        : Item(contents="This is the file 'rho'.\n"),
+    'A2/D/G/pi'         : Item(contents="This is the file 'pi'.\n"),
+    'A2/D/G/tau'        : Item(contents="This is the file 'tau'.\n"),
+    'A2/B/lambda'       : Item(contents="This is the file 'lambda'.\n"),
+    'A2/B/F'            : Item(),
+    'A2/B/E/beta'       : Item(contents="This is the file 'beta'.\n"),
+    'A2/B/E/alpha'      : Item(contents="This is the file 'alpha'.\n"),
+    'A2/C'              : Item(),
+  })
+
+  expected_status = svntest.actions.get_virginal_state(wc, 2)
+  expected_status.add({
+    'A2'                : Item(),
+    'A2/B'              : Item(),
+    'A2/B/lambda'       : Item(),
+    'A2/B/E'            : Item(),
+    'A2/B/E/alpha'      : Item(),
+    'A2/B/E/beta'       : Item(),
+    'A2/B/F'            : Item(),
+    'A2/mu'             : Item(),
+    'A2/C'              : Item(),
+    'A2/D'              : Item(),
+    'A2/D/gamma'        : Item(),
+    'A2/D/G'            : Item(),
+    'A2/D/G/pi'         : Item(),
+    'A2/D/G/rho'        : Item(),
+    'A2/D/G/tau'        : Item(),
+    'A2/D/H'            : Item(),
+    'A2/D/H/chi'        : Item(),
+    'A2/D/H/omega'      : Item(),
+    'A2/D/H/psi'        : Item(),
+  })
+  expected_status.tweak(status='  ', wc_rev='2')
+
+  svntest.actions.run_and_verify_update(wc,
+                                        expected_output,
+                                        expected_disk,
+                                        expected_status,
+                                        None, None, None, None, None, False,
+                                        wc)
+
+  # Create some conflicts...
+
+  # Modify the paths in the one directory.
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'propset', 'foo', 'foo-val', B)
+  svntest.main.file_append(g, "Modified gamma.\n")
+
+  expected_output = svntest.wc.State(wc, {
+      'A/B'               : Item(verb='Sending'),
+      'A/D/gamma'         : Item(verb='Sending'),
+    })
+
+  expected_status.tweak('A/B', 'A/D/gamma', wc_rev='3')
+
+  svntest.actions.run_and_verify_commit(wc,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        wc)
+
+  # Delete the paths in the second directory.
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'rm', B2, g2)
+
+  expected_output = svntest.wc.State(wc, {
+      'A2/B'              : Item(verb='Deleting'),
+      'A2/D/gamma'        : Item(verb='Deleting'),
+    })
+
+  expected_status.remove('A2/B', 'A2/B/lambda',
+                         'A2/B/E', 'A2/B/E/alpha', 'A2/B/E/beta',
+                         'A2/B/F',
+                         'A2/D/gamma')
+
+  svntest.actions.run_and_verify_commit(wc,
+                                        expected_output,
+                                        expected_status,
+                                        None,
+                                        A2)
+
+  # Now merge A to A2, creating conflicts...
+
+  expected_output = svntest.wc.State(A2, {
+      'B'                 : Item(status='  ', treeconflict='C'),
+      'D/gamma'           : Item(status='  ', treeconflict='C'),
+    })
+  expected_mergeinfo_output = svntest.wc.State(A2, {
+      '' : Item(status=' U')
+    })
+  expected_elision_output = svntest.wc.State(A2, {
+    })
+  expected_disk = svntest.wc.State('', {
+      'mu'                : Item(contents="This is the file 'mu'.\n"),
+      'D'                 : Item(),
+      'D/H'               : Item(),
+      'D/H/psi'           : Item(contents="This is the file 'psi'.\n"),
+      'D/H/omega'         : Item(contents="This is the file 'omega'.\n"),
+      'D/H/chi'           : Item(contents="This is the file 'chi'.\n"),
+      'D/G'               : Item(),
+      'D/G/rho'           : Item(contents="This is the file 'rho'.\n"),
+      'D/G/pi'            : Item(contents="This is the file 'pi'.\n"),
+      'D/G/tau'           : Item(contents="This is the file 'tau'.\n"),
+      'C'                 : Item(),
+    })
+
+  expected_skip = svntest.wc.State(wc, {
+    })
+
+  expected_status = svntest.wc.State(A2, {
+    ''                  : Item(status=' M', wc_rev='2'),
+    'D'                 : Item(status='  ', wc_rev='2'),
+    'D/gamma'           : Item(status='! ', treeconflict='C'),
+    'D/G'               : Item(status='  ', wc_rev='2'),
+    'D/G/pi'            : Item(status='  ', wc_rev='2'),
+    'D/G/rho'           : Item(status='  ', wc_rev='2'),
+    'D/G/tau'           : Item(status='  ', wc_rev='2'),
+    'D/H'               : Item(status='  ', wc_rev='2'),
+    'D/H/chi'           : Item(status='  ', wc_rev='2'),
+    'D/H/omega'         : Item(status='  ', wc_rev='2'),
+    'D/H/psi'           : Item(status='  ', wc_rev='2'),
+    'B'                 : Item(status='! ', treeconflict='C'),
+    'mu'                : Item(status='  ', wc_rev='2'),
+    'C'                 : Item(status='  ', wc_rev='2'),
+  })
+
+  svntest.actions.run_and_verify_merge(A2, None, None, A_url, None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk, None, expected_skip,
+                                       None, dry_run = False)
+  svntest.actions.run_and_verify_unquiet_status(A2, expected_status)
+
+  # Now resolve by recursing on the working copy root.
+  svntest.actions.run_and_verify_resolved([B2, g2], '--depth=infinity', wc)
+
+  expected_status.remove('B', 'D/gamma')
+  svntest.actions.run_and_verify_unquiet_status(A2, expected_status)
+
+#----------------------------------------------------------------------
+
+def theirs_conflict_in_subdir(sbox):
+  "resolve to 'theirs-conflict' in sub-directory"
+
+  sbox.build()
+  wc = sbox.wc_dir
+  wc2 = sbox.add_wc_path('wc2')
+  svntest.actions.duplicate_dir(sbox.wc_dir, wc2)
+
+  alpha_path = os.path.join(wc, 'A', 'B', 'E', 'alpha')
+  alpha_path2 = os.path.join(wc2, 'A', 'B', 'E', 'alpha')
+
+  svntest.main.file_append(alpha_path, "Modified alpha.\n")
+  svntest.main.run_svn(None, 'ci', '-m', 'logmsg', wc)
+
+  svntest.main.file_append(alpha_path2, "Modified alpha, too.\n")
+  svntest.main.run_svn(None, 'up', wc2)
+
+  svntest.actions.run_and_verify_resolve([alpha_path2],
+                                         '--accept=theirs-conflict',
+                                         alpha_path2)
+
+#----------------------------------------------------------------------
+
+# 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"
+
+  sbox.build()
+  os.chdir(sbox.wc_dir)
+  sbox.wc_dir = ''
+
+  # Commit some changes
+  for c in [2, 3, 4]:
+    svntest.main.file_append('iota', 'Change ' + str(c) + '\n')
+    sbox.simple_commit()
+
+  sbox.simple_update(revision=1)
+
+  # The bug: with a request to merge -c4 then -c3, it merges -c4 which
+  # conflicts then auto-resolves the conflict, then errors out with
+  # 'svn: E155035: Can't merge into conflicted node 'iota'.
+  # ### We need more checking of the result to make this test robust, since
+  #     it may not always just error out.
+  svntest.main.run_svn(None, 'merge', '-c4,3', '^/iota', 'iota',
+                       '--accept=theirs-conflict')
+
 
 ########################################################################
 # Run the tests
@@ -280,6 +616,10 @@ test_list = [ None,
               automatic_conflict_resolution,
               prop_conflict_resolution,
               auto_resolve_executable_file,
+              resolved_on_wc_root,
+              resolved_on_deleted_item,
+              theirs_conflict_in_subdir,
+              multi_range_merge_with_accept,
              ]
 
 if __name__ == '__main__':

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=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/revert_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/revert_tests.py Mon Jan 21 23:37:01 2013
@@ -79,8 +79,7 @@ def revert_replacement_with_props(sbox, 
   expected_disk.tweak('A/D/G/rho',
                       props={ 'svn:eol-style': 'LF' })
 
-  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
-  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk, True)
 
   # Commit props
   expected_output = svntest.wc.State(wc_dir, {
@@ -124,8 +123,7 @@ def revert_replacement_with_props(sbox, 
   expected_disk.tweak('A/D/G/rho',
                       contents="This is the file 'pi'.\n",
                       props=props)
-  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
-  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
 
   # Now revert
   expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
@@ -144,8 +142,7 @@ def revert_replacement_with_props(sbox, 
                       props={ 'phony-prop': '*' })
   expected_disk.tweak('A/D/G/rho',
                       props={ 'svn:eol-style': 'LF' })
-  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
-  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
 
 
 
@@ -511,9 +508,8 @@ def revert_file_merge_replace_with_histo
   expected_status.tweak('A/D/G/rho', copied=None, status='  ', wc_rev=3)
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
-  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
   expected_disk.tweak('A/D/G/rho', contents="new rho\n")
-  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
 
   # Make sure the revert removed the copy from information.
   expected_infos = [
@@ -578,8 +574,7 @@ def revert_after_second_replace(sbox):
   # Check disk status
   expected_disk = svntest.main.greek_state.copy()
   expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
-  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
-  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
 
 
 #----------------------------------------------------------------------
@@ -1594,8 +1589,7 @@ def revert_with_unversioned_targets(sbox
   expected_disk.add({
     'A/D/H/delta': Item(delta_contents),
   })
-  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
-  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())
+  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
 
 def revert_nonexistent(sbox):
   'svn revert -R nonexistent'
@@ -1636,8 +1630,10 @@ def revert_obstructing_wc(sbox):
     # A is not versioned but exists
   })
 
+  # Use expected_status.old_tree() to avoid doing an entries comparion
   svntest.actions.run_and_verify_update(wc_dir,
-                                        expected_output, None, expected_status,
+                                        expected_output, None,
+                                        expected_status.old_tree(),
                                         None, None, None,
                                         None, None, None,
                                         wc_dir, '--set-depth', 'infinity')

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/special_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/special_tests.py?rev=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/special_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/special_tests.py Mon Jan 21 23:37:01 2013
@@ -1182,7 +1182,7 @@ def incoming_symlink_changes(sbox):
   # Update back to r2, to prepare some local changes
   expected_output = svntest.wc.State(wc_dir, {
     # s-replace is D + A
-    's-replace'         : Item(status='A '),
+    's-replace'         : Item(status='A ', prev_status='D '),
     's-in-place'        : Item(status='U '),
     's-reverse'         : Item(status=' U'),
     's-type'            : Item(status=' U'),
@@ -1203,7 +1203,8 @@ def incoming_symlink_changes(sbox):
   sbox.simple_propset('x', 'y', 's-replace', 's-in-place', 's-reverse', 's-type')
 
   expected_output = svntest.wc.State(wc_dir, {
-    's-replace'         : Item(status='  ', treeconflict='A'),
+    's-replace'         : Item(prev_status = '  ', prev_treeconflict='C',
+                               status='  ', treeconflict='A'),
     's-in-place'        : Item(status='U '),
     's-reverse'         : Item(status='  ', treeconflict='C'),
     's-type'            : Item(status='  ', treeconflict='C'),

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/stat_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/stat_tests.py?rev=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/stat_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/stat_tests.py Mon Jan 21 23:37:01 2013
@@ -1988,6 +1988,40 @@ def status_unversioned_dir(sbox):
   svntest.actions.run_and_verify_svn2(None, [], expected_err, 0,
                                       "status", "/")
 
+def status_case_changed(sbox):
+  "status reporting on case changed nodes directly"
+
+  sbox.build(read_only = True)
+  wc_dir = sbox.wc_dir
+
+  os.rename(sbox.ospath('iota'), sbox.ospath('iOTA'))
+
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({
+    'iOTA'         : Item(status='? '),
+  })
+  expected_status.tweak('iota', status='! ')
+
+  # First run status on the directory
+  svntest.actions.run_and_verify_unquiet_status(wc_dir,
+                                                expected_status)
+
+  # Now on the missing iota directly, which should give the same
+  # result, even on case insenstive filesystems
+  expected_status = svntest.wc.State(wc_dir, {
+    'iota'         : Item(status='! ', wc_rev=1),
+  })
+  svntest.actions.run_and_verify_unquiet_status(sbox.ospath('iota'),
+                                                expected_status)
+
+  # And on the unversioned iOTA
+  expected_status = svntest.wc.State(wc_dir, {
+    'iOTA'         : Item(status='? '),
+  })
+  svntest.actions.run_and_verify_unquiet_status(sbox.ospath('iOTA'),
+                                                expected_status)
+
+
 ########################################################################
 # Run the tests
 
@@ -2033,6 +2067,7 @@ test_list = [ None,
               modified_modulo_translation,
               status_not_present,
               status_unversioned_dir,
+              status_case_changed,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/svnadmin_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/svnadmin_tests.py?rev=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/svnadmin_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/svnadmin_tests.py Mon Jan 21 23:37:01 2013
@@ -230,14 +230,8 @@ def load_and_verify_dumpstream(sbox, exp
                                          "update", "-r%s" % (rev+1),
                                          sbox.wc_dir)
 
-      wc_tree = svntest.tree.build_tree_from_wc(sbox.wc_dir, check_props)
-      rev_tree = revs[rev].old_tree()
-
-      try:
-        svntest.tree.compare_trees("rev/disk", rev_tree, wc_tree)
-      except svntest.tree.SVNTreeError:
-        svntest.verify.display_trees(None, 'WC TREE', wc_tree, rev_tree)
-        raise
+      rev_tree = revs[rev]
+      svntest.actions.verify_disk(sbox.wc_dir, rev_tree, check_props)
 
 def load_dumpstream(sbox, dump, *varargs):
   "Load dump text without verification."
@@ -554,11 +548,22 @@ def verify_windows_paths_in_repos(sbox):
 
   exit_code, output, errput = svntest.main.run_svnadmin("verify",
                                                         sbox.repo_dir)
-  svntest.verify.compare_and_display_lines(
-    "Error while running 'svnadmin verify'.",
-    'STDERR', ["* Verified revision 0.\n",
-               "* Verified revision 1.\n",
-               "* Verified revision 2.\n"], errput)
+
+  # unfortunately, FSFS needs to do more checks than BDB resulting in
+  # different progress output
+  if svntest.main.is_fs_type_fsfs:
+    svntest.verify.compare_and_display_lines(
+      "Error while running 'svnadmin verify'.",
+      'STDERR', ["* Verifying global structure ...\n",
+                 "* Verified revision 0.\n",
+                 "* Verified revision 1.\n",
+                 "* Verified revision 2.\n"], errput)
+  else:
+    svntest.verify.compare_and_display_lines(
+      "Error while running 'svnadmin verify'.",
+      'STDERR', ["* Verified revision 0.\n",
+                 "* Verified revision 1.\n",
+                 "* Verified revision 2.\n"], errput)
 
 #----------------------------------------------------------------------
 

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/svntest/actions.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/svntest/actions.py?rev=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/svntest/actions.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/svntest/actions.py Mon Jan 21 23:37:01 2013
@@ -455,8 +455,6 @@ def run_and_verify_checkout2(do_remove,
 
   if isinstance(output_tree, wc.State):
     output_tree = output_tree.old_tree()
-  if isinstance(disk_tree, wc.State):
-    disk_tree = disk_tree.old_tree()
 
   # Remove dir if it's already there, unless this is a forced checkout.
   # In that case assume we want to test a forced checkout's toleration
@@ -478,17 +476,10 @@ def run_and_verify_checkout2(do_remove,
     _log_tree_state("ACTUAL OUTPUT TREE:", actual, wc_dir_name)
     raise
 
-  # Create a tree by scanning the working copy
-  actual = tree.build_tree_from_wc(wc_dir_name)
-
-  # Verify expected disk against actual disk.
-  try:
-    tree.compare_trees("disk", actual, disk_tree,
-                       singleton_handler_a, a_baton,
-                       singleton_handler_b, b_baton)
-  except tree.SVNTreeUnequal:
-    _log_tree_state("ACTUAL DISK TREE:", actual, wc_dir_name)
-    raise
+  if disk_tree:
+    verify_disk(wc_dir_name, disk_tree, False,
+                singleton_handler_a, a_baton,
+                singleton_handler_b, b_baton)
 
 def run_and_verify_checkout(URL, wc_dir_name, output_tree, disk_tree,
                             singleton_handler_a = None,
@@ -759,10 +750,6 @@ def verify_update(actual_output,
     mergeinfo_output_tree = mergeinfo_output_tree.old_tree()
   if isinstance(elision_output_tree, wc.State):
     elision_output_tree = elision_output_tree.old_tree()
-  if isinstance(disk_tree, wc.State):
-    disk_tree = disk_tree.old_tree()
-  if isinstance(status_tree, wc.State):
-    status_tree = status_tree.old_tree()
 
   # Verify actual output against expected output.
   if output_tree:
@@ -794,27 +781,35 @@ def verify_update(actual_output,
 
   # Create a tree by scanning the working copy, and verify it
   if disk_tree:
-    actual_disk = tree.build_tree_from_wc(wc_dir_name, check_props)
-    try:
-      tree.compare_trees("disk", actual_disk, disk_tree,
-                         singleton_handler_a, a_baton,
-                         singleton_handler_b, b_baton)
-    except tree.SVNTreeUnequal:
-      _log_tree_state("EXPECTED DISK TREE:", disk_tree)
-      _log_tree_state("ACTUAL DISK TREE:", actual_disk)
-      raise
+    verify_disk(wc_dir_name, disk_tree, check_props,
+                singleton_handler_a, a_baton,
+                singleton_handler_b, b_baton)
 
   # Verify via 'status' command too, if possible.
   if status_tree:
     run_and_verify_status(wc_dir_name, status_tree)
 
 
-def verify_disk(wc_dir_name, disk_tree, check_props=False):
+def verify_disk(wc_dir_name, disk_tree, check_props=False,
+                singleton_handler_a = None, a_baton = None,
+                singleton_handler_b = None, b_baton = None):
   """Verify WC_DIR_NAME against DISK_TREE.  If CHECK_PROPS is set,
   the comparison will examin props.  Returns if successful, raises on
   failure."""
-  verify_update(None, None, None, wc_dir_name, None, None, None, disk_tree,
-                None, check_props=check_props)
+
+  if isinstance(disk_tree, wc.State):
+    disk_tree = disk_tree.old_tree()
+
+  actual_disk = tree.build_tree_from_wc(wc_dir_name, check_props)
+  try:
+    tree.compare_trees("disk", actual_disk, disk_tree,
+                       singleton_handler_a, a_baton,
+                       singleton_handler_b, b_baton)
+  except tree.SVNTreeUnequal:
+    _log_tree_state("EXPECTED DISK TREE:", disk_tree)
+    _log_tree_state("ACTUAL DISK TREE:", actual_disk)
+    raise
+
 
 
 
@@ -838,8 +833,6 @@ def run_and_verify_update(wc_dir_name,
   If ERROR_RE_STRING, the update must exit with error, and the error
   message must match regular expression ERROR_RE_STRING.
 
-  Else if ERROR_RE_STRING is None, then:
-
   If OUTPUT_TREE is not None, the subcommand output will be verified
   against OUTPUT_TREE.  If DISK_TREE is not None, the working copy
   itself will be verified against DISK_TREE.  If STATUS_TREE is not
@@ -864,11 +857,13 @@ def run_and_verify_update(wc_dir_name,
 
   if error_re_string:
     rm = re.compile(error_re_string)
+    match = None
     for line in errput:
       match = rm.search(line)
       if match:
-        return
-    raise main.SVNUnmatchedError
+        break
+    if not match:
+      raise main.SVNUnmatchedError
 
   actual = wc.State.from_checkout(output)
   verify_update(actual, None, None, wc_dir_name,
@@ -1171,8 +1166,6 @@ def run_and_verify_patch(dir, patch_path
   If ERROR_RE_STRING, 'svn patch' must exit with error, and the error
   message must match regular expression ERROR_RE_STRING.
 
-  Else if ERROR_RE_STRING is None, then:
-
   The subcommand output will be verified against OUTPUT_TREE, and the
   working copy itself will be verified against DISK_TREE.  If optional
   STATUS_TREE is given, then 'svn status' output will be compared.
@@ -1341,7 +1334,6 @@ def run_and_verify_switch(wc_dir_name,
       error_re_string = ".*(" + error_re_string + ")"
     expected_err = verify.RegexOutput(error_re_string, match_all=False)
     verify.verify_outputs(None, None, errput, None, expected_err)
-    return
   elif errput:
     raise verify.SVNUnexpectedStderr(err)
 
@@ -1353,7 +1345,7 @@ def run_and_verify_switch(wc_dir_name,
                 singleton_handler_b, b_baton,
                 check_props)
 
-def process_output_for_commit(output):
+def process_output_for_commit(output, error_re_string):
   """Helper for run_and_verify_commit(), also used in the factory."""
   # Remove the final output line, and verify that the commit succeeded.
   lastline = ""
@@ -1372,7 +1364,7 @@ def process_output_for_commit(output):
 
     cm = re.compile("(Committed|Imported) revision [0-9]+.")
     match = cm.search(lastline)
-    if not match:
+    if not match and not error_re_string:
       logger.warn("ERROR:  commit did not succeed.")
       logger.warn("The final line from 'svn ci' was:")
       logger.warn(lastline)
@@ -1417,8 +1409,6 @@ def run_and_verify_commit(wc_dir_name, o
 
   if isinstance(output_tree, wc.State):
     output_tree = output_tree.old_tree()
-  if isinstance(status_tree, wc.State):
-    status_tree = status_tree.old_tree()
 
   # Commit.
   if '-m' not in args and '-F' not in args:
@@ -1431,22 +1421,22 @@ def run_and_verify_commit(wc_dir_name, o
       error_re_string = ".*(" + error_re_string + ")"
     expected_err = verify.RegexOutput(error_re_string, match_all=False)
     verify.verify_outputs(None, None, errput, None, expected_err)
-    return
 
   # Else not expecting error:
 
   # Convert the output into a tree.
-  output = process_output_for_commit(output)
+  output = process_output_for_commit(output, error_re_string)
   actual = tree.build_tree_from_commit(output)
 
   # Verify actual output against expected output.
-  try:
-    tree.compare_trees("output", actual, output_tree)
-  except tree.SVNTreeError:
-      verify.display_trees("Output of commit is unexpected",
-                           "OUTPUT TREE", output_tree, actual)
-      _log_tree_state("ACTUAL OUTPUT TREE:", actual, wc_dir_name)
-      raise
+  if output_tree:
+    try:
+      tree.compare_trees("output", actual, output_tree)
+    except tree.SVNTreeError:
+        verify.display_trees("Output of commit is unexpected",
+                             "OUTPUT TREE", output_tree, actual)
+        _log_tree_state("ACTUAL OUTPUT TREE:", actual, wc_dir_name)
+        raise
 
   # Verify via 'status' command too, if possible.
   if status_tree:

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/svntest/sandbox.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/svntest/sandbox.py?rev=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/svntest/sandbox.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/svntest/sandbox.py Mon Jan 21 23:37:01 2013
@@ -318,6 +318,13 @@ class Sandbox:
       # '*' is evaluated on Windows
       self.simple_propset('svn:special', 'X', target)
 
+  def simple_add_text(self, text, *targets):
+    """Create files containing TEXT as TARGETS"""
+    assert len(targets) > 0
+    for target in targets:
+       svntest.main.file_write(self.ospath(target), text, mode='wb')
+    self.simple_add(*targets)
+
   def simple_copy(self, source, dest):
     """Copy SOURCE to DEST in the WC.
        SOURCE and DEST are relpaths relative to the WC."""

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/svntest/tree.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/svntest/tree.py?rev=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/svntest/tree.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/svntest/tree.py Mon Jan 21 23:37:01 2013
@@ -253,6 +253,10 @@ class SVNTreeNode:
     # remove the subtree path, skip this node if necessary.
     if path.startswith(subtree):
       path = path[len(subtree):]
+    elif path + os.sep == subtree:
+      # Many callers set subtree to 'some-path' + os.sep. Don't skip the
+      # root node in that case.
+      path = ''
     else:
       return 0
 

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/svntest/wc.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/svntest/wc.py?rev=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/svntest/wc.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/svntest/wc.py Mon Jan 21 23:37:01 2013
@@ -96,6 +96,7 @@ _re_parse_status = re.compile('^([?!MACD
 
 _re_parse_status_ex = re.compile('^      ('
                '(  \> moved (from (?P<moved_from>.+)|to (?P<moved_to>.*)))'
+              '|(  \> swapped places with (?P<swapped_with>.+).*)'
               '|(\>   (?P<tc>.+))'
   ')$')
 
@@ -144,10 +145,11 @@ class State:
     "Import state items from a State object, reparent the items to PARENT."
     assert isinstance(state, State)
 
-    if parent and parent[-1] != '/':
-      parent += '/'
     for path, item in state.desc.items():
-      path = parent + path
+      if path == '':
+        path = parent
+      else:
+        path = parent + '/' + path
       self.desc[path] = item
 
   def remove(self, *paths):
@@ -337,6 +339,13 @@ class State:
         # These are only in their parents' THIS_DIR, they don't have entries.
         if item.status[0] in '!?' and item.treeconflict == 'C':
           del self.desc[path]
+        # Normal externals are not stored in the parent wc, drop the root
+        # and everything in these working copies
+        elif item.status == 'X ' or item.prev_status == 'X ':
+          del self.desc[path]
+          for p, i in self.desc.copy().items():
+            if p.startswith(path + '/'):
+              del self.desc[p]
         else:
           # when reading the entry structures, we don't examine for text or
           # property mods, so clear those flags. we also do not examine the
@@ -355,6 +364,9 @@ class State:
           if item.entry_status is not None:
             item.status = item.entry_status
             item.entry_status = None
+          if item.entry_copied is not None:
+            item.copied = item.entry_copied
+            item.entry_copied = None
       if item.writelocked:
         # we don't contact the repository, so our only information is what
         # is in the working copy. 'K' means we have one and it matches the
@@ -372,6 +384,9 @@ class State:
           item.writelocked = None
       item.moved_from = None
       item.moved_to = None
+      if path == '':
+        item.switched = None
+      item.treeconflict = None
 
   def old_tree(self):
     "Return an old-style tree (for compatibility purposes)."
@@ -421,12 +436,6 @@ class State:
       if line.startswith('DBG:'):
         continue
 
-      # Quit when we hit an externals status announcement.
-      ### someday we can fix the externals tests to expect the additional
-      ### flood of externals status data.
-      if line.startswith('Performing'):
-        break
-
       match = _re_parse_status.search(line)
       if not match or match.group(10) == '-':
 
@@ -445,12 +454,27 @@ class State:
               path = path[len(wc_dir_name) + 1:]
             
             last.tweak(moved_to = to_relpath(path))
+          elif ex_match.group('swapped_with'):
+            path = ex_match.group('swapped_with')
+            if wc_dir_name and path.startswith(wc_dir_name + os.path.sep):
+              path = path[len(wc_dir_name) + 1:]
+            
+            last.tweak(moved_to = to_relpath(path))
+            last.tweak(moved_from = to_relpath(path))
 
           # Parse TC description?
 
         # ignore non-matching lines, or items that only exist on repos
         continue
 
+      prev_status = None
+      prev_treeconflict = None
+
+      path = to_relpath(match.group('path'))
+      if path in desc:
+        prev_status = desc[path].status
+        prev_treeconflict = desc[path].treeconflict
+
       item = StateItem(status=match.group(1),
                        locked=not_space(match.group(2)),
                        copied=not_space(match.group(3)),
@@ -458,8 +482,10 @@ class State:
                        writelocked=not_space(match.group(5)),
                        treeconflict=not_space(match.group(6)),
                        wc_rev=not_space(match.group('wc_rev')),
+                       prev_status=prev_status,
+                       prev_treeconflict =prev_treeconflict
                        )
-      desc[to_relpath(match.group('path'))] = item
+      desc[path] = item
       last = item
 
     return cls('', desc)
@@ -515,12 +541,38 @@ class State:
           treeconflict = match.group(3)
         else:
           treeconflict = None
-        desc[to_relpath(match.group(4))] = StateItem(status=match.group(1),
-                                                     treeconflict=treeconflict)
+        path = to_relpath(match.group(4))
+        prev_status = None
+        prev_verb = None
+        prev_treeconflict = None
+
+        if path in desc:
+          prev_status = desc[path].status
+          prev_verb = desc[path].verb
+          prev_treeconflict = desc[path].treeconflict
+
+        desc[path] = StateItem(status=match.group(1),
+                               treeconflict=treeconflict,
+                               prev_status=prev_status,
+                               prev_verb=prev_verb,
+                               prev_treeconflict=prev_treeconflict)
       else:
         match = re_extra.search(line)
         if match:
-          desc[to_relpath(match.group(2))] = StateItem(verb=match.group(1))
+          path = to_relpath(match.group(2))
+          prev_status = None
+          prev_verb = None
+          prev_treeconflict = None
+
+          if path in desc:
+            prev_status = desc[path].status
+            prev_verb = desc[path].verb
+            prev_treeconflict = desc[path].treeconflict
+
+          desc[path] = StateItem(verb=match.group(1),
+                                 prev_status=prev_status,
+                                 prev_verb=prev_verb,
+                                 prev_treeconflict=prev_treeconflict)
 
     return cls('', desc)
 
@@ -638,6 +690,9 @@ class State:
         # entries that are ABSENT don't show up in status
         if entry.absent:
           continue
+        # entries that are User Excluded don't show up in status
+        if entry.depth == -1:
+          continue
         if name and entry.kind == 2:
           # stub subdirectory. leave a "missing" StateItem in here. note
           # that we can't put the status as "! " because that gets tweaked
@@ -663,6 +718,9 @@ class State:
         if implied_url and implied_url != entry.url:
           item.switched = 'S'
 
+        if entry.file_external:
+          item.switched = 'X'
+
     return cls('', desc)
 
 
@@ -676,9 +734,10 @@ class StateItem:
 
   def __init__(self, contents=None, props=None,
                status=None, verb=None, wc_rev=None,
-               entry_rev=None, entry_status=None,
+               entry_rev=None, entry_status=None, entry_copied=None,
                locked=None, copied=None, switched=None, writelocked=None,
-               treeconflict=None, moved_from=None, moved_to=None):
+               treeconflict=None, moved_from=None, moved_to=None,
+               prev_status=None, prev_verb=None, prev_treeconflict=None):
     # provide an empty prop dict if it wasn't provided
     if props is None:
       props = { }
@@ -695,22 +754,26 @@ class StateItem:
     self.props = props
     # A two-character string from the first two columns of 'svn status'.
     self.status = status
+    self.prev_status = prev_status
     # The action word such as 'Adding' printed by commands like 'svn update'.
     self.verb = verb
+    self.prev_verb = prev_verb
     # The base revision number of the node in the WC, as a string.
     self.wc_rev = wc_rev
     # These will be set when we expect the wc_rev/status to differ from those
     # found in the entries code.
     self.entry_rev = entry_rev
     self.entry_status = entry_status
+    self.entry_copied = entry_copied
     # For the following attributes, the value is the status character of that
     # field from 'svn status', except using value None instead of status ' '.
     self.locked = locked
     self.copied = copied
     self.switched = switched
     self.writelocked = writelocked
-    # Value 'C' or ' ', or None as an expected status meaning 'do not check'.
+    # Value 'C', 'A', 'D' or ' ', or None as an expected status meaning 'do not check'.
     self.treeconflict = treeconflict
+    self.prev_treeconflict = prev_treeconflict
     # Relative paths to the move locations
     self.moved_from = moved_from
     self.moved_to = moved_to
@@ -751,8 +814,12 @@ class StateItem:
     atts = { }
     if self.status is not None:
       atts['status'] = self.status
+    if self.prev_status is not None:
+      atts['prev_status'] = self.prev_status
     if self.verb is not None:
       atts['verb'] = self.verb
+    if self.prev_verb is not None:
+      atts['prev_verb'] = self.prev_verb
     if self.wc_rev is not None:
       atts['wc_rev'] = self.wc_rev
     if self.locked is not None:
@@ -765,6 +832,8 @@ class StateItem:
       atts['writelocked'] = self.writelocked
     if self.treeconflict is not None:
       atts['treeconflict'] = self.treeconflict
+    if self.prev_treeconflict is not None:
+      atts['prev_treeconflict'] = self.prev_treeconflict
     if self.moved_from is not None:
       atts['moved_from'] = self.moved_from
     if self.moved_to is not None:
@@ -866,9 +935,12 @@ def repos_join(base, path):
   """Join two repos paths. This generally works for URLs too."""
   if base == '':
     return path
-  if path == '':
+  elif path == '':
     return base
-  return base + '/' + path
+  elif base[len(base)-1:] == '/':
+    return base + path
+  else:
+    return base + '/' + path
 
 
 def svn_uri_quote(url):

Modified: subversion/branches/ev2-export/subversion/tests/cmdline/switch_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/tests/cmdline/switch_tests.py?rev=1436688&r1=1436687&r2=1436688&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/tests/cmdline/switch_tests.py (original)
+++ subversion/branches/ev2-export/subversion/tests/cmdline/switch_tests.py Mon Jan 21 23:37:01 2013
@@ -1463,15 +1463,15 @@ def switch_with_obstructing_local_adds(s
   expected_status.add({
     'A/B/F/gamma'     : Item(status='R ', treeconflict='C', wc_rev='1'),
     'A/B/F/G'         : Item(status='R ', treeconflict='C', wc_rev='1'),
-    'A/B/F/G/pi'      : Item(status='A ', wc_rev='-'),
-    'A/B/F/G/tau'     : Item(status='A ', wc_rev='-'),
-    'A/B/F/G/upsilon' : Item(status='A ', wc_rev='-'),
+    'A/B/F/G/pi'      : Item(status='A ', wc_rev='-', entry_status='R ', entry_rev='1'),
+    'A/B/F/G/tau'     : Item(status='A ', wc_rev='-', entry_status='R ', entry_rev='1'),
+    'A/B/F/G/upsilon' : Item(status='A ', wc_rev='-', entry_rev='0'),
     'A/B/F/G/rho'     : Item(status='D ', wc_rev='1'),
     'A/B/F/H'         : Item(status='  ', wc_rev='1'),
     'A/B/F/H/chi'     : Item(status='  ', wc_rev='1'),
     'A/B/F/H/omega'   : Item(status='  ', wc_rev='1'),
     'A/B/F/H/psi'     : Item(status='  ', wc_rev='1'),
-    'A/B/F/I'         : Item(status='A ', wc_rev='-'),
+    'A/B/F/I'         : Item(status='A ', wc_rev='-', entry_rev='0'),
   })
 
   # "Extra" files that we expect to result from the conflicts.
@@ -2213,7 +2213,7 @@ def switch_to_root(sbox):
   expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
   expected_status.remove('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau')
   expected_status.add_state('A/D/G',
-                            svntest.actions.get_virginal_state(wc_dir, 1))
+                            svntest.actions.get_virginal_state(wc_dir + '/A/D/G', 1))
   expected_status.tweak('A/D/G', switched = 'S')
   svntest.actions.run_and_verify_switch(wc_dir, ADG_path, sbox.repo_url,
                                         expected_output,
@@ -2906,6 +2906,33 @@ def different_node_kind(sbox):
   switch_to_file(sbox, 'iota', 'A/C')
   switch_to_file(sbox, 'A/D/gamma', 'A/D/G')
 
+@Issue(3332, 3333)
+def switch_to_spaces(sbox):
+  "switch to a directory with spaces in its name"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  repo_url = sbox.repo_url
+
+  # Paths are normalized in the command processing, so %20 is equivalent to ' '
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'cp', repo_url + '/A',
+                                           repo_url + '/A%20with space',
+                                     '-m', '')
+
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'mv', repo_url + '/A%20with space',
+                                           repo_url + '/A with%20more spaces',
+                                     '-m', '')
+
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
+  expected_status.tweak('A', switched='S')
+  expected_status.tweak('', 'iota', wc_rev=1)
+
+  svntest.actions.run_and_verify_switch(sbox.wc_dir, sbox.ospath('A'),
+                                        repo_url + '/A%20with more%20spaces',
+                                        None, None, expected_status)
+
 ########################################################################
 # Run the tests
 
@@ -2946,6 +2973,7 @@ test_list = [ None,
               copy_with_switched_subdir,
               up_to_old_rev_with_subtree_switched_to_root,
               different_node_kind,
+              switch_to_spaces,
               ]
 
 if __name__ == '__main__':