#!/usr/bin/env python
#
#  switch_tests.py:  testing `svn switch'.
#
#  Subversion is a tool for revision control. 
#  See http://subversion.tigris.org for more information.
#    
# ====================================================================
# Copyright (c) 2000-2004 CollabNet.  All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.  The terms
# are also available at http://subversion.tigris.org/license-1.html.
# If newer versions of this license are posted there, you may use a
# newer version instead, at your option.
#
######################################################################

# General modules
import shutil, string, sys, re, os

# Our testing module
import svntest

# (abbreviation)
Skip = svntest.testcase.Skip
XFail = svntest.testcase.XFail
Item = svntest.wc.StateItem


### Bummer.  It would be really nice to have easy access to the URL
### member of our entries files so that switches could be testing by
### examining the modified ancestry.  But status doesn't show this
### information.  Hopefully in the future the cmdline binary will have
### a subcommand for dumping multi-line detailed information about
### versioned things.  Until then, we'll stick with the traditional
### verification methods.
###
### gjs says: we have 'svn info' now

def get_routine_status_state(wc_dir):
  """get the routine status list for WC_DIR at the completion of an
  initial call to do_routine_switching()"""
  
  # Construct some paths for convenience
  ADH_path = os.path.join(wc_dir, 'A', 'D', 'H')
  chi_path = os.path.join(ADH_path, 'chi')
  omega_path = os.path.join(ADH_path, 'omega')
  psi_path = os.path.join(ADH_path, 'psi')
  pi_path = os.path.join(ADH_path, 'pi')
  tau_path = os.path.join(ADH_path, 'tau')
  rho_path = os.path.join(ADH_path, 'rho')

  # Now generate a state
  state = svntest.actions.get_virginal_state(wc_dir, 1)
  state.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F', 'A/B/lambda')
  state.add({
    'A/B/pi' : Item(status='  ', wc_rev=1),
    'A/B/tau' : Item(status='  ', wc_rev=1),
    'A/B/rho' : Item(status='  ', wc_rev=1),
    })

  return state

#----------------------------------------------------------------------

def get_routine_disk_state(wc_dir):
  """get the routine disk list for WC_DIR at the completion of an
  initial call to do_routine_switching()"""

  disk = svntest.main.greek_state.copy()

  # iota has the same contents as gamma
  disk.tweak('iota', contents=disk.desc['A/D/gamma'].contents)

  # A/B/* no longer exist, but have been replaced by copies of A/D/G/*
  disk.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', 'A/B/F', 'A/B/lambda')
  disk.add({
    'A/B/pi' : Item("This is the file 'pi'.\n"),
    'A/B/rho' : Item("This is the file 'rho'.\n"),
    'A/B/tau' : Item("This is the file 'tau'.\n"),
    })

  return disk

#----------------------------------------------------------------------

def do_routine_switching(wc_dir, verify):
  """perform some routine switching of the working copy WC_DIR for
  other tests to use.  If VERIFY, then do a full verification of the
  switching, else don't bother."""

  ### Switch the file `iota' to `A/D/gamma'.

  # Construct some paths for convenience
  iota_path = os.path.join(wc_dir, 'iota')
  gamma_url = svntest.main.current_repo_url + '/A/D/gamma'

  if verify:
    # Create expected output tree
    expected_output = svntest.wc.State(wc_dir, {
      'iota' : Item(status='U '),
      })

    # Create expected disk tree (iota will have gamma's contents)
    expected_disk = svntest.main.greek_state.copy()
    expected_disk.tweak('iota',
                        contents=expected_disk.desc['A/D/gamma'].contents)

    # Create expected status tree
    expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
    expected_status.tweak('iota', switched='S')
  
    # Do the switch and check the results in three ways.
    svntest.actions.run_and_verify_switch(wc_dir, iota_path, gamma_url,
                                          expected_output,
                                          expected_disk,
                                          expected_status)
  else:
    svntest.main.run_svn(None, 'switch',
                         '--username', svntest.main.wc_author,
                         '--password', svntest.main.wc_passwd,
                         gamma_url, iota_path)
  
  ### Switch the directory `A/B' to `A/D/G'.

  # Construct some paths for convenience
  AB_path = os.path.join(wc_dir, 'A', 'B')
  ADG_url = svntest.main.current_repo_url + '/A/D/G'

  if verify:
    # Create expected output tree
    expected_output = svntest.wc.State(wc_dir, {
      'A/B/E'       : Item(status='D '),
      'A/B/F'       : Item(status='D '),
      'A/B/lambda'  : Item(status='D '),
      'A/B/pi' : Item(status='A '),
      'A/B/tau' : Item(status='A '),
      'A/B/rho' : Item(status='A '),
      })
    
    # Create expected disk tree (iota will have gamma's contents,
    # A/B/* will look like A/D/G/*)
    expected_disk = get_routine_disk_state(wc_dir)
    
    # Create expected status
    expected_status = get_routine_status_state(wc_dir)
    expected_status.tweak('iota', 'A/B', switched='S')

    # Do the switch and check the results in three ways.
    svntest.actions.run_and_verify_switch(wc_dir, AB_path, ADG_url,
                                          expected_output,
                                          expected_disk,
                                          expected_status)
  else:
    svntest.main.run_svn(None, 'switch', ADG_url, AB_path)


