text: #!/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.
  