summaryrefslogtreecommitdiff
path: root/subversion/tests/cmdline/resolve_tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/tests/cmdline/resolve_tests.py')
-rwxr-xr-xsubversion/tests/cmdline/resolve_tests.py389
1 files changed, 378 insertions, 11 deletions
diff --git a/subversion/tests/cmdline/resolve_tests.py b/subversion/tests/cmdline/resolve_tests.py
index 474da40..c032b06 100755
--- a/subversion/tests/cmdline/resolve_tests.py
+++ b/subversion/tests/cmdline/resolve_tests.py
@@ -25,7 +25,7 @@
######################################################################
# General modules
-import shutil, sys, re, os
+import shutil, sys, re, os, stat
import time
# Our testing module
@@ -42,7 +42,15 @@ Issue = svntest.testcase.Issue_deco
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.
@@ -69,12 +77,10 @@ def automatic_conflict_resolution(sbox):
'revert', '--recursive', A_COPY_path)
svntest.actions.run_and_verify_svn(
None,
- "(--- Merging r3 into .*A_COPY':\n)|"
- "(C .*psi\n)|"
- "(--- Recording mergeinfo for merge of r3 into .*A_COPY':\n)|"
- "( U .*A_COPY\n)|"
- "(Summary of conflicts:\n)|"
- "( Text conflicts: 1\n)",
+ expected_merge_output([[3]], [
+ "C %s\n" % psi_COPY_path,
+ " U %s\n" % A_COPY_path],
+ target=A_COPY_path, text_conflicts=1),
[], 'merge', '-c3', '--allow-mixed-revisions',
sbox.repo_url + '/A',
A_COPY_path)
@@ -107,7 +113,6 @@ def automatic_conflict_resolution(sbox):
# Test for issue #3707 'property conflicts not handled correctly by
# svn resolve'.
@Issue(3707)
-@XFail()
def prop_conflict_resolution(sbox):
"resolving prop conflicts"
@@ -196,7 +201,7 @@ def prop_conflict_resolution(sbox):
# Update, postponing all conflict resolution.
svntest.actions.run_and_verify_svn(None, None, [], 'up',
'--accept=postpone', wc_dir)
- svntest.actions.run_and_verify_resolve([iota_path, mu_path, gamma_path], '-R',
+ svntest.actions.run_and_verify_resolve([iota_path, mu_path], '-R',
'--accept', resolve_accept, wc_dir)
svntest.actions.run_and_verify_svn(
'svn revolve -R --accept=' + resolve_accept + ' of prop conflict '
@@ -226,8 +231,8 @@ def prop_conflict_resolution(sbox):
# 2) 'A/mu' - An incoming prop edit on a local prop modification.
# 3) 'A/D/gamma' - An local, non-conflicted prop edit
#
- # This currently fails because svn resolve --accept=[theirs-conflict |
- # theirs-full] removes the conflicts, but doesn't install 'their' version
+ # Previously this failed because svn resolve --accept=[theirs-conflict |
+ # theirs-full] removed the conflicts, but didn't install 'their' version
# of the conflicted properties.
do_prop_conflicting_up_and_resolve('mine-full',
['local_edit\n'],
@@ -245,6 +250,363 @@ 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"
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ # Mark iota as executable
+ sbox.simple_propset("svn:executable", '*', 'iota')
+ sbox.simple_commit() # r2
+
+ # Make a change to iota in r3
+ svntest.main.file_write(sbox.ospath('iota'), "boo\n")
+ sbox.simple_commit() # r3
+
+ # Update back to r2, and tweak iota to provoke a text conflict
+ sbox.simple_update(revision=2)
+ svntest.main.file_write(sbox.ospath('iota'), "bzzt\n")
+
+ # Get permission bits of iota
+ mode = os.stat(sbox.ospath('iota'))[stat.ST_MODE]
+
+ # Update back to r3, and auto-resolve the text conflict.
+ svntest.main.run_svn(False, 'update', wc_dir, '--accept', 'theirs-full')
+
+ # permission bits of iota should be unaffected
+ 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)
+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
@@ -252,6 +614,11 @@ def prop_conflict_resolution(sbox):
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__':