#----------------------------------------------------------------------

def commit_routine_switching(wc_dir, verify):
  "Commit some stuff in a routinely-switched working copy."
  
  # Make some local mods
  iota_path = os.path.join(wc_dir, 'iota')
  Bpi_path = os.path.join(wc_dir, 'A', 'B', 'pi')
  Gpi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
  Z_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z')
  zeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z', 'zeta')

  svntest.main.file_append(iota_path, "apple")
  svntest.main.file_append(Bpi_path, "melon")
  svntest.main.file_append(Gpi_path, "banana")
  os.mkdir(Z_path)
  svntest.main.file_append(zeta_path, "This is the file 'zeta'.\n")
  svntest.main.run_svn(None, 'add', Z_path)

  # Try to commit.  We expect this to fail because, if all the
  # switching went as expected, A/B/pi and A/D/G/pi point to the
  # same URL.  We don't allow this.
  svntest.actions.run_and_verify_commit(
    wc_dir, None, None,
    "svn: Cannot commit both .* as they refer to the same URL$",
    None, None, None, None,
    wc_dir)

  # Okay, that all taken care of, let's revert the A/D/G/pi path and
  # move along.  Afterward, we should be okay to commit.  (Sorry,
  # holsta, that banana has to go...)
  svntest.main.run_svn(None, 'revert', Gpi_path)

  # Create expected output tree.
  expected_output = svntest.wc.State(wc_dir, {
    'A/D/G/Z' : Item(verb='Adding'),
    'A/D/G/Z/zeta' : Item(verb='Adding'),
    'iota' : Item(verb='Sending'),
    'A/B/pi' : Item(verb='Sending'),
    })

  # Created expected status tree.
  expected_status = get_routine_status_state(wc_dir)
  expected_status.tweak('iota', 'A/B', switched='S')
  expected_status.tweak('iota', 'A/B/pi', wc_rev=2, status='  ')
  expected_status.add({
    'A/D/G/Z' : Item(status='  ', wc_rev=2),
    'A/D/G/Z/zeta' : Item(status='  ', wc_rev=2),
    })

  # Commit should succeed
  if verify:
    svntest.actions.run_and_verify_commit(wc_dir,
                                          expected_output,
                                          expected_status,
                                          None, None, None, None, None,
                                          wc_dir)
  else:
    svntest.main.run_svn(None, 'ci', '-m', 'log msg', wc_dir)


######################################################################
# Tests
#

#----------------------------------------------------------------------

def routine_switching(sbox):
  "test some basic switching operations"
    
  sbox.build()

  # Setup (and verify) some switched things
  do_routine_switching(sbox.wc_dir, 1)


#----------------------------------------------------------------------

def commit_switched_things(sbox):
  "commits after some basic switching operations"

  sbox.build()
  wc_dir = sbox.wc_dir
  
  # Setup some switched things (don't bother verifying)
  do_routine_switching(wc_dir, 0)

  # Commit some stuff (and verify)
  commit_routine_switching(wc_dir, 1)

    
#----------------------------------------------------------------------

