| #!/usr/bin/env python |
| # |
| # switch_tests.py: testing `svn switch'. |
| # |
| # 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 shutil, re, os |
| |
| # Our testing module |
| import svntest |
| from svntest import verify, actions, main |
| |
| # (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 |
| |
| from svntest.main import SVN_PROP_MERGEINFO, server_has_mergeinfo |
| from externals_tests import change_external |
| from svntest.deeptrees import do_routine_switching |
| |
| #---------------------------------------------------------------------- |
| |
| def relocate_deleted_missing_copied(sbox): |
| "relocate with deleted, missing and copied entries" |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Delete A/mu to create a deleted entry for mu in A/.svn/entries |
| mu_path = os.path.join(wc_dir, 'A', 'mu') |
| svntest.actions.run_and_verify_svn(None, [], 'rm', mu_path) |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.remove('A/mu') |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/mu' : Item(verb='Deleting'), |
| }) |
| svntest.actions.run_and_verify_commit(wc_dir, |
| expected_output, |
| expected_status) |
| |
| # Remove A/B/F to create a missing entry |
| svntest.main.safe_rmtree(os.path.join(wc_dir, 'A', 'B', 'F')) |
| |
| # Copy A/D to A/D2 |
| D_path = os.path.join(wc_dir, 'A', 'D') |
| D2_path = os.path.join(wc_dir, 'A', 'D2') |
| svntest.actions.run_and_verify_svn(None, [], 'copy', |
| D_path, D2_path) |
| # Delete within the copy |
| D2G_path = os.path.join(wc_dir, 'A', 'D2', 'G') |
| svntest.actions.run_and_verify_svn(None, [], 'rm', D2G_path) |
| |
| expected_status.add({ |
| 'A/D2' : Item(status='A ', wc_rev='-', copied='+'), |
| 'A/D2/gamma' : Item(status=' ', wc_rev='-', copied='+'), |
| 'A/D2/G' : Item(status='D ', wc_rev='-', copied='+'), |
| 'A/D2/G/pi' : Item(status='D ', wc_rev='-', copied='+'), |
| 'A/D2/G/rho' : Item(status='D ', wc_rev='-', copied='+'), |
| 'A/D2/G/tau' : Item(status='D ', wc_rev='-', copied='+'), |
| 'A/D2/H' : Item(status=' ', wc_rev='-', copied='+'), |
| 'A/D2/H/chi' : Item(status=' ', wc_rev='-', copied='+'), |
| 'A/D2/H/omega' : Item(status=' ', wc_rev='-', copied='+'), |
| 'A/D2/H/psi' : Item(status=' ', wc_rev='-', copied='+'), |
| }) |
| expected_status.tweak('A/B/F', status='! ', wc_rev='1') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # Relocate |
| repo_dir = sbox.repo_dir |
| repo_url = sbox.repo_url |
| other_repo_dir, other_repo_url = sbox.add_repo_path('other') |
| svntest.main.copy_repos(repo_dir, other_repo_dir, 2, 0) |
| svntest.main.safe_rmtree(repo_dir, 1) |
| svntest.actions.run_and_verify_svn(None, [], 'switch', '--relocate', |
| repo_url, other_repo_url, wc_dir) |
| |
| # Deleted and missing entries should be preserved, so update should |
| # show only A/B/F being reinstated |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/B/F' : Item(verb='Restored'), |
| }) |
| |
| expected_disk = svntest.main.greek_state.copy() |
| expected_disk.remove('A/mu') |
| expected_disk.add({ |
| 'A/D2' : Item(), |
| 'A/D2/gamma' : Item("This is the file 'gamma'.\n"), |
| 'A/D2/H' : Item(), |
| 'A/D2/H/chi' : Item("This is the file 'chi'.\n"), |
| 'A/D2/H/omega' : Item("This is the file 'omega'.\n"), |
| 'A/D2/H/psi' : Item("This is the file 'psi'.\n"), |
| }) |
| |
| expected_status.add({ |
| 'A/B/F' : Item(status=' ', wc_rev='2'), |
| }) |
| expected_status.tweak(wc_rev=2) |
| expected_status.tweak('A/D2', 'A/D2/gamma', |
| 'A/D2/H', 'A/D2/H/chi', 'A/D2/H/omega', 'A/D2/H/psi', |
| wc_rev='-') |
| expected_status.tweak('A/D2/G', 'A/D2/G/pi', 'A/D2/G/rho', 'A/D2/G/tau', |
| copied='+', wc_rev='-') |
| svntest.actions.run_and_verify_update(wc_dir, |
| expected_output, |
| expected_disk, |
| expected_status) |
| |
| # Commit to verify that copyfrom URLs have been relocated |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/D2' : Item(verb='Adding'), |
| 'A/D2/G' : Item(verb='Deleting'), |
| }) |
| expected_status.tweak('A/D2', 'A/D2/gamma', |
| 'A/D2/H', 'A/D2/H/chi', 'A/D2/H/omega', 'A/D2/H/psi', |
| status=' ', wc_rev='3', copied=None) |
| expected_status.remove('A/D2/G', 'A/D2/G/pi', 'A/D2/G/rho', 'A/D2/G/tau') |
| svntest.actions.run_and_verify_commit(wc_dir, |
| expected_output, expected_status) |
| |
| #---------------------------------------------------------------------- |
| |
| @Issue(2380) |
| def relocate_beyond_repos_root(sbox): |
| "relocate with prefixes longer than repo root" |
| sbox.build(read_only=True, create_wc=False) |
| |
| wc_backup = sbox.add_wc_path('backup') |
| |
| wc_dir = sbox.wc_dir |
| repo_dir = sbox.repo_dir |
| repo_url = sbox.repo_url |
| other_repo_dir, other_repo_url = sbox.add_repo_path('other') |
| A_url = repo_url + "/A" |
| A_wc_dir = wc_dir |
| other_A_url = other_repo_url + "/A" |
| other_B_url = other_repo_url + "/B" |
| |
| svntest.main.safe_rmtree(wc_dir, 1) |
| svntest.actions.run_and_verify_svn(None, [], 'checkout', |
| repo_url + '/A', wc_dir) |
| |
| svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 0) |
| |
| # A relocate that changes the repo path part of the URL shouldn't work. |
| # This tests for issue #2380. |
| svntest.actions.run_and_verify_svn(None, |
| ".*Invalid relocation destination.*", |
| 'relocate', |
| A_url, other_B_url, A_wc_dir) |
| |
| # Another way of trying to change the fs path, leading to an invalid |
| # repository root. |
| svntest.actions.run_and_verify_svn(None, |
| ".*is not the root.*", |
| 'relocate', |
| repo_url, other_B_url, A_wc_dir) |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'relocate', |
| A_url, other_A_url, A_wc_dir) |
| |
| # Check that we can contact the repository, meaning that the |
| # relocate actually changed the URI. Escape the expected URI to |
| # avoid problems from any regex meta-characters it may contain |
| # (e.g. '+'). |
| expected_infos = [ |
| { 'URL' : re.escape(other_A_url) + '$', |
| 'Path' : '.+', |
| 'Repository UUID' : '.+', |
| 'Revision' : '.+', |
| 'Node Kind' : '.+', |
| 'Last Changed Date' : '.+' }, |
| ] |
| svntest.actions.run_and_verify_info(expected_infos, A_wc_dir, '-rHEAD') |
| |
| #---------------------------------------------------------------------- |
| # Issue 2578. |
| def relocate_and_propset(sbox): |
| "out of date propset should fail after a relocate" |
| |
| # Create virgin repos and working copy |
| svntest.main.safe_rmtree(sbox.repo_dir, 1) |
| svntest.main.create_repos(sbox.repo_dir) |
| svntest.actions.guarantee_greek_repository( |
| sbox.repo_dir, svntest.main.options.server_minor_version) |
| |
| wc_dir = sbox.wc_dir |
| repo_dir = sbox.repo_dir |
| repo_url = sbox.repo_url |
| |
| # checkout |
| svntest.main.safe_rmtree(wc_dir, 1) |
| svntest.actions.run_and_verify_svn(None, [], |
| 'checkout', |
| repo_url, wc_dir) |
| |
| # Relocate |
| other_repo_dir, other_repo_url = sbox.add_repo_path('other') |
| svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 0) |
| svntest.main.safe_rmtree(repo_dir, 1) |
| svntest.actions.run_and_verify_svn(None, [], 'relocate', |
| repo_url, other_repo_url, wc_dir) |
| |
| # Remove gamma from the working copy. |
| D_path = os.path.join(wc_dir, 'A', 'D') |
| gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma') |
| svntest.main.run_svn(None, 'rm', gamma_path) |
| |
| # Create expected commit output. |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/D/gamma' : Item(verb='Deleting'), |
| }) |
| |
| # After committing, status should show no sign of gamma. |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.remove('A/D/gamma') |
| |
| # Commit the deletion of gamma and verify. |
| svntest.actions.run_and_verify_commit(wc_dir, |
| expected_output, |
| expected_status) |
| |
| # Now gamma should be marked as `deleted' under the hood, at |
| # revision 2. Meanwhile, A/D is still lagging at revision 1. |
| |
| # Make a propchange on A/D |
| svntest.main.run_svn(None, 'ps', 'foo', 'bar', D_path) |
| |
| # Commit and *expect* a repository Merge failure: |
| svntest.actions.run_and_verify_commit(wc_dir, |
| None, |
| None, |
| ".*[Oo]ut of date.*") |
| |
| #---------------------------------------------------------------------- |
| |
| def single_file_relocate(sbox): |
| "relocate a single file" |
| |
| # Create virgin repos and working copy |
| svntest.main.safe_rmtree(sbox.repo_dir, 1) |
| svntest.actions.guarantee_greek_repository( |
| sbox.repo_dir, svntest.main.options.server_minor_version) |
| |
| wc_dir = sbox.wc_dir |
| iota_path = os.path.join(sbox.wc_dir, 'iota') |
| repo_dir = sbox.repo_dir |
| repo_url = sbox.repo_url |
| iota_url = repo_url + '/iota' |
| greek_dump_dir = sbox.add_wc_path('greek-dump') |
| |
| # checkout |
| svntest.main.safe_rmtree(wc_dir, 1) |
| svntest.actions.run_and_verify_svn(None, [], |
| 'checkout', |
| repo_url, wc_dir) |
| |
| # Relocate |
| other_repo_dir, other_repo_url = sbox.add_repo_path('other') |
| other_iota_url = other_repo_url + '/iota' |
| svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 0) |
| svntest.main.safe_rmtree(repo_dir, 1) |
| svntest.actions.run_and_verify_svn(None, |
| ".*Cannot relocate.*", |
| 'relocate', |
| iota_url, other_iota_url, iota_path) |
| |
| #---------------------------------------------------------------------- |
| |
| def relocate_with_switched_children(sbox): |
| "relocate a directory with switched children" |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Setup (and verify) some switched things |
| do_routine_switching(sbox.wc_dir, sbox.repo_url, False) |
| |
| # Relocate |
| repo_dir = sbox.repo_dir |
| repo_url = sbox.repo_url |
| other_repo_dir, other_repo_url = sbox.add_repo_path('other') |
| svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 0) |
| svntest.main.safe_rmtree(repo_dir, 1) |
| |
| # Do the switch and check the results in three ways. |
| svntest.actions.run_and_verify_svn(None, [], 'relocate', |
| repo_url, other_repo_url, wc_dir) |
| |
| # Attempt to commit changes and examine results |
| expected_output = svntest.wc.State(wc_dir, { }) |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.tweak('A/B', 'iota', |
| switched='S') |
| expected_status.remove('A/B/E', 'A/B/F', 'A/B/E/alpha', 'A/B/E/beta', |
| 'A/B/lambda') |
| expected_status.add({ |
| 'A/B/pi' : Item(status=' ', wc_rev='1'), |
| 'A/B/rho' : Item(status=' ', wc_rev='1'), |
| 'A/B/tau' : Item(status=' ', wc_rev='1'), |
| }) |
| |
| # This won't actually do a commit, because nothing should be modified. |
| svntest.actions.run_and_verify_commit(wc_dir, |
| expected_output, expected_status) |
| |
| # Check the URLs of various nodes. |
| info_output = { |
| wc_dir: '.*.other$', |
| os.path.join(wc_dir, 'iota'): '.*.other/A/D/gamma$', |
| os.path.join(wc_dir, 'A', 'B'): '.*.other/A/D/G$', |
| os.path.join(wc_dir, 'A', 'B', 'pi'): '.*.other/A/D/G/pi$', |
| } |
| |
| for path, pattern in info_output.items(): |
| expected_info = { 'URL' : pattern } |
| svntest.actions.run_and_verify_info([expected_info], path) |
| |
| #---------------------------------------------------------------------- |
| |
| |
| ### regression test for issue #3597 |
| @Issue(3597) |
| def relocate_with_relative_externals(sbox): |
| "relocate a directory containing relative externals" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| repo_dir = sbox.repo_dir |
| repo_url = sbox.repo_url |
| |
| # Add a relative external. |
| change_external(os.path.join(wc_dir, 'A', 'B'), |
| "^/A/D/G G-ext\n../D/H H-ext", commit=True) |
| svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir) |
| |
| # A second wc not at the repository root |
| other_wc = sbox.add_wc_path('other') |
| svntest.main.safe_rmtree(other_wc, 1) |
| svntest.actions.run_and_verify_svn(None, [], 'checkout', |
| repo_url + '/A/B', other_wc) |
| # Move our repository to another location. |
| other_repo_dir, other_repo_url = sbox.add_repo_path('other') |
| svntest.main.copy_repos(repo_dir, other_repo_dir, 2, 0) |
| svntest.main.safe_rmtree(repo_dir, 1) |
| |
| # Now relocate our working copy. |
| svntest.actions.run_and_verify_svn(None, [], 'relocate', |
| repo_url, other_repo_url, wc_dir) |
| |
| # Check the URLs of the externals -- were they updated to point to the |
| # .other repository URL? |
| svntest.actions.run_and_verify_info([{ 'URL' : '.*.other/A/D/G$' }], |
| os.path.join(wc_dir, 'A', 'B', 'G-ext')) |
| svntest.actions.run_and_verify_info([{ 'URL' : '.*.other/A/D/H$' }], |
| os.path.join(wc_dir, 'A', 'B', 'H-ext')) |
| |
| # Relocate with prefix too long to be valid for externals. |
| svntest.actions.run_and_verify_svn(None, [], 'relocate', |
| repo_url + '/A/B', |
| other_repo_url + '/A/B', |
| other_wc) |
| |
| svntest.actions.run_and_verify_info([{ 'URL' : '.*.other/A/D/G$' }], |
| os.path.join(other_wc, 'G-ext')) |
| svntest.actions.run_and_verify_info([{ 'URL' : '.*.other/A/D/H$' }], |
| os.path.join(other_wc, 'H-ext')) |
| |
| def prefix_partial_component(sbox): |
| """prefix with a partial component""" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| repo_dir = sbox.repo_dir |
| repo_url = sbox.repo_url |
| other1_repo_dir, other1_repo_url = sbox.add_repo_path('xxxother') |
| other2_repo_dir, other2_repo_url = sbox.add_repo_path('yyyother') |
| |
| # Relocate to 'xxxother'. |
| svntest.main.copy_repos(repo_dir, other1_repo_dir, 1, 0) |
| svntest.main.safe_rmtree(repo_dir, 1) |
| svntest.actions.run_and_verify_svn(None, [], 'relocate', |
| repo_url, other1_repo_url, wc_dir) |
| svntest.actions.run_and_verify_info([{ 'URL' : '.*.xxxother$' }], |
| wc_dir) |
| |
| # Now relocate from 'xxx' to 'yyy' omitting 'other'. |
| svntest.main.copy_repos(other1_repo_dir, other2_repo_dir, 1, 0) |
| svntest.main.safe_rmtree(other1_repo_url, 1) |
| svntest.actions.run_and_verify_svn(None, [], 'relocate', |
| other1_repo_url[:-5], |
| other2_repo_url[:-5], |
| wc_dir) |
| svntest.actions.run_and_verify_info([{ 'URL' : '.*.yyyother$' }], |
| wc_dir) |
| |
| |
| ######################################################################## |
| # Run the tests |
| |
| # list all tests here, starting with None: |
| test_list = [ None, |
| relocate_deleted_missing_copied, |
| relocate_beyond_repos_root, |
| relocate_and_propset, |
| single_file_relocate, |
| relocate_with_switched_children, |
| relocate_with_relative_externals, |
| prefix_partial_component, |
| ] |
| |
| if __name__ == '__main__': |
| svntest.main.run_tests(test_list) |
| # NOTREACHED |
| |
| |
| ### End of file. |