diff options
Diffstat (limited to 'subversion/tests/cmdline/resolve_tests.py')
-rwxr-xr-x | subversion/tests/cmdline/resolve_tests.py | 389 |
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__': |