def full_update(sbox):
  "update wc that contains switched things"

  sbox.build()
  wc_dir = sbox.wc_dir
  
  # Setup some switched things (don't bother verifying)
  do_routine_switching(wc_dir, 0)

  # Copy wc_dir to a backup location
  wc_backup = sbox.add_wc_path('backup')
  svntest.actions.duplicate_dir(wc_dir, wc_backup)
  
  # Commit some stuff (don't bother verifying)
  commit_routine_switching(wc_backup, 0)

  # Some convenient path variables
  iota_path = os.path.join(wc_dir, 'iota')
  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
  Bpi_path = os.path.join(wc_dir, 'A', 'B', 'pi')
  BZ_path = os.path.join(wc_dir, 'A', 'B', 'Z')
  Bzeta_path = os.path.join(wc_dir, 'A', 'B', 'Z', 'zeta')
  Gpi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
  GZ_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z')
  Gzeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z', 'zeta')

  # Create expected output tree for an update of wc_backup.
  expected_output = svntest.wc.State(wc_dir, {
    'iota' : Item(status='U '),
    'A/D/gamma' : Item(status='U '),
    'A/B/pi' : Item(status='U '),
    'A/B/Z' : Item(status='A '),
    'A/B/Z/zeta' : Item(status='A '),
    'A/D/G/pi' : Item(status='U '),
    'A/D/G/Z' : Item(status='A '),
    'A/D/G/Z/zeta' : Item(status='A '),
    })

  # Create expected disk tree for the update
  expected_disk = get_routine_disk_state(wc_dir)
  expected_disk.tweak('iota', contents="This is the file 'gamma'.\napple")
  expected_disk.tweak('A/D/gamma', contents="This is the file 'gamma'.\napple")
  expected_disk.tweak('A/B/pi', contents="This is the file 'pi'.\nmelon")
  expected_disk.tweak('A/D/G/pi', contents="This is the file 'pi'.\nmelon")
  expected_disk.add({
    'A/B/Z' : Item(),
    'A/B/Z/zeta' : Item(contents="This is the file 'zeta'.\n"),
    'A/D/G/Z' : Item(),
    'A/D/G/Z/zeta' : Item(contents="This is the file 'zeta'.\n"),
    })

  # Create expected status tree for the update.
  expected_status = get_routine_status_state(wc_dir)
  expected_status.tweak(wc_rev=2)
  expected_status.add({
    'A/D/G/Z' : Item(status='  ', wc_rev=2),
    'A/D/G/Z/zeta' : Item(status='  ', wc_rev=2),
    'A/B/Z' : Item(status='  ', wc_rev=2),
    'A/B/Z/zeta' : Item(status='  ', wc_rev=2),
    })
  expected_status.tweak('iota', 'A/B', switched='S')

  svntest.actions.run_and_verify_update(wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status)

#----------------------------------------------------------------------

def full_rev_update(sbox):
  "reverse update wc that contains switched things"

  sbox.build()
  wc_dir = sbox.wc_dir
  
  # Setup some switched things (don't bother verifying)
  do_routine_switching(wc_dir, 0)

  # Commit some stuff (don't bother verifying)
  commit_routine_switching(wc_dir, 0)

  # Update to HEAD (tested elsewhere)
  svntest.main.run_svn (None, 'up', wc_dir)

  # Some convenient path variables
  iota_path = os.path.join(wc_dir, 'iota')
  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
  Bpi_path = os.path.join(wc_dir, 'A', 'B', 'pi')
  BZ_path = os.path.join(wc_dir, 'A', 'B', 'Z')
  Gpi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
  GZ_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z')

  # Now, reverse update, back to the pre-commit state.
  expected_output = svntest.wc.State(wc_dir, {
    'iota' : Item(status='U '),
    'A/D/gamma' : Item(status='U '),
    'A/B/pi' : Item(status='U '),
    'A/B/Z' : Item(status='D '),
    'A/D/G/pi' : Item(status='U '),
    'A/D/G/Z' : Item(status='D '),
    })

  # Create expected disk tree
  expected_disk = get_routine_disk_state(wc_dir)
    
  # Create expected status
  expected_status = get_routine_status_state(wc_dir)
  expected_status.tweak('iota', 'A/B', switched='S')

  svntest.actions.run_and_verify_update(wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        None, None, None,
                                        None, None, 1,
                                        '-r', '1', wc_dir)

#----------------------------------------------------------------------

