blob: 063c2527373d87b8ad916a9cd2ace71bb4183737 [file] [log] [blame]
#!/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.