1#!/usr/bin/env python 2# 3# switch_tests.py: testing `svn switch'. 4# 5# Subversion is a tool for revision control. 6# See http://subversion.apache.org for more information. 7# 8# ==================================================================== 9# Licensed to the Apache Software Foundation (ASF) under one 10# or more contributor license agreements. See the NOTICE file 11# distributed with this work for additional information 12# regarding copyright ownership. The ASF licenses this file 13# to you under the Apache License, Version 2.0 (the 14# "License"); you may not use this file except in compliance 15# with the License. You may obtain a copy of the License at 16# 17# http://www.apache.org/licenses/LICENSE-2.0 18# 19# Unless required by applicable law or agreed to in writing, 20# software distributed under the License is distributed on an 21# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 22# KIND, either express or implied. See the License for the 23# specific language governing permissions and limitations 24# under the License. 25###################################################################### 26 27# General modules 28import shutil, re, os 29 30# Our testing module 31import svntest 32from svntest import verify, actions, main 33 34# (abbreviation) 35Skip = svntest.testcase.Skip_deco 36SkipUnless = svntest.testcase.SkipUnless_deco 37XFail = svntest.testcase.XFail_deco 38Issues = svntest.testcase.Issues_deco 39Issue = svntest.testcase.Issue_deco 40Wimp = svntest.testcase.Wimp_deco 41Item = svntest.wc.StateItem 42 43from svntest.main import SVN_PROP_MERGEINFO, server_has_mergeinfo 44from externals_tests import change_external 45from svntest.deeptrees import do_routine_switching 46 47#---------------------------------------------------------------------- 48 49def relocate_deleted_missing_copied(sbox): 50 "relocate with deleted, missing and copied entries" 51 sbox.build() 52 wc_dir = sbox.wc_dir 53 54 # Delete A/mu to create a deleted entry for mu in A/.svn/entries 55 mu_path = os.path.join(wc_dir, 'A', 'mu') 56 svntest.actions.run_and_verify_svn(None, [], 'rm', mu_path) 57 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 58 expected_status.remove('A/mu') 59 expected_output = svntest.wc.State(wc_dir, { 60 'A/mu' : Item(verb='Deleting'), 61 }) 62 svntest.actions.run_and_verify_commit(wc_dir, 63 expected_output, 64 expected_status) 65 66 # Remove A/B/F to create a missing entry 67 svntest.main.safe_rmtree(os.path.join(wc_dir, 'A', 'B', 'F')) 68 69 # Copy A/D to A/D2 70 D_path = os.path.join(wc_dir, 'A', 'D') 71 D2_path = os.path.join(wc_dir, 'A', 'D2') 72 svntest.actions.run_and_verify_svn(None, [], 'copy', 73 D_path, D2_path) 74 # Delete within the copy 75 D2G_path = os.path.join(wc_dir, 'A', 'D2', 'G') 76 svntest.actions.run_and_verify_svn(None, [], 'rm', D2G_path) 77 78 expected_status.add({ 79 'A/D2' : Item(status='A ', wc_rev='-', copied='+'), 80 'A/D2/gamma' : Item(status=' ', wc_rev='-', copied='+'), 81 'A/D2/G' : Item(status='D ', wc_rev='-', copied='+'), 82 'A/D2/G/pi' : Item(status='D ', wc_rev='-', copied='+'), 83 'A/D2/G/rho' : Item(status='D ', wc_rev='-', copied='+'), 84 'A/D2/G/tau' : Item(status='D ', wc_rev='-', copied='+'), 85 'A/D2/H' : Item(status=' ', wc_rev='-', copied='+'), 86 'A/D2/H/chi' : Item(status=' ', wc_rev='-', copied='+'), 87 'A/D2/H/omega' : Item(status=' ', wc_rev='-', copied='+'), 88 'A/D2/H/psi' : Item(status=' ', wc_rev='-', copied='+'), 89 }) 90 expected_status.tweak('A/B/F', status='! ', wc_rev='1') 91 svntest.actions.run_and_verify_status(wc_dir, expected_status) 92 93 # Relocate 94 repo_dir = sbox.repo_dir 95 repo_url = sbox.repo_url 96 other_repo_dir, other_repo_url = sbox.add_repo_path('other') 97 svntest.main.copy_repos(repo_dir, other_repo_dir, 2, 0) 98 svntest.main.safe_rmtree(repo_dir, 1) 99 svntest.actions.run_and_verify_svn(None, [], 'switch', '--relocate', 100 repo_url, other_repo_url, wc_dir) 101 102 # Deleted and missing entries should be preserved, so update should 103 # show only A/B/F being reinstated 104 expected_output = svntest.wc.State(wc_dir, { 105 'A/B/F' : Item(verb='Restored'), 106 }) 107 108 expected_disk = svntest.main.greek_state.copy() 109 expected_disk.remove('A/mu') 110 expected_disk.add({ 111 'A/D2' : Item(), 112 'A/D2/gamma' : Item("This is the file 'gamma'.\n"), 113 'A/D2/H' : Item(), 114 'A/D2/H/chi' : Item("This is the file 'chi'.\n"), 115 'A/D2/H/omega' : Item("This is the file 'omega'.\n"), 116 'A/D2/H/psi' : Item("This is the file 'psi'.\n"), 117 }) 118 119 expected_status.add({ 120 'A/B/F' : Item(status=' ', wc_rev='2'), 121 }) 122 expected_status.tweak(wc_rev=2) 123 expected_status.tweak('A/D2', 'A/D2/gamma', 124 'A/D2/H', 'A/D2/H/chi', 'A/D2/H/omega', 'A/D2/H/psi', 125 wc_rev='-') 126 expected_status.tweak('A/D2/G', 'A/D2/G/pi', 'A/D2/G/rho', 'A/D2/G/tau', 127 copied='+', wc_rev='-') 128 svntest.actions.run_and_verify_update(wc_dir, 129 expected_output, 130 expected_disk, 131 expected_status) 132 133 # Commit to verify that copyfrom URLs have been relocated 134 expected_output = svntest.wc.State(wc_dir, { 135 'A/D2' : Item(verb='Adding'), 136 'A/D2/G' : Item(verb='Deleting'), 137 }) 138 expected_status.tweak('A/D2', 'A/D2/gamma', 139 'A/D2/H', 'A/D2/H/chi', 'A/D2/H/omega', 'A/D2/H/psi', 140 status=' ', wc_rev='3', copied=None) 141 expected_status.remove('A/D2/G', 'A/D2/G/pi', 'A/D2/G/rho', 'A/D2/G/tau') 142 svntest.actions.run_and_verify_commit(wc_dir, 143 expected_output, expected_status) 144 145#---------------------------------------------------------------------- 146 147@Issue(2380) 148def relocate_beyond_repos_root(sbox): 149 "relocate with prefixes longer than repo root" 150 sbox.build(read_only=True, create_wc=False) 151 152 wc_backup = sbox.add_wc_path('backup') 153 154 wc_dir = sbox.wc_dir 155 repo_dir = sbox.repo_dir 156 repo_url = sbox.repo_url 157 other_repo_dir, other_repo_url = sbox.add_repo_path('other') 158 A_url = repo_url + "/A" 159 A_wc_dir = wc_dir 160 other_A_url = other_repo_url + "/A" 161 other_B_url = other_repo_url + "/B" 162 163 svntest.main.safe_rmtree(wc_dir, 1) 164 svntest.actions.run_and_verify_svn(None, [], 'checkout', 165 repo_url + '/A', wc_dir) 166 167 svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 0) 168 169 # A relocate that changes the repo path part of the URL shouldn't work. 170 # This tests for issue #2380. 171 svntest.actions.run_and_verify_svn(None, 172 ".*Invalid relocation destination.*", 173 'relocate', 174 A_url, other_B_url, A_wc_dir) 175 176 # Another way of trying to change the fs path, leading to an invalid 177 # repository root. 178 svntest.actions.run_and_verify_svn(None, 179 ".*is not the root.*", 180 'relocate', 181 repo_url, other_B_url, A_wc_dir) 182 183 svntest.actions.run_and_verify_svn(None, [], 184 'relocate', 185 A_url, other_A_url, A_wc_dir) 186 187 # Check that we can contact the repository, meaning that the 188 # relocate actually changed the URI. Escape the expected URI to 189 # avoid problems from any regex meta-characters it may contain 190 # (e.g. '+'). 191 expected_infos = [ 192 { 'URL' : re.escape(other_A_url) + '$', 193 'Path' : '.+', 194 'Repository UUID' : '.+', 195 'Revision' : '.+', 196 'Node Kind' : '.+', 197 'Last Changed Date' : '.+' }, 198 ] 199 svntest.actions.run_and_verify_info(expected_infos, A_wc_dir, '-rHEAD') 200 201#---------------------------------------------------------------------- 202# Issue 2578. 203def relocate_and_propset(sbox): 204 "out of date propset should fail after a relocate" 205 206 # Create virgin repos and working copy 207 svntest.main.safe_rmtree(sbox.repo_dir, 1) 208 svntest.main.create_repos(sbox.repo_dir) 209 svntest.actions.guarantee_greek_repository( 210 sbox.repo_dir, svntest.main.options.server_minor_version) 211 212 wc_dir = sbox.wc_dir 213 repo_dir = sbox.repo_dir 214 repo_url = sbox.repo_url 215 216 # checkout 217 svntest.main.safe_rmtree(wc_dir, 1) 218 svntest.actions.run_and_verify_svn(None, [], 219 'checkout', 220 repo_url, wc_dir) 221 222 # Relocate 223 other_repo_dir, other_repo_url = sbox.add_repo_path('other') 224 svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 0) 225 svntest.main.safe_rmtree(repo_dir, 1) 226 svntest.actions.run_and_verify_svn(None, [], 'relocate', 227 repo_url, other_repo_url, wc_dir) 228 229 # Remove gamma from the working copy. 230 D_path = os.path.join(wc_dir, 'A', 'D') 231 gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma') 232 svntest.main.run_svn(None, 'rm', gamma_path) 233 234 # Create expected commit output. 235 expected_output = svntest.wc.State(wc_dir, { 236 'A/D/gamma' : Item(verb='Deleting'), 237 }) 238 239 # After committing, status should show no sign of gamma. 240 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 241 expected_status.remove('A/D/gamma') 242 243 # Commit the deletion of gamma and verify. 244 svntest.actions.run_and_verify_commit(wc_dir, 245 expected_output, 246 expected_status) 247 248 # Now gamma should be marked as `deleted' under the hood, at 249 # revision 2. Meanwhile, A/D is still lagging at revision 1. 250 251 # Make a propchange on A/D 252 svntest.main.run_svn(None, 'ps', 'foo', 'bar', D_path) 253 254 # Commit and *expect* a repository Merge failure: 255 svntest.actions.run_and_verify_commit(wc_dir, 256 None, 257 None, 258 ".*[Oo]ut of date.*") 259 260#---------------------------------------------------------------------- 261 262def single_file_relocate(sbox): 263 "relocate a single file" 264 265 # Create virgin repos and working copy 266 svntest.main.safe_rmtree(sbox.repo_dir, 1) 267 svntest.actions.guarantee_greek_repository( 268 sbox.repo_dir, svntest.main.options.server_minor_version) 269 270 wc_dir = sbox.wc_dir 271 iota_path = os.path.join(sbox.wc_dir, 'iota') 272 repo_dir = sbox.repo_dir 273 repo_url = sbox.repo_url 274 iota_url = repo_url + '/iota' 275 greek_dump_dir = sbox.add_wc_path('greek-dump') 276 277 # checkout 278 svntest.main.safe_rmtree(wc_dir, 1) 279 svntest.actions.run_and_verify_svn(None, [], 280 'checkout', 281 repo_url, wc_dir) 282 283 # Relocate 284 other_repo_dir, other_repo_url = sbox.add_repo_path('other') 285 other_iota_url = other_repo_url + '/iota' 286 svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 0) 287 svntest.main.safe_rmtree(repo_dir, 1) 288 svntest.actions.run_and_verify_svn(None, 289 ".*Cannot relocate.*", 290 'relocate', 291 iota_url, other_iota_url, iota_path) 292 293#---------------------------------------------------------------------- 294 295def relocate_with_switched_children(sbox): 296 "relocate a directory with switched children" 297 sbox.build() 298 wc_dir = sbox.wc_dir 299 300 # Setup (and verify) some switched things 301 do_routine_switching(sbox.wc_dir, sbox.repo_url, False) 302 303 # Relocate 304 repo_dir = sbox.repo_dir 305 repo_url = sbox.repo_url 306 other_repo_dir, other_repo_url = sbox.add_repo_path('other') 307 svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 0) 308 svntest.main.safe_rmtree(repo_dir, 1) 309 310 # Do the switch and check the results in three ways. 311 svntest.actions.run_and_verify_svn(None, [], 'relocate', 312 repo_url, other_repo_url, wc_dir) 313 314 # Attempt to commit changes and examine results 315 expected_output = svntest.wc.State(wc_dir, { }) 316 expected_status = svntest.actions.get_virginal_state(wc_dir, 1) 317 expected_status.tweak('A/B', 'iota', 318 switched='S') 319 expected_status.remove('A/B/E', 'A/B/F', 'A/B/E/alpha', 'A/B/E/beta', 320 'A/B/lambda') 321 expected_status.add({ 322 'A/B/pi' : Item(status=' ', wc_rev='1'), 323 'A/B/rho' : Item(status=' ', wc_rev='1'), 324 'A/B/tau' : Item(status=' ', wc_rev='1'), 325 }) 326 327 # This won't actually do a commit, because nothing should be modified. 328 svntest.actions.run_and_verify_commit(wc_dir, 329 expected_output, expected_status) 330 331 # Check the URLs of various nodes. 332 info_output = { 333 wc_dir: '.*.other$', 334 os.path.join(wc_dir, 'iota'): '.*.other/A/D/gamma$', 335 os.path.join(wc_dir, 'A', 'B'): '.*.other/A/D/G$', 336 os.path.join(wc_dir, 'A', 'B', 'pi'): '.*.other/A/D/G/pi$', 337 } 338 339 for path, pattern in info_output.items(): 340 expected_info = { 'URL' : pattern } 341 svntest.actions.run_and_verify_info([expected_info], path) 342 343#---------------------------------------------------------------------- 344 345 346### regression test for issue #3597 347@Issue(3597) 348def relocate_with_relative_externals(sbox): 349 "relocate a directory containing relative externals" 350 351 sbox.build() 352 wc_dir = sbox.wc_dir 353 repo_dir = sbox.repo_dir 354 repo_url = sbox.repo_url 355 356 # Add a relative external. 357 change_external(os.path.join(wc_dir, 'A', 'B'), 358 "^/A/D/G G-ext\n../D/H H-ext", commit=True) 359 svntest.actions.run_and_verify_svn(None, [], 'update', wc_dir) 360 361 # A second wc not at the repository root 362 other_wc = sbox.add_wc_path('other') 363 svntest.main.safe_rmtree(other_wc, 1) 364 svntest.actions.run_and_verify_svn(None, [], 'checkout', 365 repo_url + '/A/B', other_wc) 366 # Move our repository to another location. 367 other_repo_dir, other_repo_url = sbox.add_repo_path('other') 368 svntest.main.copy_repos(repo_dir, other_repo_dir, 2, 0) 369 svntest.main.safe_rmtree(repo_dir, 1) 370 371 # Now relocate our working copy. 372 svntest.actions.run_and_verify_svn(None, [], 'relocate', 373 repo_url, other_repo_url, wc_dir) 374 375 # Check the URLs of the externals -- were they updated to point to the 376 # .other repository URL? 377 svntest.actions.run_and_verify_info([{ 'URL' : '.*.other/A/D/G$' }], 378 os.path.join(wc_dir, 'A', 'B', 'G-ext')) 379 svntest.actions.run_and_verify_info([{ 'URL' : '.*.other/A/D/H$' }], 380 os.path.join(wc_dir, 'A', 'B', 'H-ext')) 381 382 # Relocate with prefix too long to be valid for externals. 383 svntest.actions.run_and_verify_svn(None, [], 'relocate', 384 repo_url + '/A/B', 385 other_repo_url + '/A/B', 386 other_wc) 387 388 svntest.actions.run_and_verify_info([{ 'URL' : '.*.other/A/D/G$' }], 389 os.path.join(other_wc, 'G-ext')) 390 svntest.actions.run_and_verify_info([{ 'URL' : '.*.other/A/D/H$' }], 391 os.path.join(other_wc, 'H-ext')) 392 393def prefix_partial_component(sbox): 394 """prefix with a partial component""" 395 396 sbox.build() 397 wc_dir = sbox.wc_dir 398 repo_dir = sbox.repo_dir 399 repo_url = sbox.repo_url 400 other1_repo_dir, other1_repo_url = sbox.add_repo_path('xxxother') 401 other2_repo_dir, other2_repo_url = sbox.add_repo_path('yyyother') 402 403 # Relocate to 'xxxother'. 404 svntest.main.copy_repos(repo_dir, other1_repo_dir, 1, 0) 405 svntest.main.safe_rmtree(repo_dir, 1) 406 svntest.actions.run_and_verify_svn(None, [], 'relocate', 407 repo_url, other1_repo_url, wc_dir) 408 svntest.actions.run_and_verify_info([{ 'URL' : '.*.xxxother$' }], 409 wc_dir) 410 411 # Now relocate from 'xxx' to 'yyy' omitting 'other'. 412 svntest.main.copy_repos(other1_repo_dir, other2_repo_dir, 1, 0) 413 svntest.main.safe_rmtree(other1_repo_url, 1) 414 svntest.actions.run_and_verify_svn(None, [], 'relocate', 415 other1_repo_url[:-5], 416 other2_repo_url[:-5], 417 wc_dir) 418 svntest.actions.run_and_verify_info([{ 'URL' : '.*.yyyother$' }], 419 wc_dir) 420 421 422######################################################################## 423# Run the tests 424 425# list all tests here, starting with None: 426test_list = [ None, 427 relocate_deleted_missing_copied, 428 relocate_beyond_repos_root, 429 relocate_and_propset, 430 single_file_relocate, 431 relocate_with_switched_children, 432 relocate_with_relative_externals, 433 prefix_partial_component, 434 ] 435 436if __name__ == '__main__': 437 svntest.main.run_tests(test_list) 438 # NOTREACHED 439 440 441### End of file. 442