def update_switched_things(sbox):
  "update switched wc things to HEAD"

  sbox.build()
  wc_dir = sbox.wc_dir
  
  # Setup some switched things (don't bother verifying)
  do_routine_switching(wc_dir, 0)

  # Copy wc_dir to a backup location
  wc_backup = sbox.add_wc_path('backup')
  svntest.actions.duplicate_dir(wc_dir, wc_backup)
  
  # Commit some stuff (don't bother verifying)
  commit_routine_switching(wc_backup, 0)

  # Some convenient path variables
  iota_path = os.path.join(wc_dir, 'iota')
  B_path = os.path.join(wc_dir, 'A', 'B')

  # Create expected output tree for an update of wc_backup.
  expected_output = svntest.wc.State(wc_dir, {
    'iota' : Item(status='U '),
    'A/B/pi' : Item(status='U '),
    'A/B/Z' : Item(status='A '),
    'A/B/Z/zeta' : Item(status='A '),
    })

  # Create expected disk tree for the update
  expected_disk = get_routine_disk_state(wc_dir)
  expected_disk.tweak('iota', contents="This is the file 'gamma'.\napple")

  expected_disk.tweak('A/B/pi', contents="This is the file 'pi'.\nmelon")
  expected_disk.add({
    'A/B/Z' : Item(),
    'A/B/Z/zeta' : Item("This is the file 'zeta'.\n"),
    })

  # Create expected status tree for the update.
  expected_status = get_routine_status_state(wc_dir)
  expected_status.tweak('iota', 'A/B', switched='S')
  expected_status.tweak('A/B', 'A/B/pi', 'A/B/rho', 'A/B/tau', 'iota',
                        wc_rev=2)
  expected_status.add({
    'A/B/Z' : Item(status='  ', wc_rev=2),
    'A/B/Z/zeta' : Item(status='  ', wc_rev=2),
    })

  svntest.actions.run_and_verify_update(wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        None, None, None,
                                        None, None, 0,
                                        B_path,
                                        iota_path)


#----------------------------------------------------------------------

def rev_update_switched_things(sbox):
  "reverse update switched wc things to an older rev"

  sbox.build()
  wc_dir = sbox.wc_dir
  
  # Setup some switched things (don't bother verifying)
  do_routine_switching(wc_dir, 0)

  # Commit some stuff (don't bother verifying)
  commit_routine_switching(wc_dir, 0)

  # Some convenient path variables
  iota_path = os.path.join(wc_dir, 'iota')
  B_path = os.path.join(wc_dir, 'A', 'B')

  # Update to HEAD (tested elsewhere)
  svntest.main.run_svn (None, 'up', wc_dir)

  # Now, reverse update, back to the pre-commit state.
  expected_output = svntest.wc.State(wc_dir, {
    'iota' : Item(status='U '),
    'A/B/pi' : Item(status='U '),
    'A/B/Z' : Item(status='D '),
    })

  # Create expected disk tree
  expected_disk = get_routine_disk_state(wc_dir)
  expected_disk.tweak('A/D/gamma', contents="This is the file 'gamma'.\napple")
  expected_disk.tweak('A/D/G/pi', contents="This is the file 'pi'.\nmelon")
  expected_disk.add({
    'A/D/G/Z' : Item(),
    'A/D/G/Z/zeta' : Item("This is the file 'zeta'.\n"),
    })
    
  # Create expected status tree for the update.
  expected_status = get_routine_status_state(wc_dir)
  expected_status.tweak(wc_rev=2)
  expected_status.tweak('iota', 'A/B', switched='S')
  expected_status.tweak('A/B', 'A/B/pi', 'A/B/rho', 'A/B/tau', 'iota',
                        wc_rev=1)
  expected_status.add({
    'A/D/G/Z' : Item(status='  ', wc_rev=2),
    'A/D/G/Z/zeta' : Item(status='  ', wc_rev=2),
    })

  svntest.actions.run_and_verify_update(wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        None, None, None,
                                        None, None, 1,
                                        '-r', '1',
                                        B_path,
                                        iota_path)


#----------------------------------------------------------------------

def log_switched_file(sbox):
  "show logs for a switched file"

  sbox.build()
  wc_dir = sbox.wc_dir
  
  # Setup some switched things (don't bother verifying)
  do_routine_switching(wc_dir, 0)

  # edit and commit switched file 'iota'
  iota_path = os.path.join(wc_dir, 'iota')
  svntest.main.run_svn (None, 'ps', 'x', 'x', iota_path)
  svntest.main.run_svn (None, 'ci', '-m', 
                        'set prop on switched iota', 
                        iota_path)

  # log switched file 'iota'
  output, error = svntest.main.run_svn (None, 'log', iota_path)
  for line in output:
    if line.find("set prop on switched iota") != -1:
      break
  else:
    raise svntest.Failure

