diff options
Diffstat (limited to 'subversion/tests/cmdline/move_tests.py')
-rwxr-xr-x | subversion/tests/cmdline/move_tests.py | 1558 |
1 files changed, 1558 insertions, 0 deletions
diff --git a/subversion/tests/cmdline/move_tests.py b/subversion/tests/cmdline/move_tests.py new file mode 100755 index 0000000..4495b59 --- /dev/null +++ b/subversion/tests/cmdline/move_tests.py @@ -0,0 +1,1558 @@ +#!/usr/bin/env python +# +# move_tests.py: testing the local move tracking +# +# Subversion is a tool for revision control. +# See http://subversion.apache.org for more information. +# +# ==================================================================== +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +###################################################################### + + +# General modules +import os, re, logging + +logger = logging.getLogger() + +# Our testing module +import svntest +from svntest import wc, actions, verify + +# (abbreviation) +Skip = svntest.testcase.Skip_deco +SkipUnless = svntest.testcase.SkipUnless_deco +XFail = svntest.testcase.XFail_deco +Issues = svntest.testcase.Issues_deco +Issue = svntest.testcase.Issue_deco +Wimp = svntest.testcase.Wimp_deco +Item = svntest.wc.StateItem +exp_noop_up_out = svntest.actions.expected_noop_update_output + +def build_incoming_changes_file(sbox, source, dest): + "Build up revs to receive incoming changes over our local file move" + + # r1 = greek tree sandbox + + # r2 = Modify source of moved file + sbox.simple_append(source, "modified\n") + sbox.simple_commit(message="Modify source of moved file") + + # r3 = Delete source of moved file + sbox.simple_rm(source) + sbox.simple_commit(message="Delete source of moved file") + + # r4 = Replace source of moved file + # To get a replace update from r2 to r4. + sbox.simple_add_text("This is the replaced file.\n", source) + sbox.simple_commit(message="Replace source of moved file") + + # r5 = Add destination of moved file + sbox.simple_add_text("This is the destination file.\n", dest) + sbox.simple_commit(message="Add destination of moved file") + + # r6 = Modify destination of moved file + sbox.simple_append(dest, "modified\n") + sbox.simple_commit(message="Modify destination of moved file") + + # r7 = Delete destination of moved file + sbox.simple_rm(dest) + sbox.simple_commit(message="Delete destination of moved file") + + # r8 = Copy destination of moved file + sbox.simple_copy('A/mu', dest) + sbox.simple_commit(message="Copy destination of moved file") + + # r9 = Replace destination of moved file + sbox.simple_rm(dest) + sbox.simple_add_text("This is the destination file.\n", dest) + sbox.simple_commit(message="Replace destination of moved file") + + # r10 = Add property on destination of moved file. + sbox.simple_propset("foo", "bar", dest) + sbox.simple_commit(message="Add property on destination of moved file") + + # r11 = Modify property on destination of moved file. + sbox.simple_propset("foo", "baz", dest) + sbox.simple_commit(message="Modify property on destination of moved file") + + # r12 = Delete property on destination of moved file. + sbox.simple_propdel("foo", dest) + sbox.simple_commit(message="Delete property on destination of moved file") + + # r13 = Remove destination again (not needed for any test just cleanup). + sbox.simple_rm(dest) + sbox.simple_commit(message="Remove destination (cleanup)") + + # r14 = Add property on source of moved file. + sbox.simple_propset("foo", "bar", source) + sbox.simple_commit(message="Add property on source of moved file") + + # r15 = Modify property on source of moved file. + sbox.simple_propset("foo", "baz", source) + sbox.simple_commit(message="Modify property on source of moved file") + + # r16 = Delete property on source of moved file. + sbox.simple_propdel("foo", source) + sbox.simple_commit(message="Delete property on source of moved file") + + # r17 = Move that is identical to our local move. + sbox.simple_move(source, dest) + sbox.simple_commit(message="Identical move to our local move") + +def move_file_test(sbox, source, dest, move_func, test): + """Execute a series of actions to test local move tracking. sbox is the + sandbox we're working in, source is the source of the move, dest is the + destination for the move and tests is various other parameters of the move + testing. In particular: + start_rev: revision to update to before starting + start_output: validate the output of the start update against this. + start_disk: validate the on disk state after the start update against this. + start_status: validate the wc status after the start update against this. + end_rev: revision to update to, bringing in some update you want to test. + up_output: validate the output of the end update agianst this. + up_disk: validate the on disk state after the end update against this. + up_status: validate the wc status after the end update against this. + revert_paths: validate the paths reverted. + resolves: A directory of resolve accept arguments to test, the whole test will + be run for each. The value is a directory with the following keys: + output: validate the output of the resolve command against this. + error: validate the error of the resolve command against this. + status: validate the wc status after the resolve against this. + revert_paths: override the paths reverted check in the test.""" + + wc_dir = sbox.wc_dir + + source_path = sbox.ospath(source) + dest_path = sbox.ospath(dest) + + # Deal with if there's no resolves key, as in we're not going to + # do a resolve. + if not 'resolves' in test or not test['resolves']: + test['resolves'] = {None: None} + + # Do the test for every type of resolve provided. + for resolve_accept in test['resolves'].keys(): + + # update to start_rev + svntest.actions.run_and_verify_update(wc_dir, test['start_output'], + test['start_disk'], test['start_status'], + None, None, None, None, None, False, + '-r', test['start_rev'], wc_dir) + # execute the move + move_func(test['start_rev']) + + # update to end_rev, which will create a conflict + # TODO: Limit the property checks to only when we're doing something with + # properties. + svntest.actions.run_and_verify_update(wc_dir, test['up_output'], + test['up_disk'], test['up_status'], + None, None, None, None, None, True, + '-r', test['end_rev'], wc_dir) + + revert_paths = None + if 'revert_paths' in test: + revert_paths = test['revert_paths'] + + # resolve the conflict + # TODO: Switch to using run_and_verify_resolve, can't use it right now because + # it's not friendly with the output of resolutions right now. + if resolve_accept: + resolve = test['resolves'][resolve_accept] + if not 'output' in resolve: + resolve['output'] = None + if not 'error' in resolve: + resolve['error'] = [] + if not 'disk' in resolve: + resolve['disk'] = None + if 'revert_paths' in resolve: + revert_paths = resolve['revert_paths'] + svntest.actions.run_and_verify_svn('Resolve modification to source of move', + resolve['output'], resolve['error'], + 'resolve', '--accept', resolve_accept, + '-R', wc_dir) + + # TODO: This should be moved into the run_and_verify_resolve mentioned + # above. + if resolve['status']: + svntest.actions.run_and_verify_status(wc_dir, resolve['status']) + + # TODO: This should be moved into the run_and_verify_resolve mentioned + # above. + if resolve['disk']: + svntest.actions.verify_disk(wc_dir, resolve['disk'], True) + + # revert to preprare for the next test + svntest.actions.run_and_verify_revert(revert_paths, '-R', wc_dir) + +# tests is an array of test dictionaries that move_file_test above will take +def move_file_tests(sbox, source, dest, move_func, tests): + for test in tests: + move_file_test(sbox, source, dest, move_func, test) + +def build_simple_file_move_tests(sbox, source, dest): + """Given a sandbox, source and destination build the array of tests for + a file move""" + + wc_dir = sbox.wc_dir + source_path = sbox.ospath(source) + dest_path = sbox.ospath(dest) + + # Build the tests list + tests = [] + + # move and update with incoming change to source (r1-2). + test = {} + test['start_rev'] = 1 + test['end_rev'] = 2 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + source : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the file 'lambda'.\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest, + treeconflict='C') + test['up_status'].add({dest: Item(status='A ', moved_from=source, + copied='+', wc_rev='-')}) + mc = {} + mc['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % source_path, match_all=False + ) + mc['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + mc['status'].tweak(source, status='D ', moved_to=dest) + mc['status'].add({dest: Item(status='A ', moved_from=source, + copied='+', wc_rev='-')}) + mc['disk'] = test['up_disk'].copy() + mc['disk'].tweak(dest, contents="This is the file 'lambda'.\nmodified\n") + # theirs-conflict doesn't work + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + # working breaks the move + working = {} + working['output'] = svntest.verify.ExpectedOutput( + [ + "Breaking move with source path '%s'\n" % source_path, + "Resolved conflicted state of '%s'\n" % source_path, + ] + ) + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='D ') + working['status'].add({dest: Item(status='A ', copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming deletion of source (r2-3) + test = {} + test['start_rev'] = 2 + test['end_rev'] = 3 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + source : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the file 'lambda'.\nmodified\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='! ', treeconflict='C', wc_rev=None) + test['up_status'].add({dest: Item(status='A ', copied='+', wc_rev='-')}) + # mine-conflict and theirs-conflict don't work. + mc = {} + mc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + mc['status'] = test['up_status'] + mc['disk'] = test['up_disk'] + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + # XXX: Doesn't say it broke the move it should. + working['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % source_path, match_all=False + ) + # move is broken now + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].add({dest: Item(status='A ', copied='+', wc_rev='-')}) + working['status'].remove(source) + working['disk'] = test['up_disk'] + working['revert_paths'] = [dest_path] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [dest_path, source_path] + tests.append(test) + + # move and update with incoming replacement of source (r2-4) + test = {} + test['start_rev'] = 2 + test['end_rev'] = 4 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + source : Item(status=' ', prev_status=' ', treeconflict='A', + prev_treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the file 'lambda'.\nmodified\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + # XXX: Is entry_status=' ' really right here? + test['up_status'].tweak(source, status='! ', treeconflict='C', entry_status=' ') + test['up_status'].add({dest: Item(status='A ', copied='+', wc_rev='-')}) + # mine-conflict and theirs-conflict don't work. + mc = {} + mc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + mc['status'] = test['up_status'] + mc['disk'] = test['up_disk'] + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + # XXX: Broke the move but doesn't notify that it does. + working['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % source_path, match_all=False + ) + # XXX: Not sure this status is really correct here + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='! ') + working['status'].add({dest: Item(status='A ', copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming add of dest (r4-5) + test = {} + test['start_rev'] = 4 + test['end_rev'] = 5 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + dest : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest) + test['up_status'].add({dest: Item(status='R ', copied='+', treeconflict='C', + wc_rev='-', moved_from=source)}) + # mine-conflict and theirs-conflict don't work. + mc = {} + mc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + mc['status'] = test['up_status'] + mc['disk'] = test['up_disk'] + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + # XXX: Doesn't say what it did. + working['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % dest_path, match_all=False + ) + # working converts the move into a replacement + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='D ', moved_to=dest) + working['status'].add({dest: Item(status='R ', moved_from=source, + copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming add of dest (r4-6) + # we're going 4-6 because we're not testing a replacement move + test = {} + test['start_rev'] = 4 + test['end_rev'] = 6 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + dest : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest) + test['up_status'].add({dest: Item(status='R ', copied='+', treeconflict='C', + wc_rev='-', moved_from=source)}) + # mine-conflict and theirs-conflict don't work. + mc = {} + mc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + mc['status'] = test['up_status'] + mc['disk'] = test['up_disk'] + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + working['accept'] = 'working' + # XXX: Doesn't say what it did. + working['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % dest_path, match_all=False + ) + # working converts the move into a replacement + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='D ', moved_to=dest) + working['status'].add({dest: Item(status='R ', moved_from=source, + copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming delete of dest (r4-7) + # Since we're not testing a replacement move the incoming delete has to + # be done starting from a rev where the file doesn't exist. So it ends + # up being a no-op update. So this test might be rather pointless. + test = {} + test['start_rev'] = 4 + test['end_rev'] = 7 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest) + test['up_status'].add({dest: Item(status='A ', copied='+', + wc_rev='-', moved_from=source)}) + # no conflict so no resolve. + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming copy to dest (r7-8) + test = {} + test['start_rev'] = 7 + test['end_rev'] = 8 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + dest : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest) + test['up_status'].add({dest: Item(status='R ', copied='+', treeconflict='C', + wc_rev='-', moved_from=source)}) + # mine-conflict and theirs-conflict don't work. + mc = {} + mc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + mc['status'] = test['up_status'] + mc['disk'] = test['up_disk'] + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + # XXX: Doesn't say what it did. + working['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % dest_path, match_all=False + ) + # working converts the move into a replacement + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='D ', moved_to=dest) + working['status'].add({dest: Item(status='R ', moved_from=source, + copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming replace to dest (r7-9) + test = {} + test['start_rev'] = 7 + test['end_rev'] = 9 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + dest : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest) + test['up_status'].add({dest: Item(status='R ', copied='+', treeconflict='C', + wc_rev='-', moved_from=source)}) + # mine-conflict and theirs-conflict don't work. + mc = {} + mc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + mc['status'] = test['up_status'] + mc['disk'] = test['up_disk'] + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + # XXX: Doesn't say what it did. + working['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % dest_path, match_all=False + ) + # working converts the move into a replacement + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='D ', moved_to=dest) + working['status'].add({dest: Item(status='R ', moved_from=source, + copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming property addition to dest (r7-10) + test = {} + test['start_rev'] = 7 + test['end_rev'] = 10 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + dest : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest) + test['up_status'].add({dest: Item(status='R ', copied='+', treeconflict='C', + wc_rev='-', moved_from=source)}) + # mine-conflict and theirs-conflict don't work. + mc = {} + mc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + mc['status'] = test['up_status'] + mc['disk'] = test['up_disk'] + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + # XXX: Didn't tell us what it did. + working['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % dest_path, match_all=False + ) + # working converts the move into a replacement + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='D ', moved_to=dest) + working['status'].add({dest: Item(status='R ', moved_from=source, + copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming property modification to dest (r7-11) + test = {} + test['start_rev'] = 7 + test['end_rev'] = 11 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + dest : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest) + test['up_status'].add({dest: Item(status='R ', copied='+', treeconflict='C', + wc_rev='-', moved_from=source)}) + # mine-conflict and theirs-conflict don't work. + mc = {} + mc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + mc['status'] = test['up_status'] + mc['disk'] = test['up_disk'] + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + # XXX: Doesn't tell you what it did. + working['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % dest_path, match_all=False + ) + # working converts the move into a replacement + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='D ', moved_to=dest) + working['status'].add({dest: Item(status='R ', moved_from=source, + copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming property deletion to dest (r7-12) + test = {} + test['start_rev'] = 7 + test['end_rev'] = 12 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + dest : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest) + test['up_status'].add({dest: Item(status='R ', copied='+', treeconflict='C', + wc_rev='-', moved_from=source)}) + # mine-conflict and theirs-conflict don't work. + mc = {} + mc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + mc['status'] = test['up_status'] + mc['disk'] = test['up_disk'] + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + # XXX: Doesn't tell you what it did. + working['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % dest_path, match_all=False + ) + # working converts the move into a replacement + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='D ', moved_to=dest) + working['status'].add({dest: Item(status='R ', moved_from=source, + copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming property addition to source (r13-14) + test = {} + test['start_rev'] = 13 + test['end_rev'] = 14 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + source : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest, treeconflict='C') + test['up_status'].add({dest: Item(status='A ', copied='+', wc_rev='-', + moved_from=source)}) + mc = {} + # TODO: Should check that the output includes that the update was applied to + # the destination + mc['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % source_path, match_all=False + ) + mc['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + mc['status'].tweak(source, status='D ', moved_to=dest) + mc['status'].add({dest: Item(status='A ', moved_from=source, + copied='+', wc_rev='-')}) + mc['disk'] = test['up_disk'].copy() + mc['disk'].tweak(dest, props={u'foo': u'bar'}) + # theirs-conflict doesn't work + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + working['output'] = svntest.verify.ExpectedOutput( + [ + "Breaking move with source path '%s'\n" % source_path, + "Resolved conflicted state of '%s'\n" % source_path + ] + ) + # XXX: working breaks the move? Is that right? + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='D ') + working['status'].add({dest: Item(status='A ', copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming property modification to source (r14-15) + test = {} + test['start_rev'] = 14 + test['end_rev'] = 15 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + source : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n", props={u'foo': u'bar'}) + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest, treeconflict='C') + test['up_status'].add({dest: Item(status='A ', copied='+', wc_rev='-', + moved_from=source)}) + mc = {} + # TODO: Should check that the output includes that the update was applied to + # the destination + mc['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % source_path, match_all=False + ) + mc['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + mc['status'].tweak(source, status='D ', moved_to=dest) + mc['status'].add({dest: Item(status='A ', moved_from=source, + copied='+', wc_rev='-')}) + mc['disk'] = test['up_disk'].copy() + mc['disk'].tweak(dest, props={u'foo': u'baz'}) + # theirs-conflict doesn't work + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + working['output'] = svntest.verify.ExpectedOutput( + [ + "Breaking move with source path '%s'\n" % source_path, + "Resolved conflicted state of '%s'\n" % source_path + ] + ) + # XXX: working breaks the move? Is that right? + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='D ') + working['status'].add({dest: Item(status='A ', copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming property deletion to source (r15-16) + test = {} + test['start_rev'] = 15 + test['end_rev'] = 16 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + source : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n", props={'foo': 'baz'}) + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='D ', moved_to=dest, treeconflict='C') + test['up_status'].add({dest: Item(status='A ', copied='+', wc_rev='-', + moved_from=source)}) + mc = {} + # TODO: Should check that the output includes that the update was applied to + # the destination + mc['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % source_path, match_all=False + ) + mc['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + mc['status'].tweak(source, status='D ', moved_to=dest) + mc['status'].add({dest: Item(status='A ', moved_from=source, + copied='+', wc_rev='-')}) + mc['disk'] = test['up_disk'].copy() + mc['disk'].tweak(dest, props={}) + # theirs-conflict doesn't work + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + working['output'] = svntest.verify.ExpectedOutput( + [ + "Breaking move with source path '%s'\n" % source_path, + "Resolved conflicted state of '%s'\n" % source_path + ] + ) + # XXX: working breaks the move? Is that right? + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].tweak(source, status='D ') + working['status'].add({dest: Item(status='A ', copied='+', wc_rev='-')}) + working['disk'] = test['up_disk'] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [source_path, dest_path] + tests.append(test) + + # move and update with incoming identical move (r16-17) + # XXX: It'd be really nice if we actually recognized this and the wc + # showed no conflict at all on udpate. + test = {} + test['start_rev'] = 16 + test['end_rev'] = 17 + test['start_output'] = None + test['start_disk'] = None + test['start_status'] = None + test['up_output'] = svntest.wc.State(wc_dir, { + source : Item(status=' ', treeconflict='C'), + dest : Item(status=' ', treeconflict='C'), + }) + test['up_disk'] = svntest.main.greek_state.copy() + test['up_disk'].add({ + dest: Item("This is the replaced file.\n") + }) + test['up_disk'].remove(source) + test['up_status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + test['up_status'].tweak(source, status='! ', treeconflict='C', wc_rev=None) + test['up_status'].add({dest: Item(status='R ', copied='+', wc_rev='-', + treeconflict='C')}) + # mine-conflict and theirs-conflict don't work. + mc = {} + mc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + mc['status'] = test['up_status'] + mc['disk'] = test['up_disk'] + tc = {} + tc['error'] = svntest.verify.RegexOutput(".*: .*: W155027:.*", match_all=False) + tc['status'] = test['up_status'] + tc['disk'] = test['up_disk'] + working = {} + working['output'] = svntest.verify.ExpectedOutput( + "Resolved conflicted state of '%s'\n" % source_path, match_all=False + ) + # move is broken now + working['status'] = svntest.actions.get_virginal_state(wc_dir, test['end_rev']) + working['status'].add({dest: Item(status='R ', copied='+', wc_rev='-')}) + working['status'].remove(source) + working['disk'] = test['up_disk'] + working['revert_paths'] = [dest_path] + test['resolves'] = {'mine-conflict': mc, 'theirs-conflict': tc, + 'working': working} + test['revert_paths'] = [dest_path, source_path] + tests.append(test) + + return tests + +def build_simple_file_move_func(sbox, source, dest): + wc_dir = sbox.wc_dir + source_path = sbox.ospath(source) + dest_path = sbox.ospath(dest) + + # Setup the move function + def move_func(rev): + # execute the move + svntest.actions.run_and_verify_svn(None, None, [], "move", + source_path, dest_path) + if move_func.extra_mv_tests: + mv_status = svntest.actions.get_virginal_state(wc_dir, rev) + mv_status.tweak(source, status='D ', moved_to=dest) + mv_status.add({dest: Item(status='A ', moved_from=source, + copied='+', wc_rev='-')}) + mv_info_src = [ + { + 'Path' : re.escape(source_path), + 'Moved To' : re.escape(dest), + } + ] + mv_info_dst = [ + { + 'Path' : re.escape(dest_path), + 'Moved From' : re.escape(source), + } + ] + + # check the status output. + svntest.actions.run_and_verify_status(wc_dir, mv_status) + + # check the info output + svntest.actions.run_and_verify_info(mv_info_src, source_path) + svntest.actions.run_and_verify_info(mv_info_dst, dest_path) + move_func.extra_mv_tests = False + + # Do the status and info tests the first time through + # No reason to repeat these tests for each of the variations below + # since the move is exactly the same. + move_func.extra_mv_tests = True + + return move_func + +###################################################################### +# Tests +# +# Each test must return on success or raise on failure. +# +# See http://wiki.apache.org/subversion/LocalMoves + +def lateral_move_file_test(sbox): + "lateral (rename) move of a file test" + sbox.build() + + # Plan to test moving A/B/lambda to A/B/lambda-moved + source = 'A/B/lambda' + dest = 'A/B/lambda-moved' + + # Build the revisions to do the updates via + build_incoming_changes_file(sbox, source, dest) + + # Get function to implement the actual move + move_func = build_simple_file_move_func(sbox, source, dest) + + # Get the test plan + tests = build_simple_file_move_tests(sbox, source, dest) + + # Actually run the tests + move_file_tests(sbox, source, dest, move_func, tests) + +def sibling_move_file_test(sbox): + "sibling move of a file test" + sbox.build() + + # Plan to test moving A/B/lambda to A/C/lambda + source = 'A/B/lambda' + dest = 'A/C/lambda' + + # Build the revisions to do the updates via + build_incoming_changes_file(sbox, source, dest) + + # Get function to implement the actual move + move_func = build_simple_file_move_func(sbox, source, dest) + + # Get the test plan + tests = build_simple_file_move_tests(sbox, source, dest) + + # Actually run the tests + move_file_tests(sbox, source, dest, move_func, tests) + +def shallower_move_file_test(sbox): + "shallower move of a file test" + sbox.build() + + # Plan to test moving A/B/lambda to A/lambda + source = 'A/B/lambda' + dest = 'A/lambda' + + # Build the revisions to do the updates via + build_incoming_changes_file(sbox, source, dest) + + # Get function to implement the actual move + move_func = build_simple_file_move_func(sbox, source, dest) + + # Get the test plan + tests = build_simple_file_move_tests(sbox, source, dest) + + # Actually run the tests + move_file_tests(sbox, source, dest, move_func, tests) + +def deeper_move_file_test(sbox): + "deeper move of a file test" + sbox.build() + + # Plan to test moving A/B/lambda to A/B/F/lambda + source = 'A/B/lambda' + dest = 'A/B/F/lambda' + + # Build the revisions to do the updates via + build_incoming_changes_file(sbox, source, dest) + + # Get function to implement the actual move + move_func = build_simple_file_move_func(sbox, source, dest) + + # Get the test plan + tests = build_simple_file_move_tests(sbox, source, dest) + + # Actually run the tests + move_file_tests(sbox, source, dest, move_func, tests) + + +def property_merge(sbox): + "test property merging on move-update" + + # pristine local incoming outcome revert + # 1 p1 v2 p2 v2 p1 v2, p2 v2 p2 v2 + # 2 p1 v1 p1 v2 p2 v2 p1 v2, p2 v2 p1 v1 p2 v2 + # 3 p1 v1 p1 v2 p1 v2 p1 v2 p1 v2 + # 4 p1 v2 p1 v3 p1 v2 conflict p1 v3 + # 5 p1 v1 p1 v2 p1 v3 p1 v2 conflict p1 v3 + + sbox.build() + wc_dir = sbox.wc_dir + + sbox.simple_mkdir('A/C/D1') + sbox.simple_mkdir('A/C/D2') + sbox.simple_mkdir('A/C/D3') + sbox.simple_mkdir('A/C/D4') + sbox.simple_mkdir('A/C/D5') + sbox.simple_add_text('content of f1', 'A/C/f1') + sbox.simple_add_text('content of f2', 'A/C/f2') + sbox.simple_add_text('content of f3', 'A/C/f3') + sbox.simple_add_text('content of f4', 'A/C/f4') + sbox.simple_add_text('content of f5', 'A/C/f5') + sbox.simple_propset('key1', 'value1', + 'A/C/D2', 'A/C/D3', 'A/C/D5', + 'A/C/f2', 'A/C/f3', 'A/C/f5') + sbox.simple_commit() + sbox.simple_propset('key2', 'value2', + 'A/C/D1', 'A/C/D2', + 'A/C/f1', 'A/C/f2') + sbox.simple_propset('key1', 'value2', + 'A/C/D3', + 'A/C/f3') + sbox.simple_propset('key1', 'value3', + 'A/C/D4', 'A/C/D5', + 'A/C/f4', 'A/C/f5') + sbox.simple_commit() + sbox.simple_update('', 2) + sbox.simple_propset('key1', 'value2', + 'A/C/D1', 'A/C/D2', 'A/C/D3', 'A/C/D4', 'A/C/D5', + 'A/C/f1', 'A/C/f2', 'A/C/f3', 'A/C/f4', 'A/C/f5') + sbox.simple_move('A/C', 'A/C2') + + expected_status = svntest.actions.get_virginal_state(wc_dir, 2) + expected_status.tweak('A/C', status='D ', moved_to='A/C2') + expected_status.add({ + 'A/C/D1' : Item(status='D ', wc_rev=2), + 'A/C/D2' : Item(status='D ', wc_rev=2), + 'A/C/D3' : Item(status='D ', wc_rev=2), + 'A/C/D4' : Item(status='D ', wc_rev=2), + 'A/C/D5' : Item(status='D ', wc_rev=2), + 'A/C/f1' : Item(status='D ', wc_rev=2), + 'A/C/f2' : Item(status='D ', wc_rev=2), + 'A/C/f3' : Item(status='D ', wc_rev=2), + 'A/C/f4' : Item(status='D ', wc_rev=2), + 'A/C/f5' : Item(status='D ', wc_rev=2), + 'A/C2' : Item(status='A ', copied='+', wc_rev='-', moved_from='A/C'), + 'A/C2/D1' : Item(status=' M', copied='+', wc_rev='-'), + 'A/C2/D2' : Item(status=' M', copied='+', wc_rev='-'), + 'A/C2/D3' : Item(status=' M', copied='+', wc_rev='-'), + 'A/C2/D4' : Item(status=' M', copied='+', wc_rev='-'), + 'A/C2/D5' : Item(status=' M', copied='+', wc_rev='-'), + 'A/C2/f1' : Item(status=' M', copied='+', wc_rev='-'), + 'A/C2/f2' : Item(status=' M', copied='+', wc_rev='-'), + 'A/C2/f3' : Item(status=' M', copied='+', wc_rev='-'), + 'A/C2/f4' : Item(status=' M', copied='+', wc_rev='-'), + 'A/C2/f5' : Item(status=' M', copied='+', wc_rev='-'), + }) + svntest.actions.run_and_verify_status(wc_dir, expected_status) + + sbox.simple_update() + svntest.actions.run_and_verify_svn("resolve failed", None, [], + 'resolve', + '--accept=mine-conflict', + sbox.ospath('A/C')) + + expected_status.tweak(wc_rev=3) + expected_status.tweak('A/C2', + 'A/C2/D1', 'A/C2/D2', 'A/C2/D3', 'A/C2/D4', 'A/C2/D5', + 'A/C2/f1', 'A/C2/f2', 'A/C2/f3', 'A/C2/f4', 'A/C2/f5', + wc_rev='-') + expected_status.tweak('A/C2/D3', + 'A/C2/f3', + status=' ') + expected_status.tweak('A/C2/D4', 'A/C2/D5', + 'A/C2/f4', 'A/C2/f5', + status=' C') + + svntest.actions.run_and_verify_status(wc_dir, expected_status) + + expected_disk = svntest.main.greek_state.copy() + expected_disk.remove('A/C') + expected_disk.add({ + 'A/C2' : Item(), + 'A/C2/D1' : Item(props={'key1' : 'value2', 'key2' : 'value2'}), + 'A/C2/D2' : Item(props={'key1' : 'value2', 'key2' : 'value2'}), + 'A/C2/D3' : Item(props={'key1' : 'value2'}), + 'A/C2/D4' : Item(props={'key1' : 'value2'}), + 'A/C2/D5' : Item(props={'key1' : 'value2'}), + 'A/C2/f1' : Item(contents='content of f1', + props={'key1' : 'value2', 'key2' : 'value2'}), + 'A/C2/f2' : Item(contents='content of f2', + props={'key1' : 'value2', 'key2' : 'value2'}), + 'A/C2/f3' : Item(contents='content of f3', + props={'key1' : 'value2'}), + 'A/C2/f4' : Item(contents='content of f4', + props={'key1' : 'value2'}), + 'A/C2/f5' : Item(contents='content of f5', + props={'key1' : 'value2'}), + 'A/C2/D4/dir_conflicts.prej' : Item(contents= +"""Trying to add new property 'key1' +but the property already exists. +<<<<<<< (local property value) +value2======= +value3>>>>>>> (incoming property value) +"""), + 'A/C2/D5/dir_conflicts.prej' : Item(contents= +"""Trying to change property 'key1' +but the property has already been locally changed to a different value. +<<<<<<< (local property value) +value2======= +value3>>>>>>> (incoming property value) +"""), + 'A/C2/f4.prej' : Item(contents= +"""Trying to add new property 'key1' +but the property already exists. +<<<<<<< (local property value) +value2======= +value3>>>>>>> (incoming property value) +"""), + 'A/C2/f5.prej' : Item(contents= +"""Trying to change property 'key1' +but the property has already been locally changed to a different value. +<<<<<<< (local property value) +value2======= +value3>>>>>>> (incoming property value) +"""), + }) + + svntest.actions.verify_disk(wc_dir, expected_disk, True) + + sbox.simple_revert('A/C2/D1', 'A/C2/D2', 'A/C2/D4', 'A/C2/D5', + 'A/C2/f1', 'A/C2/f2', 'A/C2/f4', 'A/C2/f5') + + expected_status.tweak('A/C2/D1', 'A/C2/D2', 'A/C2/D4', 'A/C2/D5', + 'A/C2/f1', 'A/C2/f2', 'A/C2/f4', 'A/C2/f5', + status=' ') + svntest.actions.run_and_verify_status(wc_dir, expected_status) + + expected_disk.remove('A/C2/D4/dir_conflicts.prej', + 'A/C2/D5/dir_conflicts.prej', + 'A/C2/f4.prej', + 'A/C2/f5.prej') + expected_disk.tweak('A/C2/D1', + 'A/C2/f1', + props={'key2' : 'value2'}) + expected_disk.tweak('A/C2/D2', + 'A/C2/f2', + props={'key1' : 'value1', 'key2' : 'value2'}) + expected_disk.tweak('A/C2/D4', 'A/C2/D5', + 'A/C2/f4', 'A/C2/f5', + props={'key1' : 'value3'}) + svntest.actions.verify_disk(wc_dir, expected_disk, True) + + +@Issue(4356) +def move_missing(sbox): + "move a missing directory" + + sbox.build(read_only=True) + wc_dir = sbox.wc_dir + + svntest.main.safe_rmtree(sbox.ospath('A/D/G')) + + expected_err = '.*Can\'t move \'.*G\' to \'.*R\':.*' + + # This move currently fails halfway between adding the dest and + # deleting the source + svntest.actions.run_and_verify_svn(None, None, expected_err, + 'mv', sbox.ospath('A/D/G'), + sbox.ospath('R')) + + expected_status = svntest.actions.get_virginal_state(wc_dir, 1) + expected_status.tweak('A/D/G', 'A/D/G/tau', 'A/D/G/pi', 'A/D/G/rho', + status='! ', entry_status=' ') + + expected_status.add({ + 'R' : Item(status='! ', wc_rev='-', + entry_status='A ', entry_copied='+'), + 'R/pi' : Item(status='! ', wc_rev='-', + entry_status=' ', entry_copied='+'), + 'R/tau' : Item(status='! ', wc_rev='-', + entry_status=' ', entry_copied='+'), + 'R/rho' : Item(status='! ', wc_rev='-', + entry_status=' ', entry_copied='+'), + }) + + # Verify that the status processing doesn't crash + svntest.actions.run_and_verify_status(wc_dir, expected_status) + + # The issue is a crash when the destination is present + os.mkdir(sbox.ospath('R')) + expected_status.tweak('R', status='A ', copied='+') + + svntest.actions.run_and_verify_status(wc_dir, expected_status) + + +def setup_move_many(sbox): + "helper function which creates a wc with node A/A/A which is moved 3 times" + + sbox.simple_rm('A', 'iota') + sbox.simple_mkdir('A', + 'A/A', + 'A/A/A', + 'A/A/A/A', + 'B', + 'B/A', + 'B/A/A', + 'B/A/A/A', + 'C', + 'C/A', + 'C/A/A', + 'C/A/A/A') + sbox.simple_commit() + sbox.simple_update() + + sbox.simple_move('A/A/A', 'AAA_1') + + sbox.simple_rm('A') + sbox.simple_move('B', 'A') + + sbox.simple_move('A/A/A', 'AAA_2') + + sbox.simple_rm('A/A') + sbox.simple_move('C/A', 'A/A') + + sbox.simple_move('A/A/A', 'AAA_3') + +def move_many_status(wc_dir): + "obtain standard status after setup_move_many" + + return svntest.wc.State(wc_dir, { + '' : Item(status=' ', wc_rev='2'), + + 'AAA_1' : Item(status='A ', copied='+', moved_from='A/A/A', wc_rev='-'), + 'AAA_1/A' : Item(status=' ', copied='+', wc_rev='-'), + + 'AAA_2' : Item(status='A ', copied='+', moved_from='A/A/A', wc_rev='-'), + 'AAA_2/A' : Item(status=' ', copied='+', wc_rev='-'), + + 'AAA_3' : Item(status='A ', copied='+', moved_from='A/A/A', wc_rev='-'), + 'AAA_3/A' : Item(status=' ', copied='+', wc_rev='-'), + + 'A' : Item(status='R ', copied='+', moved_from='B', wc_rev='-'), + 'A/A' : Item(status='R ', copied='+', moved_from='C/A', wc_rev='-'), + 'A/A/A' : Item(status='D ', copied='+', wc_rev='-', moved_to='AAA_3'), + 'A/A/A/A' : Item(status='D ', copied='+', wc_rev='-'), + + 'B' : Item(status='D ', wc_rev='2', moved_to='A'), + 'B/A' : Item(status='D ', wc_rev='2'), + 'B/A/A' : Item(status='D ', wc_rev='2'), + 'B/A/A/A' : Item(status='D ', wc_rev='2'), + + 'C' : Item(status=' ', wc_rev='2'), + 'C/A' : Item(status='D ', wc_rev='2', moved_to='A/A'), + 'C/A/A' : Item(status='D ', wc_rev='2'), + 'C/A/A/A' : Item(status='D ', wc_rev='2'), + }) + +def move_many_update_delete(sbox): + "move many and delete-on-update" + + sbox.build() + setup_move_many(sbox) + + wc_dir = sbox.wc_dir + + # Verify start situation + expected_status = move_many_status(wc_dir) + svntest.actions.run_and_verify_status(wc_dir, expected_status) + + # And now create a tree conflict + svntest.actions.run_and_verify_svn(None, None, [], + 'rm', sbox.repo_url + '/B', + '-m', '') + + expected_output = svntest.wc.State(wc_dir, { + 'B' : Item(status=' ', treeconflict='C'), + }) + + + expected_status.tweak('', 'C', 'C/A', 'C/A/A', 'C/A/A/A', wc_rev='3') + expected_status.tweak('A', moved_from=None) + expected_status.remove('B/A', 'B/A/A', 'B/A/A/A') + expected_status.tweak('B', status='! ', treeconflict='C', wc_rev=None, moved_to=None) + + svntest.actions.run_and_verify_update(wc_dir, expected_output, None, + expected_status) + + # Would be nice if we could run the resolver as a separate step, + # but 'svn resolve' just fails for any value but working + +@XFail() +def move_many_update_add(sbox): + "move many and add-on-update" + + sbox.build() + setup_move_many(sbox) + + wc_dir = sbox.wc_dir + + # Verify start situation + expected_status = move_many_status(wc_dir) + #svntest.actions.run_and_verify_status(wc_dir, expected_status) + + # And now create a tree conflict + svntest.actions.run_and_verify_svn(None, None, [], + 'mkdir', sbox.repo_url + '/B/A/A/BB', + '-m', '') + + expected_output = svntest.wc.State(wc_dir, { + 'B' : Item(status=' ', treeconflict='C'), + 'B/A' : Item(status=' ', treeconflict='U'), + 'B/A/A' : Item(status=' ', treeconflict='U'), + 'B/A/A/BB' : Item(status=' ', treeconflict='A'), + # And while resolving + 'A/A/' : Item(status=' ', treeconflict='C') + }) + + expected_status.tweak('', + 'B', 'B/A', 'B/A/A', 'B/A/A/A', + 'C', 'C/A', 'C/A/A', 'C/A/A/A', + wc_rev='3') + + expected_status.tweak('A/A', treeconflict='C') + expected_status.add({ + 'A/A/A/BB' : Item(status='D ', copied='+', wc_rev='-'), + 'B/A/A/BB' : Item(status='D ', wc_rev='3'), + }) + + svntest.actions.run_and_verify_update(wc_dir, expected_output, None, + expected_status, + None, None, None, + None, None, None, + wc_dir, '--accept', 'mine-conflict') + + # And another one + svntest.actions.run_and_verify_svn(None, None, [], + 'mkdir', sbox.repo_url + '/C/A/A/BB', + '-m', '') + + expected_status.tweak('', + 'B', 'B/A', 'B/A/A', 'B/A/A/A', + 'C', 'C/A', 'C/A/A', 'C/A/A/A', + wc_rev='4') + + expected_output = svntest.wc.State(wc_dir, { + 'C/A' : Item(status=' ', treeconflict='C'), + 'C/A/A' : Item(status=' ', treeconflict='U'), + 'C/A/A/BB' : Item(status=' ', treeconflict='A'), + }) + + # This currently triggers an assertion failure + svntest.actions.run_and_verify_update(wc_dir, expected_output, None, + expected_status, + None, None, None, + None, None, None, + wc_dir, '--accept', 'mine-conflict') + +@Issue(4437) +def move_del_moved(sbox): + "delete moved node, still a move" + sbox.build() + wc_dir = sbox.wc_dir + + sbox.simple_mkdir('A/NEW') + sbox.simple_move('A/mu', 'A/NEW/mu') + sbox.simple_rm('A/NEW/mu') + + expected_status = svntest.actions.get_virginal_state(wc_dir, 1) + expected_status.tweak('A/mu', status='D ') + expected_status.add({ + 'A/NEW' : Item(status='A ', wc_rev='-') + }) + + # A/mu still reports that it is moved to A/NEW/mu, while it is already + # deleted there. + svntest.actions.run_and_verify_status(wc_dir, expected_status) + +def copy_move_commit(sbox): + "copy, move and commit" + + sbox.build() + wc_dir = sbox.wc_dir + #repro + # Prepare + # - Create folder aaa + # - Add file bbb.sql + # create table bbb (Id int not null) + # - Commit + # Repro Issue 2 + # - Copy folder aaa under same parent folder (i.e. as a sibling). (using Ctrl drag/drop). + # Creates Copy of aaa + # - Rename Copy of aaa to eee + # - Commit + # Get error need to update + # - Update + # - Commit + # Get error need to update + + sbox.simple_copy('A/D/G', 'A/D/GG') + sbox.simple_move('A/D/GG', 'A/D/GG-moved') + sbox.simple_commit('A/D/GG-moved') + + +def move_to_from_external(sbox): + "move to and from an external" + + sbox.build() + sbox.simple_propset('svn:externals', '^/A/D/G GG', '') + sbox.simple_update() + + svntest.actions.run_and_verify_svn(None, None, [], + 'move', + sbox.ospath('GG/tau'), + sbox.ospath('tau')) + + svntest.actions.run_and_verify_svn(None, None, [], + 'move', + sbox.ospath('iota'), + sbox.ospath('GG/tau')) + + svntest.actions.run_and_verify_svn(None, None, [], + 'ci', '-m', 'Commit both', + sbox.ospath(''), + sbox.ospath('GG')) + +def move_conflict_markers(sbox): + "move conflict markers" + + sbox.build() + wc_dir = sbox.wc_dir + sbox.simple_propset('key','val', 'iota', 'A/B/E', 'A/B/E/beta') + sbox.simple_commit() + sbox.simple_update('', 1) + sbox.simple_propset('key','false', 'iota', 'A/B/E', 'A/B/E/beta') + + expected_output = svntest.wc.State(wc_dir, { + 'A/B/E' : Item(status=' C'), + 'A/B/E/beta' : Item(status=' C'), + 'iota' : Item(status=' C'), + }) + expected_status = svntest.actions.get_virginal_state(wc_dir, 2) + expected_status.tweak('iota', 'A/B/E', 'A/B/E/beta', status=' C') + expected_disk = svntest.main.greek_state.copy() + expected_disk.add({ + 'A/B/E/dir_conflicts.prej' : Item(contents= + "Trying to add new property 'key'\n" + "but the property already exists.\n" + "<<<<<<< (local property value)\n" + "false=======\n" + "val>>>>>>> (incoming property value)\n"), + 'A/B/E/beta.prej' : Item(contents= + "Trying to add new property 'key'\n" + "but the property already exists.\n" + "<<<<<<< (local property value)\n" + "false=======\n" + "val>>>>>>> (incoming property value)\n"), + 'iota.prej' : Item(contents= + "Trying to add new property 'key'\n" + "but the property already exists.\n" + "<<<<<<< (local property value)\n" + "false=======\n" + "val>>>>>>> (incoming property value)\n"), + }) + svntest.actions.run_and_verify_update(wc_dir, + expected_output, + expected_disk, + expected_status) + + sbox.simple_move('iota', 'A/iotb') + sbox.simple_move('A/B/E', 'E') + + expected_status.tweak('iota', status='D ', moved_to='A/iotb') + expected_status.tweak('A/B/E', status='D ', moved_to='E') + expected_status.tweak('A/B/E/alpha', 'A/B/E/beta', status='D ') + expected_status.add({ + 'A/iotb' : Item(status='A ', copied='+', moved_from='iota', wc_rev='-'), + 'E' : Item(status='A ', copied='+', moved_from='A/B/E', wc_rev='-'), + 'E/beta' : Item(status=' M', copied='+', wc_rev='-'), + 'E/alpha' : Item(status=' ', copied='+', wc_rev='-'), + }) + expected_disk.remove('iota', 'iota.prej', + 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', + 'A/B/E/dir_conflicts.prej', + 'A/B/E/beta.prej') + expected_disk.add({ + 'A/iotb' : Item(contents="This is the file 'iota'.\n"), + 'E/beta' : Item(contents="This is the file 'beta'.\n"), + 'E/alpha' : Item(contents="This is the file 'alpha'.\n"), + }) + svntest.actions.run_and_verify_status(wc_dir, expected_status) + svntest.actions.verify_disk(wc_dir, expected_disk) + +####################################################################### +# Run the tests + +# list all tests here, starting with None: +test_list = [ None, + lateral_move_file_test, + sibling_move_file_test, + shallower_move_file_test, + deeper_move_file_test, + property_merge, + move_missing, + move_many_update_delete, + move_many_update_add, + move_del_moved, + copy_move_commit, + move_to_from_external, + move_conflict_markers, + ] + +if __name__ == '__main__': + svntest.main.run_tests(test_list) + # NOTREACHED + + +### End of file. |