#----------------------------------------------------------------------

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, None, [], 'rm', mu_path)
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.tweak(wc_rev=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,
                                         None, None, None, None, None,
                                         wc_dir)

  # Remove A/B/F to create a missing entry
  svntest.main.safe_rmtree(os.path.join(wc_dir, 'A', 'B', 'F'))

  # Copy A/D/H to A/D/H2
  H_path = os.path.join(wc_dir, 'A', 'D', 'H')
  H2_path = os.path.join(wc_dir, 'A', 'D', 'H2')
  svntest.actions.run_and_verify_svn(None, None, [], 'copy',
                                     H_path, H2_path)
  expected_status.add({
    'A/D/H2'       : Item(status='A ', wc_rev='-', copied='+'),
    'A/D/H2/chi'   : Item(status='  ', wc_rev='-', copied='+'),
    'A/D/H2/omega' : Item(status='  ', wc_rev='-', copied='+'),
    'A/D/H2/psi'   : Item(status='  ', wc_rev='-', copied='+'),
    })
  expected_status.tweak('A/B/F', status='! ', wc_rev='?')
  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)
  svntest.main.safe_rmtree(repo_dir, 1)
  svntest.actions.run_and_verify_svn(None, 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(status='A '),
    })
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.remove('A/mu')
  expected_disk.add({
    'A/D/H2'       : Item(),
    'A/D/H2/chi'   : Item("This is the file 'chi'.\n"),
    'A/D/H2/omega' : Item("This is the file 'omega'.\n"),
    'A/D/H2/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/D/H2', 'A/D/H2/chi', 'A/D/H2/omega', 'A/D/H2/psi',
                        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/D/H2'       : Item(verb='Adding'),
    'A/D/H2/chi'   : Item(verb='Adding'),
    'A/D/H2/omega' : Item(verb='Adding'),
    'A/D/H2/psi'   : Item(verb='Adding'),
    })
  expected_status.tweak('A/D/H2', 'A/D/H2/chi', 'A/D/H2/omega', 'A/D/H2/psi',
                        status='  ', wc_rev='3', copied=None)
  svntest.actions.run_and_verify_commit (wc_dir,
                                         expected_output, expected_status,
                                         None, None, None, None, None,
                                         wc_dir)


#----------------------------------------------------------------------

def delete_subdir(sbox):
  "switch that deletes a sub-directory"
  sbox.build()
  wc_dir = sbox.wc_dir

  A_path = os.path.join(wc_dir, 'A')
  A_url = svntest.main.current_repo_url + '/A'
  A2_url = svntest.main.current_repo_url + '/A2'
  A2_B_F_url = svntest.main.current_repo_url + '/A2/B/F'

  svntest.actions.run_and_verify_svn(None,
                                     ['\n', 'Committed revision 2.\n'], [],
                                     'cp', '-m', 'make copy', A_url, A2_url)

  svntest.actions.run_and_verify_svn(None,
                                     ['\n', 'Committed revision 3.\n'], [],
                                     'rm', '-m', 'delete subdir', A2_B_F_url)

  expected_output = svntest.wc.State(wc_dir, {
    'A/B/F' : Item(status='D '),
    })
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.remove('A/B/F')
  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
  expected_status.tweak('A', switched='S')
  expected_status.remove('A/B/F')
  expected_status.tweak('', 'iota', wc_rev=1)

  # Used to fail with a 'directory not locked' error for A/B/F
  svntest.actions.run_and_verify_switch(wc_dir, A_path, A2_url,
                                        expected_output,
                                        expected_disk,
                                        expected_status)

#----------------------------------------------------------------------
# Issue 1532: Switch a file to a dir: can't switch it back to the file

def file_dir_file(sbox):
  "switch a file to a dir and back to the file"
  sbox.build()
  wc_dir = sbox.wc_dir

  file_path = os.path.join(wc_dir, 'iota')
  file_url = svntest.main.current_repo_url + '/iota'
  dir_url = svntest.main.current_repo_url + '/A/C'

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'switch', dir_url, file_path)

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'switch', file_url, file_path)

#----------------------------------------------------------------------
# Issue 1751: "svn switch --non-recursive" does not switch existing files,
# and generates the wrong URL for new files.

def nonrecursive_switching(sbox):
  "non-recursive switch"
  sbox.build()
  wc1_dir = sbox.wc_dir
  wc2_dir = os.path.join(wc1_dir, 'wc2')

  # "Trunk" will be the existing dir "A/", with existing file "mu".
  # "Branch" will be the new dir "branch/version1/", with added file "newfile".
  # "wc1" will hold the whole repository (including trunk and branch).
  # "wc2" will hold the "trunk" and then be switched to the "branch".
  # It is irrelevant that wc2 is located on disk as a sub-directory of wc1.
  trunk_url = svntest.main.current_repo_url + '/A'
  branch_url = svntest.main.current_repo_url + '/branch'
  version1_url = branch_url + '/version1'
  wc1_new_file = os.path.join(wc1_dir, 'branch', 'version1', 'newfile')
  wc2_new_file = os.path.join(wc2_dir, 'newfile')
  wc2_mu_file = os.path.join(wc2_dir, 'mu')
  wc2_B_dir = os.path.join(wc2_dir, 'B')
  wc2_C_dir = os.path.join(wc2_dir, 'C')
  wc2_D_dir = os.path.join(wc2_dir, 'D')
  
  # Check out the trunk as "wc2"
  svntest.main.run_svn(None, 'co', trunk_url, wc2_dir)

  # Make a branch, and add a new file, in "wc_dir" and repository
  svntest.main.run_svn(None, 'mkdir', '-m', '', branch_url)
  svntest.main.run_svn(None, 'cp', '-m', '', trunk_url, version1_url)
  svntest.main.run_svn(None, 'up', wc1_dir)
  svntest.main.file_append(wc1_new_file, "This is the file 'newfile'.\n")
  svntest.main.run_svn(None, 'add', wc1_new_file)
  svntest.main.run_svn(None, 'ci', '-m', '', wc1_dir)

  # Try to switch "wc2" to the branch (non-recursively)
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'switch', '-N', version1_url, wc2_dir)

  # Check the URLs of the (not switched) directories.
  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'info', wc2_B_dir)
  if out[1].find('/A/B') == -1:
    print out[1]
    raise svntest.Failure

  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'info', wc2_C_dir)
  if out[1].find('/A/C') == -1:
    print out[1]
    raise svntest.Failure

  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'info', wc2_D_dir)
  if out[1].find('/A/D') == -1:
    print out[1]
    raise svntest.Failure

  # Check the URLs of the switched files.
  # ("svn status -u" might be a better check: it fails when newfile's URL
  # is bad, and shows "S" when mu's URL is wrong.)
  # mu: not switched
  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'info', wc2_mu_file)
  if out[2].find('/branch/version1/mu') == -1:
    print out[2]
    raise svntest.Failure
  # newfile: wrong URL
  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'info', wc2_new_file)
  if out[2].find('/branch/version1/newfile') == -1:
    print out[2]
    raise svntest.Failure


#----------------------------------------------------------------------
def failed_anchor_is_target(sbox):
  "anchor=target that fails due to local mods"
  sbox.build()
  wc_dir = sbox.wc_dir

  G_url = svntest.main.current_repo_url + '/A/D/G'
  G_psi_url = G_url + '/psi'
  svntest.actions.run_and_verify_svn(None,
                                     ['\n', 'Committed revision 2.\n'], [],
                                     'mkdir', '-m', 'log msg', G_psi_url)

  H_path = os.path.join(wc_dir, 'A', 'D', 'H')
  psi_path = os.path.join(H_path, 'psi')
  svntest.main.file_append(psi_path, "more text")

  # This switch leaves psi unversioned, because of the local mods,
  # then fails because it tries to add a directory of the same name.
  out, err = svntest.main.run_svn(1, 'switch',
                                  '--username', svntest.main.wc_author,
                                  '--password', svntest.main.wc_passwd,
                                  G_url, H_path)
  if not err:
    raise svntest.Failure

  # Some items under H show up as switched because, while H itself was
  # switched, the switch command failed before it reached all items
  #
  # NOTE: I suspect this whole test is dependent on the order in
  # which changes are received, but since the new psi is a dir, it
  # appears we can count on it being received last.  But if this test
  # ever starts failing, you read it here first :-).
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.tweak(wc_rev=1)
  expected_status.tweak('A/D/H', status='! ', switched='S', wc_rev=2)
  expected_status.remove('A/D/H/psi', 'A/D/H/chi', 'A/D/H/omega')
  expected_status.add({
    'A/D/H/pi'      : Item(status='  ', wc_rev=2),
    'A/D/H/tau'     : Item(status='  ', wc_rev=2),
    'A/D/H/rho'     : Item(status='  ', wc_rev=2),
    })
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # There was a bug whereby the failed switch left the wrong URL in
  # the target directory H.  Check for that.
  out, err = svntest.actions.run_and_verify_svn(None, None, [], 'info', H_path)
  for line in out:
    if line.find('URL: ' + G_url) != -1:
      break
  else:
    raise svntest.Failure

  # Remove the now-unversioned psi, and repeat the switch.  This
  # should complete the switch.
  os.remove(psi_path)
  svntest.actions.run_and_verify_svn(None, None, [], 'switch',
                                     '--username', svntest.main.wc_author,
                                     '--password', svntest.main.wc_passwd,
                                     G_url, H_path)

  expected_status.tweak('A/D/H', status='  ') # remains switched
  expected_status.add({ 'A/D/H/psi' : Item(status='  ',
                                           switched=None,
                                           wc_rev=2) })
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

#----------------------------------------------------------------------
# Issue #1826 - svn switch temporarily drops invalid URLs into the entries
#               files (which become not-temporary if the switch fails).
def bad_intermediate_urls(sbox):
  "bad intermediate urls in use"
  sbox.build()
  wc_dir = sbox.wc_dir

  # We'll be switching our working copy to (a modified) A/C in the Greek tree.
  
  # First, make an extra subdirectory in C to match one in the root, plus
  # another one inside of that.
  C_url = svntest.main.current_repo_url + '/A/C'
  C_A_url = svntest.main.current_repo_url + '/A/C/A'
  C_A_Z_url = svntest.main.current_repo_url + '/A/C/A/Z'
  svntest.actions.run_and_verify_svn(None,
                                     ['\n', 'Committed revision 2.\n'], [],
                                     'mkdir', '-m', 'log msg',
                                     C_A_url, C_A_Z_url)

  # Now, we'll drop a conflicting path under the root.
  A_path = os.path.join(wc_dir, 'A')
  A_Z_path = os.path.join(A_path, 'Z')
  svntest.main.file_append(A_Z_path, 'Look, Mom, no ... switch success.')

  # This switch should fail for reasons of obstruction.
  out, err = svntest.main.run_svn(1, 'switch',
                                  '--username', svntest.main.wc_author,
                                  '--password', svntest.main.wc_passwd,
                                  C_url, wc_dir)
  if not err:
    raise svntest.Failure

  # However, the URL for A should now reflect A/C/A, not something else.
  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'info', A_path)
  if out[1].find('/A/C/A') == -1:
    raise svntest.Failure
  


#----------------------------------------------------------------------
# Regression test for issue #1825: failed switch may corrupt
# working copy

def obstructed_switch(sbox):
  "obstructed switch"
  sbox.build()
  wc_dir = sbox.wc_dir

  E_url      = svntest.main.current_repo_url + '/A/B/E'
  E_url2     = svntest.main.current_repo_url + '/A/B/Esave'
  svntest.actions.run_and_verify_svn(None,
                                     ['\n', 'Committed revision 2.\n'], [],
                                     'cp', '-m', 'msgcopy', E_url, E_url2)

  E_path     = os.path.join(wc_dir, 'A', 'B', 'E')
  alpha_path = os.path.join(E_path, 'alpha')
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', alpha_path)
  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
  expected_status.tweak(wc_rev=1)
  expected_status.remove('A/B/E/alpha')
  expected_output = svntest.wc.State(wc_dir, {
    'A/B/E/alpha' : Item(verb='Deleting'),
    })
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output, expected_status,
                                        None, None, None, None, None,
                                        wc_dir)

  svntest.main.file_append(alpha_path, "hello")
  out, err = svntest.main.run_svn(1, 'sw', E_url2, E_path)
  for line in err:
    if line.find("object of the same name already exists") != -1:
      break
  else:
    raise svntest.Failure

  os.remove(alpha_path)
  svntest.actions.run_and_verify_svn(None, None, [], 'sw', E_url2, E_path)
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/B/E', 'A/B/E/alpha', 'A/B/E/beta', wc_rev=3)
  expected_status.tweak('A/B/E', switched='S')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)


#----------------------------------------------------------------------
# Issue 2353.
def commit_mods_below_switch(sbox):
  "commit with mods below switch" 
  sbox.build()
  wc_dir = sbox.wc_dir

  C_path = os.path.join(wc_dir, 'A', 'C')
  B_url = svntest.main.current_repo_url + '/A/B'
  expected_output = svntest.wc.State(wc_dir, {
    'A/C/E'       : Item(status='A '),
    'A/C/E/alpha' : Item(status='A '),
    'A/C/E/beta'  : Item(status='A '),
    'A/C/F'       : Item(status='A '),
    'A/C/lambda'  : Item(status='A '),
    })
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.add({
    'A/C/E'       : Item(),
    'A/C/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
    'A/C/E/beta'  : Item(contents="This is the file 'beta'.\n"),
    'A/C/F'       : Item(),
    'A/C/lambda'  : Item(contents="This is the file 'lambda'.\n"),
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/C', switched='S')
  expected_status.add({
    'A/C/E'       : Item(status='  ', wc_rev=1),
    'A/C/E/alpha' : Item(status='  ', wc_rev=1),
    'A/C/E/beta'  : Item(status='  ', wc_rev=1),
    'A/C/F'       : Item(status='  ', wc_rev=1),
    'A/C/lambda'  : Item(status='  ', wc_rev=1),
    })
  svntest.actions.run_and_verify_switch(wc_dir, C_path, B_url,
                                        expected_output,
                                        expected_disk,
                                        expected_status)

  D_path = os.path.join(wc_dir, 'A', 'D')
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'propset', 'x', 'x', C_path, D_path)

  expected_status.tweak('A/C', 'A/D', status=' M')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  expected_output = svntest.wc.State(wc_dir, {
    'A/C' : Item(verb='Sending'),
    'A/D' : Item(verb='Sending'),
    })
  expected_status.tweak('A/C', 'A/D', status='  ', wc_rev=2)

  # A/C erroneously classified as a wc root caused the commit to fail
  # with "'A/C/E' is missing or not locked"
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output, expected_status,
                                        None, None, None, None, None,
                                        C_path, D_path)

def relocate_beyond_repos_root(sbox):
  "relocate with prefixes longer than repo root"
  sbox.build()

  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')
  svntest.main.copy_repos(repo_dir, other_repo_dir, 1)
  svntest.main.safe_rmtree(repo_dir, 1)

  A_url = repo_url + "/A"
  other_A_url = other_repo_url + "/A"
  other_B_url = other_repo_url + "/B"
  A_wc_dir = os.path.join(wc_dir, "A")

  # 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, None,
                                     ".*can only change the repository part.*",
                                     'switch', '--relocate',
                                     A_url, other_B_url, A_wc_dir)

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'switch', '--relocate',
                                     A_url, other_A_url, A_wc_dir)

  # Check that we can contact the repository, meaning that the relocate
  # actually changed the URL.
  svntest.actions.run_and_verify_svn(None, '^URL: ' + other_A_url + '$', [],
                                     'info', '-rHEAD', A_wc_dir)
                                     

# Check that switch can't change the repository root.
def switch_change_repos_root(sbox):
  "switch shouldn't allow changing repos root"
  sbox.build()

  wc_dir = sbox.wc_dir
  repo_url = sbox.repo_url
  other_repo_url = repo_url

  # Strip trailing slashes and add something bogus to that other URL.
  while other_repo_url[-1] == '/':
    other_repos_url = other_repo_url[:-1]
  other_repo_url = other_repo_url + "_bogus"

  other_A_url = other_repo_url +  "/A"
  A_wc_dir = os.path.join(wc_dir, "A")

  # A switch that changes the repo root part of the URL shouldn't work.
  svntest.actions.run_and_verify_svn(None, None,
                                     ".*not the same repository.*",
                                     'switch',
                                     other_A_url, A_wc_dir)

  # Make sure we didn't break the WC.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)
                                        


########################################################################
# Run the tests


# list all tests here, starting with None:
test_list = [ None,
              routine_switching,
              commit_switched_things,
              full_update,
              full_rev_update,
              update_switched_things,
              rev_update_switched_things,
              log_switched_file,
              relocate_deleted_missing_copied,
              delete_subdir,
              XFail(file_dir_file),
              nonrecursive_switching,
              failed_anchor_is_target,
              bad_intermediate_urls,
              obstructed_switch,
              XFail(commit_mods_below_switch),
              relocate_beyond_repos_root,
              switch_change_repos_root,
             ]

if __name__ == '__main__':
  svntest.main.run_tests(test_list)
  # NOTREACHED


### End of file.
