#!/usr/bin/env python
#
#  basic_tests.py:  testing working-copy interactions with ra_local
#
#  Subversion is a tool for revision control.
#  See http://subversion.tigris.org for more information.
#
# ====================================================================
# Copyright (c) 2000-2008 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, stat, re, os

# Our testing module
import svntest
from svntest import wc

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

######################################################################
# Tests
#
#   Each test must return on success or raise on failure.

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

def basic_checkout(sbox):
  "basic checkout of a wc"

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  # Checkout of a different URL into a working copy fails
  A_url = sbox.repo_url + '/A'
  svntest.actions.run_and_verify_svn("No error where some expected",
                                      None, svntest.verify.AnyOutput,
                                     # "Obstructed update",
                                     'co', A_url,
                                     wc_dir)

  # Make some changes to the working copy
  mu_path = os.path.join(wc_dir, 'A', 'mu')
  svntest.main.file_append(mu_path, 'appended mu text')
  lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
  os.remove(lambda_path)
  G_path = os.path.join(wc_dir, 'A', 'D', 'G')

  svntest.actions.run_and_verify_svn(None, None, [], 'rm', G_path)

  expected_output = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_output.tweak('A/mu', status='M ')
  expected_output.tweak('A/B/lambda', status='! ')
  expected_output.tweak('A/D/G',
                        'A/D/G/pi',
                        'A/D/G/rho',
                        'A/D/G/tau', status='D ')

  svntest.actions.run_and_verify_status(wc_dir, expected_output)

  # Repeat checkout of original URL into working copy with modifications
  url = sbox.repo_url

  svntest.actions.run_and_verify_svn("Repeat checkout failed", None, [],
                                     'co', url,
                                     wc_dir)

  # lambda is restored, modifications remain, deletes remain scheduled
  # for deletion although files are restored to the filesystem
  expected_output.tweak('A/B/lambda', status='  ')
  svntest.actions.run_and_verify_status(wc_dir, expected_output)

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

def basic_status(sbox):
  "basic status command"

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  # Created expected output tree for 'svn status'
  output = svntest.actions.get_virginal_state(wc_dir, 1)

  svntest.actions.run_and_verify_status(wc_dir, output)

  os.chdir(os.path.join(wc_dir, 'A'))
  output = svntest.actions.get_virginal_state("..", 1)
  svntest.actions.run_and_verify_status("..", output)

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

def basic_commit(sbox):
  "basic commit command"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Make a couple of local mods to files
  mu_path = os.path.join(wc_dir, 'A', 'mu')
  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
  svntest.main.file_append(mu_path, 'appended mu text')
  svntest.main.file_append(rho_path, 'new appended text for rho')

  # Created expected output tree for 'svn ci'
  expected_output = wc.State(wc_dir, {
    'A/mu' : Item(verb='Sending'),
    'A/D/G/rho' : Item(verb='Sending'),
    })

  # Create expected status tree; all local revisions should be at 1,
  # but mu and rho should be at revision 2.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2)

  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output,
                                        expected_status,
                                        None,
                                        wc_dir)


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

def basic_update(sbox):
  "basic update command"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Make a backup copy of the working copy
  wc_backup = sbox.add_wc_path('backup')
  svntest.actions.duplicate_dir(wc_dir, wc_backup)

  # Make a couple of local mods to files
  mu_path = os.path.join(wc_dir, 'A', 'mu')
  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
  svntest.main.file_append(mu_path, 'appended mu text')
  svntest.main.file_append(rho_path, 'new appended text for rho')

  # Created expected output tree for 'svn ci'
  expected_output = wc.State(wc_dir, {
    'A/mu' : Item(verb='Sending'),
    'A/D/G/rho' : Item(verb='Sending'),
    })

  # Create expected status tree; all local revisions should be at 1,
  # but mu and rho should be at revision 2.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2)

  # Commit.
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        expected_status, None, wc_dir)

  # Create expected output tree for an update of the wc_backup.
  expected_output = wc.State(wc_backup, {
    'A/mu' : Item(status='U '),
    'A/D/G/rho' : Item(status='U '),
    })

  # Create expected disk tree for the update.
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.tweak('A/mu',
                      contents=expected_disk.desc['A/mu'].contents
                      + 'appended mu text')
  expected_disk.tweak('A/D/G/rho',
                      contents=expected_disk.desc['A/D/G/rho'].contents
                      + 'new appended text for rho')

  # Create expected status tree for the update.
  expected_status = svntest.actions.get_virginal_state(wc_backup, 2)

  # Do the update and check the results in three ways.
  svntest.actions.run_and_verify_update(wc_backup,
                                        expected_output,
                                        expected_disk,
                                        expected_status)

  # Unversioned paths, those that are not immediate children of a versioned
  # path, are skipped and do not raise an error
  xx_path = os.path.join(wc_dir, 'xx', 'xx')
  exit_code, out, err = svntest.actions.run_and_verify_svn(
    "update xx/xx", ["Skipped '"+xx_path+"'\n"], [],
    'update', xx_path)
  exit_code, out, err = svntest.actions.run_and_verify_svn(
    "update xx/xx", [], [],
    'update', '--quiet', xx_path)

  # URL's are also skipped.
  urls = ('http://localhost/a/b/c', 'http://localhost', 'svn://localhost')
  for url in urls:
    exit_code, out, err = svntest.actions.run_and_verify_svn(
      "update " + url, ["Skipped '"+url+"'\n"], [],
      'update', url)

#----------------------------------------------------------------------
def basic_mkdir_url(sbox):
  "basic mkdir URL"

  sbox.build()

  Y_url = sbox.repo_url + '/Y'
  Y_Z_url = sbox.repo_url + '/Y/Z'

  svntest.actions.run_and_verify_svn("mkdir URL URL/subdir",
                                     ["\n", "Committed revision 2.\n"], [],
                                     'mkdir', '-m', 'log_msg', Y_url, Y_Z_url)

  expected_output = wc.State(sbox.wc_dir, {
    'Y'   : Item(status='A '),
    'Y/Z' : Item(status='A '),
    })
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.add({
    'Y'   : Item(),
    'Y/Z' : Item()
    })
  expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 2)
  expected_status.add({
    'Y'   : Item(status='  ', wc_rev=2),
    'Y/Z' : Item(status='  ', wc_rev=2)
    })

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


#----------------------------------------------------------------------
def basic_mkdir_url_with_parents(sbox):
  "basic mkdir URL, including parent directories"

  sbox.build()

  X_url = sbox.repo_url + '/X'
  X_Y_Z_url = sbox.repo_url + '/X/Y/Z'
  X_Y_Z2_url = sbox.repo_url + '/X/Y/Z2'
  X_T_C_url = sbox.repo_url + '/X/T/C'
  U_url = sbox.repo_url + '/U'
  U_V_url = sbox.repo_url + '/U/V'
  U_V_W_url = sbox.repo_url + '/U/V/W'
  svntest.actions.run_and_verify_svn("erroneous mkdir sans --parents",
                                     [],
                                     ".*Try 'svn mkdir --parents' instead.*",
                                     'mkdir', '-m', 'log_msg',
                                     X_Y_Z_url, X_Y_Z2_url, X_T_C_url, U_V_W_url)

  svntest.actions.run_and_verify_svn("mkdir",
                                     ["\n", "Committed revision 2.\n"], [],
                                     'mkdir', '-m', 'log_msg',
                                     X_url, U_url)

  svntest.actions.run_and_verify_svn("mkdir --parents",
                                     ["\n", "Committed revision 3.\n"], [],
                                     'mkdir', '-m', 'log_msg', '--parents',
                                     X_Y_Z_url, X_Y_Z2_url, X_T_C_url, U_V_W_url)

  expected_output = wc.State(sbox.wc_dir, {
    'X'      : Item(status='A '),
    'X/Y'    : Item(status='A '),
    'X/Y/Z'  : Item(status='A '),
    'X/Y/Z2' : Item(status='A '),
    'X/T'    : Item(status='A '),
    'X/T/C'  : Item(status='A '),
    'U'      : Item(status='A '),
    'U/V'    : Item(status='A '),
    'U/V/W'  : Item(status='A '),
    })
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.add({
    'X'      : Item(),
    'X/Y'    : Item(),
    'X/Y/Z'  : Item(),
    'X/Y/Z2' : Item(),
    'X/T'    : Item(),
    'X/T/C'  : Item(),
    'U'      : Item(),
    'U/V'    : Item(),
    'U/V/W'  : Item(),
    })
  expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 3)
  expected_status.add({
    'X'      : Item(status='  ', wc_rev=3),
    'X/Y'    : Item(status='  ', wc_rev=3),
    'X/Y/Z'  : Item(status='  ', wc_rev=3),
    'X/Y/Z2' : Item(status='  ', wc_rev=3),
    'X/T'    : Item(status='  ', wc_rev=3),
    'X/T/C'  : Item(status='  ', wc_rev=3),
    'U'      : Item(status='  ', wc_rev=3),
    'U/V'    : Item(status='  ', wc_rev=3),
    'U/V/W'  : Item(status='  ', wc_rev=3),
    })
  svntest.actions.run_and_verify_update(sbox.wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status)


#----------------------------------------------------------------------
def basic_mkdir_wc_with_parents(sbox):
  "basic mkdir, including parent directories"

  sbox.build()
  wc_dir = sbox.wc_dir

  Y_Z_path = os.path.join(wc_dir, 'Y', 'Z')

  svntest.actions.run_and_verify_svn("erroneous mkdir dir/subdir", [],
                                     ".*Try 'svn mkdir --parents' instead.*",
                                     'mkdir', Y_Z_path)

  svntest.actions.run_and_verify_svn("mkdir dir/subdir", None, [],
                                     'mkdir', '--parents', Y_Z_path)


#----------------------------------------------------------------------
def basic_corruption(sbox):
  "basic corruption detection"

  ## I always wanted a test named "basic_corruption". :-)
  ## Here's how it works:
  ##
  ##    1. Make a working copy at rev 1, duplicate it.  Now we have
  ##        two working copies at rev 1.  Call them first and second.
  ##    2. Make a local mod to `first/A/mu'.
  ##    3. Intentionally corrupt `first/A/.svn/text-base/mu.svn-base'.
  ##    4. Try to commit, expect a failure.
  ##    5. Repair the text-base, commit again, expect success.
  ##    6. Intentionally corrupt `second/A/.svn/text-base/mu.svn-base'.
  ##    7. Try to update `second', expect failure.
  ##    8. Repair the text-base, update again, expect success.
  ##
  ## Here we go...

  sbox.build()
  wc_dir = sbox.wc_dir

  # Make the "other" working copy
  other_wc = sbox.add_wc_path('other')
  svntest.actions.duplicate_dir(wc_dir, other_wc)

  # Make a local mod to mu
  mu_path = os.path.join(wc_dir, 'A', 'mu')
  svntest.main.file_append(mu_path, 'appended mu text')

  # Created expected output tree for 'svn ci'
  expected_output = wc.State(wc_dir, {
    'A/mu' : Item(verb='Sending'),
    })

  # Create expected status tree; all local revisions should be at 1,
  # but mu should be at revision 2.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/mu', wc_rev=2)

  # Modify mu's text-base, so we get a checksum failure the first time
  # we try to commit.
  tb_dir_path = os.path.join(wc_dir, 'A',
                             svntest.main.get_admin_name(), 'text-base')
  mu_tb_path = os.path.join(tb_dir_path, 'mu.svn-base')
  mu_saved_tb_path = mu_tb_path + "-saved"
  tb_dir_saved_mode = os.stat(tb_dir_path)[stat.ST_MODE]
  mu_tb_saved_mode = os.stat(mu_tb_path)[stat.ST_MODE]
  os.chmod(tb_dir_path, 0777)  # ### What's a more portable way to do this?
  os.chmod(mu_tb_path, 0666)   # ### Would rather not use hardcoded numbers.
  shutil.copyfile(mu_tb_path, mu_saved_tb_path)
  svntest.main.file_append(mu_tb_path, 'Aaagggkkk, corruption!')
  os.chmod(tb_dir_path, tb_dir_saved_mode)
  os.chmod(mu_tb_path, mu_tb_saved_mode)

  # This commit should fail due to text base corruption.
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        expected_status, "svn: Checksum",
                                        wc_dir)

  # Restore the uncorrupted text base.
  os.chmod(tb_dir_path, 0777)
  os.chmod(mu_tb_path, 0666)
  os.remove(mu_tb_path)
  os.rename(mu_saved_tb_path, mu_tb_path)
  os.chmod(tb_dir_path, tb_dir_saved_mode)
  os.chmod(mu_tb_path, mu_tb_saved_mode)

  # This commit should succeed.
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        expected_status, None, wc_dir)

  # Create expected output tree for an update of the other_wc.
  expected_output = wc.State(other_wc, {
    'A/mu' : Item(status='U '),
    })

  # Create expected disk tree for the update.
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.tweak('A/mu',
                      contents=expected_disk.desc['A/mu'].contents
                      + 'appended mu text')

  # Create expected status tree for the update.
  expected_status = svntest.actions.get_virginal_state(other_wc, 2)

  # Modify mu's text-base, so we get a checksum failure the first time
  # we try to update.
  tb_dir_path = os.path.join(other_wc, 'A',
                             svntest.main.get_admin_name(), 'text-base')
  mu_tb_path = os.path.join(tb_dir_path, 'mu.svn-base')
  mu_saved_tb_path = mu_tb_path + "-saved"
  tb_dir_saved_mode = os.stat(tb_dir_path)[stat.ST_MODE]
  mu_tb_saved_mode = os.stat(mu_tb_path)[stat.ST_MODE]
  os.chmod(tb_dir_path, 0777)
  os.chmod(mu_tb_path, 0666)
  shutil.copyfile(mu_tb_path, mu_saved_tb_path)
  svntest.main.file_append(mu_tb_path, 'Aiyeeeee, corruption!\nHelp!\n')
  os.chmod(tb_dir_path, tb_dir_saved_mode)
  os.chmod(mu_tb_path, mu_tb_saved_mode)

  # Do the update and check the results in three ways.
  svntest.actions.run_and_verify_update(other_wc,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        "svn: Checksum", other_wc)

  # Restore the uncorrupted text base.
  os.chmod(tb_dir_path, 0777)
  os.chmod(mu_tb_path, 0666)
  os.remove(mu_tb_path)
  os.rename(mu_saved_tb_path, mu_tb_path)
  os.chmod(tb_dir_path, tb_dir_saved_mode)
  os.chmod(mu_tb_path, mu_tb_saved_mode)

  # This update should succeed.  (Actually, I'm kind of astonished
  # that this works without even an intervening "svn cleanup".)
  svntest.actions.run_and_verify_update(other_wc,
                                        expected_output,
                                        expected_disk,
                                        expected_status)

#----------------------------------------------------------------------
def basic_merging_update(sbox):
  "receiving text merges as part of an update"

  sbox.build()
  wc_dir = sbox.wc_dir

  # First change the greek tree to make two files 10 lines long
  mu_path = os.path.join(wc_dir, 'A', 'mu')
  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
  mu_text = ""
  rho_text = ""
  for x in range(2,11):
    mu_text = mu_text + '\nThis is line ' + repr(x) + ' in mu'
    rho_text = rho_text + '\nThis is line ' + repr(x) + ' in rho'
  svntest.main.file_append(mu_path, mu_text)
  svntest.main.file_append(rho_path, rho_text)

  # Create expected output tree for initial commit
  expected_output = wc.State(wc_dir, {
    'A/mu' : Item(verb='Sending'),
    'A/D/G/rho' : Item(verb='Sending'),
    })

  # Create expected status tree; all local revisions should be at 1,
  # but mu and rho should be at revision 2.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2)

  # Initial commit.
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output,
                                        expected_status,
                                        None,
                                        wc_dir)

  # Make a backup copy of the working copy
  wc_backup = sbox.add_wc_path('backup')
  svntest.actions.duplicate_dir(wc_dir, wc_backup)

  # Make a couple of local mods to files
  svntest.main.file_append(mu_path, ' Appended to line 10 of mu')
  svntest.main.file_append(rho_path, ' Appended to line 10 of rho')

  # Created expected output tree for 'svn ci'
  expected_output = wc.State(wc_dir, {
    'A/mu' : Item(verb='Sending'),
    'A/D/G/rho' : Item(verb='Sending'),
    })

  # Create expected status tree; all local revisions should be at 1,
  # but mu and rho should be at revision 3.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=3)

  # Commit.
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output,
                                        expected_status,
                                        None,
                                        wc_dir)

  # Make local mods to wc_backup by recreating mu and rho
  mu_path_backup = os.path.join(wc_backup, 'A', 'mu')
  rho_path_backup = os.path.join(wc_backup, 'A', 'D', 'G', 'rho')

  # open in 'truncate to zero then write" mode
  backup_mu_text = 'This is the new line 1 in the backup copy of mu'
  for x in range(2,11):
    backup_mu_text = backup_mu_text + '\nThis is line ' + repr(x) + ' in mu'
  svntest.main.file_write(mu_path_backup, backup_mu_text, 'w+')

  backup_rho_text = 'This is the new line 1 in the backup copy of rho'
  for x in range(2,11):
    backup_rho_text = backup_rho_text + '\nThis is line ' + repr(x) + ' in rho'
  svntest.main.file_write(rho_path_backup, backup_rho_text, 'w+')

  # Create expected output tree for an update of the wc_backup.
  expected_output = wc.State(wc_backup, {
    'A/mu' : Item(status='G '),
    'A/D/G/rho' : Item(status='G '),
    })

  # Create expected disk tree for the update.
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.tweak('A/mu',
                      contents=backup_mu_text + ' Appended to line 10 of mu')
  expected_disk.tweak('A/D/G/rho',
                      contents=backup_rho_text + ' Appended to line 10 of rho')

  # Create expected status tree for the update.
  expected_status = svntest.actions.get_virginal_state(wc_backup, 3)
  expected_status.tweak('A/mu', 'A/D/G/rho', status='M ')

  # Do the update and check the results in three ways.
  svntest.actions.run_and_verify_update(wc_backup,
                                        expected_output,
                                        expected_disk,
                                        expected_status)

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


def basic_conflict(sbox):
  "basic conflict creation and resolution"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Make a backup copy of the working copy
  wc_backup = sbox.add_wc_path('backup')
  svntest.actions.duplicate_dir(wc_dir, wc_backup)

  # Make a couple of local mods to files which will be committed
  mu_path = os.path.join(wc_dir, 'A', 'mu')
  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
  svntest.main.file_append(mu_path, 'Original appended text for mu\n')
  svntest.main.file_append(rho_path, 'Original appended text for rho\n')

  # Make a couple of local mods to files which will be conflicted
  mu_path_backup = os.path.join(wc_backup, 'A', 'mu')
  rho_path_backup = os.path.join(wc_backup, 'A', 'D', 'G', 'rho')
  svntest.main.file_append(mu_path_backup,
                             'Conflicting appended text for mu\n')
  svntest.main.file_append(rho_path_backup,
                             'Conflicting appended text for rho\n')

  # Created expected output tree for 'svn ci'
  expected_output = wc.State(wc_dir, {
    'A/mu' : Item(verb='Sending'),
    'A/D/G/rho' : Item(verb='Sending'),
    })

  # Create expected status tree; all local revisions should be at 1,
  # but mu and rho should be at revision 2.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2)

  # Commit.
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        expected_status, None, wc_dir)

  # Create expected output tree for an update of the wc_backup.
  expected_output = wc.State(wc_backup, {
    'A/mu' : Item(status='C '),
    'A/D/G/rho' : Item(status='C '),
    })

  # Create expected disk tree for the update.
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.tweak('A/mu',
                      contents="\n".join(["This is the file 'mu'.",
                                          "<<<<<<< .mine",
                                          "Conflicting appended text for mu",
                                          "=======",
                                          "Original appended text for mu",
                                          ">>>>>>> .r2",
                                          ""]))
  expected_disk.tweak('A/D/G/rho',
                      contents="\n".join(["This is the file 'rho'.",
                                          "<<<<<<< .mine",
                                          "Conflicting appended text for rho",
                                          "=======",
                                          "Original appended text for rho",
                                          ">>>>>>> .r2",
                                          ""]))

  # Create expected status tree for the update.
  expected_status = svntest.actions.get_virginal_state(wc_backup, '2')
  expected_status.tweak('A/mu', 'A/D/G/rho', status='C ')

  # "Extra" files that we expect to result from the conflicts.
  # These are expressed as list of regexps.  What a cool system!  :-)
  extra_files = ['mu.*\.r1', 'mu.*\.r2', 'mu.*\.mine',
                 'rho.*\.r1', 'rho.*\.r2', 'rho.*\.mine',]

  # Do the update and check the results in three ways.
  # All "extra" files are passed to detect_conflict_files().
  svntest.actions.run_and_verify_update(wc_backup,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        None,
                                        svntest.tree.detect_conflict_files,
                                        extra_files)

  # verify that the extra_files list is now empty.
  if len(extra_files) != 0:
    # Because we want to be a well-behaved test, we silently raise if
    # the test fails.  However, these two print statements would
    # probably reveal the cause for the failure, if they were
    # uncommented:
    #
    # print "Not all extra reject files have been accounted for:"
    # print extra_files
    ### we should raise a less generic error here. which?
    raise svntest.Failure

  # So now mu and rho are both in a "conflicted" state.  Run 'svn
  # resolved' on them.

  svntest.actions.run_and_verify_resolved([mu_path_backup, rho_path_backup])

  # See if they've changed back to plain old 'M' state.
  expected_status.tweak('A/mu', 'A/D/G/rho', status='M ')

  # There should be *no* extra backup files lying around the working
  # copy after resolving the conflict; thus we're not passing a custom
  # singleton handler.
  svntest.actions.run_and_verify_status(wc_backup, expected_status)


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

def basic_cleanup(sbox):
  "basic cleanup command"

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  # Lock some directories.
  B_path = os.path.join(wc_dir, 'A', 'B')
  G_path = os.path.join(wc_dir, 'A', 'D', 'G')
  C_path = os.path.join(wc_dir, 'A', 'C')
  svntest.actions.lock_admin_dir(B_path)
  svntest.actions.lock_admin_dir(G_path)
  svntest.actions.lock_admin_dir(C_path)

  # Verify locked status.
  expected_output = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_output.tweak('A/B', 'A/D/G', 'A/C', locked='L')

  svntest.actions.run_and_verify_status(wc_dir, expected_output)

  # corrupted/non-existing temporary directory should be restored
  svntest.actions.remove_admin_tmp_dir(B_path)

  # Run cleanup (### todo: cleanup doesn't currently print anything)
  svntest.actions.run_and_verify_svn("Cleanup command", None, [],
                                     'cleanup', wc_dir)

  # Verify unlocked status.
  expected_output = svntest.actions.get_virginal_state(wc_dir, 1)

  svntest.actions.run_and_verify_status(wc_dir, expected_output)


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

def basic_revert(sbox):
  "basic revert command"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Modify some files and props.
  beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
  iota_path = os.path.join(wc_dir, 'iota')
  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
  zeta_path = os.path.join(wc_dir, 'A', 'D', 'H', 'zeta')
  svntest.main.file_append(beta_path, "Added some text to 'beta'.\n")
  svntest.main.file_append(iota_path, "Added some text to 'iota'.\n")
  svntest.main.file_append(rho_path, "Added some text to 'rho'.\n")
  svntest.main.file_append(zeta_path, "Added some text to 'zeta'.\n")

  svntest.actions.run_and_verify_svn("Add command", None, [],
                                     'add', zeta_path)
  svntest.actions.run_and_verify_svn("Add prop command", None, [],
                                     'ps', 'random-prop', 'propvalue',
                                     gamma_path)
  svntest.actions.run_and_verify_svn("Add prop command", None, [],
                                     'ps', 'random-prop', 'propvalue',
                                     iota_path)

  # Verify modified status.
  expected_output = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_output.tweak('A/B/E/beta', 'A/D/G/rho', status='M ')
  expected_output.tweak('iota', status='MM')
  expected_output.tweak('A/D/gamma', status=' M')
  expected_output.add({
    'A/D/H/zeta' : Item(status='A ', wc_rev=0),
    })

  svntest.actions.run_and_verify_status(wc_dir, expected_output)

  # Run revert (### todo: revert doesn't currently print anything)
  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', beta_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', gamma_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', iota_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', rho_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', zeta_path)

  # Verify unmodified status.
  expected_output = svntest.actions.get_virginal_state(wc_dir, 1)

  svntest.actions.run_and_verify_status(wc_dir, expected_output)

  # Now, really make sure the contents are back to their original state.
  fp = open(beta_path, 'r')
  lines = fp.readlines()
  if not ((len (lines) == 1) and (lines[0] == "This is the file 'beta'.\n")):
    print("Revert failed to restore original text.")
    raise svntest.Failure
  fp = open(iota_path, 'r')
  lines = fp.readlines()
  if not ((len (lines) == 1) and (lines[0] == "This is the file 'iota'.\n")):
    print("Revert failed to restore original text.")
    raise svntest.Failure
  fp = open(rho_path, 'r')
  lines = fp.readlines()
  if not ((len (lines) == 1) and (lines[0] == "This is the file 'rho'.\n")):
    print("Revert failed to restore original text.")
    raise svntest.Failure
  fp = open(zeta_path, 'r')
  lines = fp.readlines()
  if not ((len (lines) == 1) and (lines[0] == "Added some text to 'zeta'.\n")):
    ### we should raise a less generic error here. which?
    raise svntest.Failure

  # Finally, check that reverted file is not readonly
  os.remove(beta_path)
  svntest.actions.run_and_verify_svn(None, None, [], 'revert', beta_path)
  if not (open(beta_path, 'r+')):
    raise svntest.Failure

  # Check that a directory scheduled to be added, but physically
  # removed, can be reverted.
  X_path = os.path.join(wc_dir, 'X')

  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', X_path)

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.add({
    'X' : Item(status='A ', wc_rev=0),
    })
  svntest.actions.run_and_verify_status(wc_dir, expected_status)
  svntest.main.safe_rmtree(X_path)

  svntest.actions.run_and_verify_svn(None, None, [], 'revert', X_path)

  expected_status.remove('X')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Check that a directory scheduled for deletion, but physically
  # removed, can be reverted.
  E_path = os.path.join(wc_dir, 'A', 'B', 'E')
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)

  ### Most of the rest of this test is ineffective, due to the
  ### problems described in issue #1611.
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', E_path)
  svntest.main.safe_rmtree(E_path)
  expected_status.tweak('A/B/E', status='D ')
  expected_status.tweak('A/B/E', wc_rev='?')
  ### FIXME: A weakness in the test framework, described in detail
  ### in issue #1611, prevents us from checking via status.  Grr.
  #
  # svntest.actions.run_and_verify_status(wc_dir, expected_status,
  #                                       None, None, None, None)
  #
  #
  ### If you were to uncomment the above, you'd get an error like so:
  #
  # =============================================================
  # Expected E and actual E are different!
  # =============================================================
  # EXPECTED NODE TO BE:
  # =============================================================
  #  * Node name:   E
  #     Path:       working_copies/basic_tests-10/A/B/E
  #     Contents:   None
  #     Properties: {}
  #     Attributes: {'status': 'D ', 'wc_rev': '?'}
  #     Children:   2
  # =============================================================
  # ACTUAL NODE FOUND:
  # =============================================================
  #  * Node name:   E
  #     Path:       working_copies/basic_tests-10/A/B/E
  #     Contents:   None
  #     Properties: {}
  #     Attributes: {'status': 'D ', 'wc_rev': '?'}
  #     Children: is a file.
  # Unequal Types: one Node is a file, the other is a directory

  # This will actually print
  #
  #    "Failed to revert 'working_copies/basic_tests-10/A/B/E' -- \
  #    try updating instead."
  #
  # ...but due to test suite lossage, it'll still look like success.
  svntest.actions.run_and_verify_svn(None, None, [], 'revert', E_path)

  ### FIXME: Again, the problem described in issue #1611 bites us here.
  #
  # expected_status.tweak('A/B/E', status='  ')
  # svntest.actions.run_and_verify_status(wc_dir, expected_status,
  #                                       None, None, None, None)


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

def basic_switch(sbox):
  "basic switch command"

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

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

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

  # Create expected output tree
  expected_output = 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)

  ### Switch the directory `A/D/H' to `A/D/G'.

  # 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')
  ADG_url = sbox.repo_url + '/A/D/G'

  # Create expected output tree
  expected_output = wc.State(wc_dir, {
    'A/D/H/chi' : Item(status='D '),
    'A/D/H/omega' : Item(status='D '),
    'A/D/H/psi' : Item(status='D '),
    'A/D/H/pi' : Item(status='A '),
    'A/D/H/rho' : Item(status='A '),
    'A/D/H/tau' : Item(status='A '),
    })

  # Create expected disk tree (iota will have gamma's contents,
  # A/D/H/* will look like A/D/G/*)
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.tweak('iota',
                      contents=expected_disk.desc['A/D/gamma'].contents)
  expected_disk.remove('A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi')
  expected_disk.add({
    'A/D/H/pi' : Item("This is the file 'pi'.\n"),
    'A/D/H/rho' : Item("This is the file 'rho'.\n"),
    'A/D/H/tau' : Item("This is the file 'tau'.\n"),
    })

  # Create expected status
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.remove('A/D/H/chi',
                         'A/D/H/omega',
                         'A/D/H/psi')
  expected_status.add({
    'A/D/H/pi'  : Item(status='  ', wc_rev=1),
    'A/D/H/rho' : Item(status='  ', wc_rev=1),
    'A/D/H/tau' : Item(status='  ', wc_rev=1),
    })
  expected_status.tweak('iota', 'A/D/H', switched='S')

  # Do the switch and check the results in three ways.
  svntest.actions.run_and_verify_switch(wc_dir, ADH_path, ADG_url,
                                        expected_output,
                                        expected_disk,
                                        expected_status)

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

def verify_file_deleted(message, path):
  try:
    open(path, 'r')
  except IOError:
    return
  if message is not None:
    print(message)
  ###TODO We should raise a less generic error here. which?
  raise Failure

def verify_dir_deleted(path):
  if not os.path.isdir(path):
    return 0

  return 1

def basic_delete(sbox):
  "basic delete command"

  sbox.build()
  wc_dir = sbox.wc_dir

  # modify text of chi
  chi_parent_path = os.path.join(wc_dir, 'A', 'D', 'H')
  chi_path = os.path.join(chi_parent_path, 'chi')
  svntest.main.file_append(chi_path, 'added to chi')

  # modify props of rho (file)
  rho_parent_path = os.path.join(wc_dir, 'A', 'D', 'G')
  rho_path = os.path.join(rho_parent_path, 'rho')
  svntest.main.run_svn(None, 'ps', 'abc', 'def', rho_path)

  # modify props of F (dir)
  F_parent_path = os.path.join(wc_dir, 'A', 'B')
  F_path = os.path.join(F_parent_path, 'F')
  svntest.main.run_svn(None, 'ps', 'abc', 'def', F_path)

  # unversioned file
  sigma_parent_path = os.path.join(wc_dir, 'A', 'C')
  sigma_path = os.path.join(sigma_parent_path, 'sigma')
  svntest.main.file_append(sigma_path, 'unversioned sigma')

  # unversioned directory
  Q_parent_path = sigma_parent_path
  Q_path = os.path.join(Q_parent_path, 'Q')
  os.mkdir(Q_path)

  # added directory hierarchies
  X_parent_path =  os.path.join(wc_dir, 'A', 'B')
  X_path = os.path.join(X_parent_path, 'X')
  svntest.main.run_svn(None, 'mkdir', X_path)
  X_child_path = os.path.join(X_path, 'xi')
  svntest.main.file_append(X_child_path, 'added xi')
  svntest.main.run_svn(None, 'add', X_child_path)
  Y_parent_path = os.path.join(wc_dir, 'A', 'D')
  Y_path = os.path.join(Y_parent_path, 'Y')
  svntest.main.run_svn(None, 'mkdir', Y_path)

  # check status
  expected_output = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_output.tweak('A/D/H/chi', status='M ')
  expected_output.tweak('A/D/G/rho', 'A/B/F', status=' M')
#  expected_output.tweak('A/C/sigma', status='? ')
  expected_output.add({
    'A/B/X' : Item(status='A ', wc_rev=0),
    'A/B/X/xi' : Item(status='A ', wc_rev=0),
    'A/D/Y' : Item(status='A ', wc_rev=0),
    })

  svntest.actions.run_and_verify_status(wc_dir, expected_output)

  # 'svn rm' that should fail
  svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
                                     'rm', chi_path)

  svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
                                     'rm', chi_parent_path)

  svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
                                     'rm', rho_path)

  svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
                                     'rm', rho_parent_path)

  svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
                                     'rm', F_path)

  svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
                                     'rm', F_parent_path)

  svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
                                     'rm', sigma_path)

  svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
                                     'rm', sigma_parent_path)

  svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
                                     'rm', X_path)

  # check status has not changed
  svntest.actions.run_and_verify_status(wc_dir, expected_output)

  # 'svn rm' that should work
  E_path =  os.path.join(wc_dir, 'A', 'B', 'E')
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', E_path)

  # 'svn rm --force' that should work
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', '--force',
                                     chi_parent_path)

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'rm', '--force', rho_parent_path)

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'rm', '--force', F_path)

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'rm', '--force', sigma_parent_path)

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'rm', '--force', X_path)

  # Deleting already removed from wc versioned item with --force
  iota_path = os.path.join(wc_dir, 'iota')
  os.remove(iota_path)
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'rm', '--force', iota_path)

  # and without --force
  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
  os.remove(gamma_path)
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', gamma_path)

  # Deleting already scheduled for deletion doesn't require --force
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', gamma_path)

  svntest.actions.run_and_verify_svn(None, None, [], 'rm', E_path)

  # check status
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/D/H',
                        'A/D/H/chi',
                        'A/D/H/omega',
                        'A/D/H/psi',
                        'A/D/G',
                        'A/D/G/rho',
                        'A/D/G/pi',
                        'A/D/G/tau',
                        'A/B/E',
                        'A/B/E/alpha',
                        'A/B/E/beta',
                        'A/B/F',
                        'A/C',
                        'iota',
                        'A/D/gamma', status='D ')
  expected_status.add({
    'A/D/Y' : Item(status='A ', wc_rev=0),
    })

  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # issue 687 delete directory with uncommitted directory child
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'rm', '--force', Y_parent_path)

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

  # check files have been removed
  verify_file_deleted("Failed to remove text modified file", rho_path)
  verify_file_deleted("Failed to remove prop modified file", chi_path)
  verify_file_deleted("Failed to remove unversioned file", sigma_path)
  verify_file_deleted("Failed to remove unmodified file",
                      os.path.join(E_path, 'alpha'))

  # check versioned dir is not removed
  if not verify_dir_deleted(F_path):
    print("Removed versioned dir")
    ### we should raise a less generic error here. which?
    raise svntest.Failure

  # check unversioned and added dirs has been removed
  if verify_dir_deleted(Q_path):
    print("Failed to remove unversioned dir")
    ### we should raise a less generic error here. which?
    raise svntest.Failure
  if verify_dir_deleted(X_path):
    print("Failed to remove added dir")
    ### we should raise a less generic error here. which?
    raise svntest.Failure

  # Deleting unversioned file explicitly
  foo_path = os.path.join(wc_dir, 'foo')
  svntest.main.file_append(foo_path, 'unversioned foo')
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'rm', '--force', foo_path)
  verify_file_deleted("Failed to remove unversioned file foo", foo_path)

  # At one stage deleting an URL dumped core
  iota_URL = sbox.repo_url + '/iota'

  svntest.actions.run_and_verify_svn(None,
                                     ["\n", "Committed revision 2.\n"], [],
                                     'rm', '-m', 'delete iota URL',
                                     iota_URL)

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

def basic_checkout_deleted(sbox):
  "checkout a path no longer in HEAD"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Delete A/D and commit.
  D_path = os.path.join(wc_dir, 'A', 'D')
  svntest.actions.run_and_verify_svn("error scheduling A/D for deletion",
                                     None, [], 'rm', '--force', D_path)

  expected_output = wc.State(wc_dir, {
    'A/D' : Item(verb='Deleting'),
    })

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.remove('A/D', 'A/D/G', 'A/D/G/rho', 'A/D/G/pi', 'A/D/G/tau',
                         'A/D/H', 'A/D/H/chi', 'A/D/H/psi', 'A/D/H/omega',
                         'A/D/gamma')

  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output, expected_status,
                                        None, wc_dir)

  # Now try to checkout revision 1 of A/D.
  url = sbox.repo_url + '/A/D'
  wc2 = os.path.join(sbox.wc_dir, 'new_D')
  svntest.actions.run_and_verify_svn("error checking out r1 of A/D",
                                     None, [], 'co', '-r', '1',
                                     url + "@1", wc2)

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

# Issue 846, changing a deleted file to an added directory is not
# supported.

def basic_node_kind_change(sbox):
  "attempt to change node kind"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Schedule a file for deletion
  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
  svntest.main.run_svn(None, 'rm', gamma_path)

  # Status shows deleted file
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/D/gamma', status='D ')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Try and fail to create a directory (file scheduled for deletion)
  svntest.actions.run_and_verify_svn('Cannot change node kind',
                                     None, svntest.verify.AnyOutput,
                                     'mkdir', gamma_path)

  # Status is unchanged
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Commit file deletion
  expected_output = wc.State(wc_dir, {
    'A/D/gamma' : Item(verb='Deleting'),
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.remove('A/D/gamma')
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output, expected_status,
                                        None, wc_dir)

  # Try and fail to create a directory (file deleted)
  svntest.actions.run_and_verify_svn('Cannot change node kind',
                                     None, svntest.verify.AnyOutput,
                                     'mkdir', gamma_path)

  # Status is unchanged
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Update to finally get rid of file
  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)

  # mkdir should succeed
  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', gamma_path)

  expected_status.tweak(wc_rev=2)
  expected_status.add({
    'A/D/gamma' : Item(status='A ', wc_rev=0),
    })
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

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

def basic_import(sbox):
  "basic import of single new file"

  sbox.build()
  wc_dir = sbox.wc_dir

  # create a new directory with files of various permissions
  new_path = os.path.join(wc_dir, 'new_file')

  svntest.main.file_append(new_path, "some text")

  # import new files into repository
  url = sbox.repo_url + "/dirA/dirB/new_file"
  exit_code, output, errput = svntest.actions.run_and_verify_svn(
    'Cannot change node kind', None, [], 'import',
    '-m', 'Log message for new import', new_path, url)

  lastline = output.pop().strip()
  cm = re.compile("(Committed|Imported) revision [0-9]+.")
  match = cm.search(lastline)
  if not match:
    ### we should raise a less generic error here. which?
    raise svntest.Failure

  # remove (uncontrolled) local file
  os.remove(new_path)

  # Create expected disk tree for the update (disregarding props)
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.add({
    'dirA/dirB/new_file' : Item('some text'),
    })

  # Create expected status tree for the update (disregarding props).
  # Newly imported file should be at revision 2.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.add({
    'dirA'                : Item(status='  ', wc_rev=2),
    'dirA/dirB'           : Item(status='  ', wc_rev=2),
    'dirA/dirB/new_file'  : Item(status='  ', wc_rev=2),
    })

  # Create expected output tree for the update.
  expected_output = svntest.wc.State(wc_dir, {
    'dirA'               : Item(status='A '),
    'dirA/dirB'          : Item(status='A '),
    'dirA/dirB/new_file' : Item(status='A '),
  })

  # do update and check three ways
  svntest.actions.run_and_verify_update(wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        None, None, None,
                                        None, None, 1)

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

def basic_cat(sbox):
  "basic cat of files"

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  mu_path = os.path.join(wc_dir, 'A', 'mu')

  # Get repository text even if wc is modified
  svntest.main.file_append(mu_path, "some text")
  svntest.actions.run_and_verify_svn(None, ["This is the file 'mu'.\n"],
                                     [], 'cat',
                                     ###TODO is user/pass really necessary?
                                     mu_path)


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

def basic_ls(sbox):
  'basic ls'

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  # Even on Windows, the output will use forward slashes, so that's
  # what we expect below.

  cwd = os.getcwd()
  os.chdir(wc_dir)
  svntest.actions.run_and_verify_svn("ls implicit current directory",
                                     ["A/\n", "iota\n"],
                                     [], 'ls')
  os.chdir(cwd)

  svntest.actions.run_and_verify_svn('ls the root of working copy',
                                     ['A/\n', 'iota\n'],
                                     [], 'ls',
                                     wc_dir)

  svntest.actions.run_and_verify_svn('ls a working copy directory',
                                     ['B/\n', 'C/\n', 'D/\n', 'mu\n'],
                                     [], 'ls',
                                     os.path.join(wc_dir, 'A'))

  svntest.actions.run_and_verify_svn('ls working copy directory with -r BASE',
                                     ['B/\n', 'C/\n', 'D/\n', 'mu\n'],
                                     [], 'ls', '-r', 'BASE',
                                     os.path.join(wc_dir, 'A'))

  svntest.actions.run_and_verify_svn('ls a single file',
                                     ['mu\n'],
                                     [], 'ls',
                                     os.path.join(wc_dir, 'A', 'mu'))

  svntest.actions.run_and_verify_svn('recursive ls',
                                     ['E/\n', 'E/alpha\n', 'E/beta\n', 'F/\n',
                                      'lambda\n' ], [], 'ls', '-R',
                                     os.path.join(wc_dir, 'A', 'B'))


#----------------------------------------------------------------------
def nonexistent_repository(sbox):
  "'svn log file:///nonexistent_path' should fail"

  # The bug was that
  #
  #   $ svn log file:///nonexistent_path
  #
  # would go into an infinite loop, instead of failing immediately as
  # it should.  The loop was because svn_ra_local__split_URL() used
  # svn_path_split() to lop off components and look for a repository
  # in each shorter path in turn, depending on svn_path_is_empty()
  # to test if it had reached the end.  Somewhere along the line we
  # changed the path functions (perhaps revision 3113?), and
  # svn_path_split() stopped cooperating with svn_path_is_empty() in
  # this particular context -- svn_path_split() would reach "/",
  # svn_path_is_empty() would correctly claim that "/" is not empty,
  # the next svn_path_split() would return "/" again, and so on,
  # forever.
  #
  # This bug was fixed in revision 3150, by checking for "/"
  # explicitly in svn_ra_local__split_URL().  By the time you read
  # this, that may or may not be the settled fix, however, so check
  # the logs to see if anything happened later.
  #
  # Anyway: this test _always_ operates on a file:/// path.  Note that
  # if someone runs this test on a system with "/nonexistent_path" in
  # the root directory, the test could fail, and that's just too bad :-).

  exit_code, output, errput = svntest.actions.run_and_verify_svn(
    None, None, svntest.verify.AnyOutput,
    'log', 'file:///nonexistent_path')

  for line in errput:
    if re.match(".*Unable to open an ra_local session to URL.*", line):
      return

  # Else never matched the expected error output, so the test failed.
  raise svntest.main.SVNUnmatchedError


#----------------------------------------------------------------------
# Issue 1064. This test is only useful if running over a non-local RA
# with authentication enabled, otherwise it will pass trivially.
def basic_auth_cache(sbox):
  "basic auth caching"

  sbox.build(create_wc = False, read_only = True)
  wc_dir         = sbox.wc_dir

  repo_dir       = sbox.repo_dir
  repo_url       = sbox.repo_url

  # Create a working copy without auth tokens
  svntest.main.safe_rmtree(wc_dir)


  svntest.actions.run_and_verify_svn(None, None, [],
                                     'checkout',
                                     repo_url, wc_dir)

  # Failed with "not locked" error on missing directory
  svntest.main.safe_rmtree(os.path.join(wc_dir, 'A', 'B', 'E'))
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'status', '-u',
                                     os.path.join(wc_dir, 'A', 'B'))

  # Failed with "already locked" error on new dir
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'copy',
                                     repo_url + '/A/B/E',
                                     os.path.join(wc_dir, 'A', 'D', 'G'))


#----------------------------------------------------------------------
def basic_add_ignores(sbox):
  'ignored files in added dirs should not be added'

  # The bug was that
  #
  #   $ svn add dir
  #
  # where dir contains some items that match the ignore list and some
  # do not would add all items, ignored or not.

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  dir_path = os.path.join(wc_dir, 'dir')
  foo_c_path = os.path.join(dir_path, 'foo.c')
  foo_o_path = os.path.join(dir_path, 'foo.o')

  os.mkdir(dir_path, 0755)
  open(foo_c_path, 'w')
  open(foo_o_path, 'w')

  exit_code, output, err = svntest.actions.run_and_verify_svn(
    "No output where some expected", svntest.verify.AnyOutput, [],
    'add', dir_path)

  for line in output:
    # If we see foo.o in the add output, fail the test.
    if re.match(r'^A\s+.*foo.o$', line):
      raise svntest.verify.SVNUnexpectedOutput

  # Else never matched the unwanted output, so the test passed.


#----------------------------------------------------------------------
def basic_add_local_ignores(sbox):
  'ignore files matching local ignores in added dirs'

  #Issue #2243
  #svn add command not keying off svn:ignore value
  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  dir_path = os.path.join(wc_dir, 'dir')
  file_path = os.path.join(dir_path, 'app.lock')

  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
                                     'mkdir', dir_path)
  svntest.main.run_svn(None, 'propset', 'svn:ignore', '*.lock', dir_path)
  open(file_path, 'w')
  svntest.actions.run_and_verify_svn(None, [], [],
                                     'add', '--force', dir_path)

#----------------------------------------------------------------------
def basic_add_no_ignores(sbox):
  'add ignored files in added dirs'

  # add ignored files using the '--no-ignore' option
  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  dir_path = os.path.join(wc_dir, 'dir')
  foo_c_path = os.path.join(dir_path, 'foo.c')
  # add a few files that match the default ignore patterns
  foo_o_path = os.path.join(dir_path, 'foo.o')
  foo_lo_path = os.path.join(dir_path, 'foo.lo')
  foo_rej_path = os.path.join(dir_path, 'foo.rej')

  os.mkdir(dir_path, 0755)
  open(foo_c_path, 'w')
  open(foo_o_path, 'w')
  open(foo_lo_path, 'w')
  open(foo_rej_path, 'w')

  exit_code, output, err = svntest.actions.run_and_verify_svn(
    "No output where some expected", svntest.verify.AnyOutput, [],
    'add', '--no-ignore', dir_path)

  for line in output:
    # If we don't see ignores in the add output, fail the test.
    if not re.match(r'^A\s+.*(foo.(o|rej|lo|c)|dir)$', line):
      raise svntest.verify.SVNUnexpectedOutput

#----------------------------------------------------------------------
def basic_add_parents(sbox):
  'test add --parents'

  sbox.build()
  wc_dir = sbox.wc_dir

  X_path = os.path.join(wc_dir, 'X')
  Y_path = os.path.join(X_path, 'Y')
  Z_path = os.path.join(Y_path, 'Z')
  zeta_path = os.path.join(Z_path, 'zeta')
  omicron_path = os.path.join(Y_path, 'omicron')

  # Create some unversioned directories
  os.mkdir(X_path, 0755)
  os.mkdir(Y_path, 0755)
  os.mkdir(Z_path, 0755)

  # Create new files
  z = open(zeta_path, 'w')
  z.write("This is the file 'zeta'.\n")
  z.close()
  o = open(omicron_path, 'w')
  o.write("This is the file 'omicron'.\n")
  o.close()

  # Add the file, with it's parents
  svntest.actions.run_and_verify_svn(None, None, [], 'add', '--parents',
                                     zeta_path)

  # Build expected state
  expected_output = wc.State(wc_dir, {
      'X'            : Item(verb='Adding'),
      'X/Y'          : Item(verb='Adding'),
      'X/Y/Z'        : Item(verb='Adding'),
      'X/Y/Z/zeta'   : Item(verb='Adding'),
    })

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.add({
    'X'           : Item(status='  ', wc_rev=2),
    'X/Y'         : Item(status='  ', wc_rev=2),
    'X/Y/Z'       : Item(status='  ', wc_rev=2),
    'X/Y/Z/zeta'  : Item(status='  ', wc_rev=2),
    })

  # Commit and verify
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output,
                                        expected_status,
                                        None,
                                        wc_dir)

#----------------------------------------------------------------------
def uri_syntax(sbox):
  'make sure URI syntaxes are parsed correctly'

  sbox.build(create_wc = False, read_only = True)
  local_dir = sbox.wc_dir

  # Revision 6638 made 'svn co http://host' seg fault, this tests the fix.
  url = sbox.repo_url
  scheme = url[:url.find(":")]
  url = scheme + "://some_nonexistent_host_with_no_trailing_slash"
  svntest.actions.run_and_verify_svn("No error where one expected",
                                     None, svntest.verify.AnyOutput,
                                     'co', url, local_dir)

  # Different RA layers give different errors for failed checkouts;
  # for us, it's only important to know that it _did_ error (as
  # opposed to segfaulting), so we don't examine the error text.

#----------------------------------------------------------------------
def basic_checkout_file(sbox):
  "trying to check out a file should fail"

  sbox.build(read_only = True)

  iota_url = sbox.repo_url + '/iota'

  exit_code, output, errput = svntest.main.run_svn(1, 'co', iota_url)

  for line in errput:
    if line.find("refers to a file") != -1:
      break
  else:
    raise svntest.Failure

#----------------------------------------------------------------------
def basic_info(sbox):
  "basic info command"

  def check_paths(lines, expected_paths):
    "check that paths found on input lines beginning 'Path: ' are as expected"
    paths = []
    for line in lines:
      if line.startswith('Path: '):
        paths.append(line[6:].rstrip())
    if paths != expected_paths:
      print("Reported paths: %s" % paths)
      print("Expected paths: %s" % expected_paths)
      raise svntest.Failure

  sbox.build(read_only = True)

  os.chdir(sbox.wc_dir)

  # Check that "info" works with 0, 1 and more than 1 explicit targets.
  exit_code, output, errput = svntest.main.run_svn(None, 'info')
  check_paths(output, ['.'])
  exit_code, output, errput = svntest.main.run_svn(None, 'info', 'iota')
  check_paths(output, ['iota'])
  exit_code, output, errput = svntest.main.run_svn(None, 'info', 'iota', '.')
  check_paths(output, ['iota', '.'])

def repos_root(sbox):
  "check that repos root gets set on checkout"

  def check_repos_root(lines):
    for line in lines:
      if line == "Repository Root: " + sbox.repo_url + "\n":
        break
    else:
      print("Bad or missing repository root")
      raise svntest.Failure

  sbox.build(read_only = True)

  exit_code, output, errput = svntest.main.run_svn(None, "info",
                                                   sbox.wc_dir)
  check_repos_root(output)

  exit_code, output, errput = svntest.main.run_svn(None, "info",
                                                   os.path.join(sbox.wc_dir,
                                                                "A"))
  check_repos_root(output)

  exit_code, output, errput = svntest.main.run_svn(None, "info",
                                                   os.path.join(sbox.wc_dir,
                                                                "A", "B",
                                                                "lambda"))
  check_repos_root(output)

def basic_peg_revision(sbox):
  "checks peg revision on filename with @ sign"

  sbox.build()
  wc_dir = sbox.wc_dir
  repos_dir = sbox.repo_url
  filename = 'abc@abc'
  wc_file = os.path.join(wc_dir,  filename)
  url = repos_dir + '/' + filename

  svntest.main.file_append(wc_file, 'xyz\n')
  # We need to escape the @ in the middle of abc@abc by appending another @
  svntest.main.run_svn(None, 'add', wc_file + '@')
  svntest.main.run_svn(None,
                       'ci', '-m', 'secret log msg', wc_file + '@')

  # Without the trailing "@", expect failure.
  exit_code, output, errlines = svntest.actions.run_and_verify_svn(
    None, None, ".*Syntax error parsing revision 'abc'", 'cat', wc_file)
  exit_code, output, errlines = svntest.actions.run_and_verify_svn(
    None, None, ".*Syntax error parsing revision 'abc'", 'cat', url)

  # With the trailing "@", expect success.
  exit_code, output, errlines = svntest.actions.run_and_verify_svn(
    None, ["xyz\n"], [], 'cat', wc_file + '@')
  exit_code, output, errlines = svntest.actions.run_and_verify_svn(
    None, ["xyz\n"], [], 'cat', url + '@')

  # Test with leading @ character in filename.
  filename = '@abc'
  wc_file = os.path.join(wc_dir,  filename)
  url = repos_dir + '/' + filename

  svntest.main.file_append(wc_file, 'xyz\n')
  exit_code, output, errlines = svntest.actions.run_and_verify_svn(
    None, None, [], 'add', wc_file + '@')
  exit_code, output, errlines = svntest.actions.run_and_verify_svn(
    None, None, [], 'ci', '-m', 'secret log msg',  wc_file + '@')

  # With a leading "@" which isn't escaped, expect failure.
  # Note that we just test with filename starting with '@', because
  # wc_file + '@' + filename is a different situation where svn
  # will try to parse filename as a peg revision.
  exit_code, output, errlines = svntest.actions.run_and_verify_svn(
    None, None, ".*'%s' is just a peg revision.*" % filename,
    'cat', filename)

  # With a leading "@" which is escaped, expect success.
  exit_code, output, errlines = svntest.actions.run_and_verify_svn(
    None, ["xyz\n"], [], 'cat', wc_file + '@')
  exit_code, output, errlines = svntest.actions.run_and_verify_svn(
    None, ["xyz\n"], [], 'cat', repos_dir + '/' + filename + '@')

def info_nonhead(sbox):
  "info on file not existing in HEAD"
  sbox.build()

  wc_dir = sbox.wc_dir
  repo_url = sbox.repo_url
  fname = os.path.join(wc_dir, 'iota')
  furl = repo_url + "/iota"

  # Remove iota and commit.
  svntest.actions.run_and_verify_svn(None, None, [],
                                     "delete", fname)
  expected_output = svntest.wc.State(wc_dir, {
    'iota' : Item(verb='Deleting'),
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.remove("iota")
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output,
                                        expected_status,
                                        None,
                                        wc_dir)
  # Get info for old iota at r1.
  exit_code, output, errput = svntest.actions.run_and_verify_svn(None, None,
                                                                 [], 'info',
                                                                 furl + '@1',
                                                                 '-r1')
  got_url = 0
  for line in output:
    if line.find("URL:") >= 0:
      got_url = 1
  if not got_url:
    print("Info didn't output an URL.")
    raise svntest.Failure



#----------------------------------------------------------------------
# Issue #2442.
def ls_nonhead(sbox):
  "ls a path no longer in HEAD"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Delete A/D/rho and commit.
  G_path = os.path.join(wc_dir, 'A', 'D', 'G')
  svntest.actions.run_and_verify_svn("error scheduling A/D/G for deletion",
                                     None, [], 'rm', G_path)

  expected_output = wc.State(wc_dir, {
    'A/D/G' : Item(verb='Deleting'),
    })

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.remove('A/D/G', 'A/D/G/rho', 'A/D/G/pi', 'A/D/G/tau',)

  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output, expected_status,
                                        None, wc_dir)

  # Check that we can list a file in A/D/G at revision 1.
  rho_url = sbox.repo_url + "/A/D/G/rho"
  svntest.actions.run_and_verify_svn(None, '.* rho\n', [],
                                     'ls', '--verbose', rho_url + '@1')


#----------------------------------------------------------------------
# Issue #2315.
def cat_added_PREV(sbox):
  "cat added file using -rPREV"

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir
  f_path = os.path.join(wc_dir, 'f')

  # Create and add a file.
  svntest.main.file_append(f_path, 'new text')
  svntest.actions.run_and_verify_svn("adding file",
                                     None, [], 'add', f_path)

  # Cat'ing the previous version should fail.
  svntest.actions.run_and_verify_svn("cat PREV version of file",
                                     None, ".*has no committed revision.*",
                                     'cat', '-rPREV', f_path)

# Issue #2612.
def ls_space_in_repo_name(sbox):
  'basic ls of repos with space in name'

  sbox.build(name = "repo with spaces")
  wc_dir = sbox.wc_dir

  svntest.actions.run_and_verify_svn('ls the root of the repository',
                                     ['A/\n', 'iota\n'],
                                     [], 'ls',
                                     sbox.repo_url)


def delete_keep_local(sbox):
  'delete file and directory with --keep-local'

  sbox.build()
  wc_dir = sbox.wc_dir
  iota_path = os.path.join(wc_dir, 'iota')
  C_path = os.path.join(wc_dir, 'A', 'C')

  # Remove file iota
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', '--keep-local',
                                     iota_path)

  # Remove directory 'A/C'
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', '--keep-local',
                                     C_path)

  # Commit changes
  expected_output = wc.State(wc_dir, {
    'iota' : Item(verb='Deleting'),
    'A/C' : Item(verb='Deleting'),
    })

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.remove('iota')
  expected_status.remove('A/C')

  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output,
                                        expected_status,
                                        None,
                                        wc_dir)

  # Update working copy to check disk state still greek tree
  expected_disk = svntest.main.greek_state.copy()
  expected_output = svntest.wc.State(wc_dir, {})
  expected_status.tweak(wc_rev = 2);

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

def delete_keep_local_twice(sbox):
  'delete file and directory with --keep-local twice'
  
  sbox.build()
  wc_dir = sbox.wc_dir
  
  dir = os.path.join(wc_dir, 'dir')
  
  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', dir)
  
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', '--keep-local', dir)
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', '--keep-local', dir)
  
  if not os.path.isdir(dir):
    print 'Directory was really deleted'
    raise svntest.Failure

def windows_paths_in_repos(sbox):
  "use folders with names like 'c:hi'"

  sbox.build(create_wc = False)
  repo_url       = sbox.repo_url

  chi_url = sbox.repo_url + '/c:hi'

  # do some manipulations on a folder containing a windows drive name.
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'mkdir', '-m', 'log_msg',
                                     chi_url)

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'rm', '-m', 'log_msg',
                                     chi_url)

def basic_rm_urls_one_repo(sbox):
  "remotely remove directories from one repository"

  sbox.build()
  repo_url = sbox.repo_url
  wc_dir = sbox.wc_dir

  # Test 1: remotely delete one directory
  E_url = repo_url + '/A/B/E'

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'rm', '-m', 'log_msg',
                                     E_url)

  # Create expected trees and update
  expected_output = svntest.wc.State(wc_dir, {
    'A/B/E' : Item(status='D '),
    })
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')

  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')

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

  # Test 2: remotely delete two directories in the same repository
  F_url = repo_url + '/A/B/F'
  C_url = repo_url + '/A/C'

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'rm', '-m', 'log_msg',
                                     F_url, C_url)

  # Create expected output tree for an update of wc_backup.
  expected_output = svntest.wc.State(wc_dir, {
    'A/B/F' : Item(status='D '),
    'A/C'   : Item(status='D '),
    })

  # Create expected disk tree for the update
  expected_disk.remove('A/B/F', 'A/C')

  # Create expected status tree for the update.
  expected_status.tweak(wc_rev = 3)
  expected_status.remove('A/B/F', 'A/C')

  # Do the update and check the results in three ways.
  svntest.actions.run_and_verify_update(wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status)

def basic_rm_urls_multi_repos(sbox):
  "remotely remove directories from two repositories"

  sbox.build()
  repo_url = sbox.repo_url
  repo_dir = sbox.repo_dir
  wc_dir = sbox.wc_dir

  # create a second repository and working copy
  other_repo_dir, other_repo_url = sbox.add_repo_path("other")
  svntest.main.copy_repos(repo_dir, other_repo_dir, 1, 1)
  other_wc_dir = sbox.add_wc_path("other")
  svntest.actions.run_and_verify_svn("Unexpected error during co",
                                     svntest.verify.AnyOutput, [], "co",
                                     other_repo_url,
                                     other_wc_dir)

  # Remotely delete two x two directories in the two repositories
  F_url = repo_url + '/A/B/F'
  C_url = repo_url + '/A/C'
  F2_url = other_repo_url + '/A/B/F'
  C2_url = other_repo_url + '/A/C'

  svntest.actions.run_and_verify_svn(None, None, [], 'rm', '-m', 'log_msg',
                                     F_url, C_url, F2_url, C2_url)

  # Check that the two rm's to each of the repositories were handled in one
  # revision (per repo)
  expected_output = svntest.wc.State(wc_dir, {
    'A/B/F' : Item(status='D '),
    'A/C'   : Item(status='D '),
    })
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.remove('A/B/F', 'A/C')
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.remove('A/B/F', 'A/C')

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

  expected_status = svntest.actions.get_virginal_state(other_wc_dir, 2)
  expected_status.remove('A/B/F', 'A/C')
  expected_output = svntest.wc.State(other_wc_dir, {
    'A/B/F' : Item(status='D '),
    'A/C'   : Item(status='D '),
    })

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

#-----------------------------------------------------------------------
def automatic_conflict_resolution(sbox):
  "automatic conflict resolution"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Make a backup copy of the working copy
  wc_backup = sbox.add_wc_path('backup')
  svntest.actions.duplicate_dir(wc_dir, wc_backup)

  # Make a couple of local mods to files which will be committed
  mu_path = os.path.join(wc_dir, 'A', 'mu')
  lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
  tau_path = os.path.join(wc_dir, 'A', 'D', 'G', 'tau')
  omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
  svntest.main.file_append(mu_path, 'Original appended text for mu\n')
  svntest.main.file_append(lambda_path, 'Original appended text for lambda\n')
  svntest.main.file_append(rho_path, 'Original appended text for rho\n')
  svntest.main.file_append(tau_path, 'Original appended text for tau\n')
  svntest.main.file_append(omega_path, 'Original appended text for omega\n')

  # Make a couple of local mods to files which will be conflicted
  mu_path_backup = os.path.join(wc_backup, 'A', 'mu')
  lambda_path_backup = os.path.join(wc_backup, 'A', 'B', 'lambda')
  rho_path_backup = os.path.join(wc_backup, 'A', 'D', 'G', 'rho')
  tau_path_backup = os.path.join(wc_backup, 'A', 'D', 'G', 'tau')
  omega_path_backup = os.path.join(wc_backup, 'A', 'D', 'H', 'omega')
  svntest.main.file_append(mu_path_backup,
                             'Conflicting appended text for mu\n')
  svntest.main.file_append(lambda_path_backup,
                             'Conflicting appended text for lambda\n')
  svntest.main.file_append(rho_path_backup,
                             'Conflicting appended text for rho\n')
  svntest.main.file_append(tau_path_backup,
                             'Conflicting appended text for tau\n')
  svntest.main.file_append(omega_path_backup,
                             'Conflicting appended text for omega\n')

  # Created expected output tree for 'svn ci'
  expected_output = wc.State(wc_dir, {
    'A/mu' : Item(verb='Sending'),
    'A/B/lambda' : Item(verb='Sending'),
    'A/D/G/rho' : Item(verb='Sending'),
    'A/D/G/tau' : Item(verb='Sending'),
    'A/D/H/omega' : Item(verb='Sending'),
    })

  # Create expected status tree; all local revisions should be at 1,
  # but lambda, mu and rho should be at revision 2.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/mu', 'A/B/lambda', 'A/D/G/rho', 'A/D/G/tau',
                        'A/D/H/omega', wc_rev=2)

  # Commit.
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        expected_status, None, wc_dir)

  # Create expected output tree for an update of the wc_backup.
  expected_output = wc.State(wc_backup, {
    'A/mu' : Item(status='C '),
    'A/B/lambda' : Item(status='C '),
    'A/D/G/rho' : Item(status='C '),
    'A/D/G/tau' : Item(status='C '),
    'A/D/H/omega' : Item(status='C '),
    })

  # Create expected disk tree for the update.
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.tweak('A/B/lambda',
                      contents="\n".join(["This is the file 'lambda'.",
                                          "<<<<<<< .mine",
                                          "Conflicting appended text for lambda",
                                          "=======",
                                          "Original appended text for lambda",
                                          ">>>>>>> .r2",
                                          ""]))
  expected_disk.tweak('A/mu',
                      contents="\n".join(["This is the file 'mu'.",
                                          "<<<<<<< .mine",
                                          "Conflicting appended text for mu",
                                          "=======",
                                          "Original appended text for mu",
                                          ">>>>>>> .r2",
                                          ""]))
  expected_disk.tweak('A/D/G/rho',
                      contents="\n".join(["This is the file 'rho'.",
                                          "<<<<<<< .mine",
                                          "Conflicting appended text for rho",
                                          "=======",
                                          "Original appended text for rho",
                                          ">>>>>>> .r2",
                                          ""]))
  expected_disk.tweak('A/D/G/tau',
                      contents="\n".join(["This is the file 'tau'.",
                                          "<<<<<<< .mine",
                                          "Conflicting appended text for tau",
                                          "=======",
                                          "Original appended text for tau",
                                          ">>>>>>> .r2",
                                          ""]))
  expected_disk.tweak('A/D/H/omega',
                      contents="\n".join(["This is the file 'omega'.",
                                          "<<<<<<< .mine",
                                          "Conflicting appended text for omega",
                                          "=======",
                                          "Original appended text for omega",
                                          ">>>>>>> .r2",
                                          ""]))

  # Create expected status tree for the update.
  expected_status = svntest.actions.get_virginal_state(wc_backup, '2')
  expected_status.tweak('A/mu', 'A/B/lambda', 'A/D/G/rho', 'A/D/G/tau',
                        'A/D/H/omega', status='C ')

  # "Extra" files that we expect to result from the conflicts.
  # These are expressed as list of regexps.  What a cool system!  :-)
  extra_files = ['mu.*\.r1', 'mu.*\.r2', 'mu.*\.mine',
                 'lambda.*\.r1', 'lambda.*\.r2', 'lambda.*\.mine',
                 'omega.*\.r1', 'omega.*\.r2', 'omega.*\.mine',
                 'rho.*\.r1', 'rho.*\.r2', 'rho.*\.mine',
                 'tau.*\.r1', 'tau.*\.r2', 'tau.*\.mine',
                 ]

  # Do the update and check the results in three ways.
  # All "extra" files are passed to detect_conflict_files().
  svntest.actions.run_and_verify_update(wc_backup,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        None,
                                        svntest.tree.detect_conflict_files,
                                        extra_files)

  # verify that the extra_files list is now empty.
  if len(extra_files) != 0:
    # Because we want to be a well-behaved test, we silently raise if
    # the test fails.  However, these two print statements would
    # probably reveal the cause for the failure, if they were
    # uncommented:
    #
    # print "Not all extra reject files have been accounted for:"
    # print extra_files
    ### we should raise a less generic error here. which?
    raise svntest.Failure

  # So now lambda, mu and rho are all in a "conflicted" state.  Run 'svn
  # resolve' with the respective "--accept[mine|orig|repo]" flag.

  # But first, check --accept actions resolved does not accept.
  svntest.actions.run_and_verify_svn(None,
                                     # stdout, stderr
                                     None,
                                     ".*invalid 'accept' ARG",
                                     'resolve', '--accept=postpone')
  svntest.actions.run_and_verify_svn(None,
                                     # stdout, stderr
                                     None,
                                     ".*invalid 'accept' ARG",
                                     'resolve', '--accept=edit')
  svntest.actions.run_and_verify_svn(None,
                                     # stdout, stderr
                                     None,
                                     ".*invalid 'accept' ARG",
                                     'resolve', '--accept=launch')
  # Run 'svn resolved --accept=NOPE.  Using omega for the test.
  svntest.actions.run_and_verify_svn("Resolve command", None,
                                     ".*NOPE' is not a valid --accept value",
                                     'resolve',
                                     '--accept=NOPE',
                                     omega_path_backup)

  # Resolve lambda, mu, and rho with different --accept options.
  svntest.actions.run_and_verify_svn("Resolve command", None, [],
                                     'resolve', '--accept=base',
                                     lambda_path_backup)
  svntest.actions.run_and_verify_svn("Resolve command", None, [],
                                     'resolve',
                                     '--accept=mine-full',
                                     mu_path_backup)
  svntest.actions.run_and_verify_svn("Resolve command", None, [],
                                     'resolve',
                                     '--accept=theirs-full',
                                     rho_path_backup)
  fp = open(tau_path_backup, 'w')
  fp.write("Resolution text for 'tau'.\n")
  fp.close()
  svntest.actions.run_and_verify_svn("Resolve command", None, [],
                                     'resolve',
                                     '--accept=working',
                                     tau_path_backup)

  # Set the expected disk contents for the test
  expected_disk = svntest.main.greek_state.copy()

  expected_disk.tweak('A/B/lambda', contents="This is the file 'lambda'.\n")
  expected_disk.tweak('A/mu', contents="This is the file 'mu'.\n"
                      "Conflicting appended text for mu\n")
  expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n"
                      "Original appended text for rho\n")
  expected_disk.tweak('A/D/G/tau', contents="Resolution text for 'tau'.\n")
  expected_disk.tweak('A/D/H/omega',
                      contents="\n".join(["This is the file 'omega'.",
                                          "<<<<<<< .mine",
                                          "Conflicting appended text for omega",
                                          "=======",
                                          "Original appended text for omega",
                                          ">>>>>>> .r2",
                                          ""]))

  # Set the expected extra files for the test
  extra_files = ['omega.*\.r1', 'omega.*\.r2', 'omega.*\.mine',]

  # Set the expected status for the test
  expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
  expected_status.tweak('A/mu', 'A/B/lambda', 'A/D/G/rho', 'A/D/G/tau',
                        'A/D/H/omega', wc_rev=2)

  expected_status.tweak('A/mu', status='M ')
  expected_status.tweak('A/B/lambda', status='M ')
  expected_status.tweak('A/D/G/rho', status='  ')
  expected_status.tweak('A/D/G/tau', status='M ')
  expected_status.tweak('A/D/H/omega', status='C ')

  # Set the expected output for the test
  expected_output = wc.State(wc_backup, {})

  # Do the update and check the results in three ways.
  svntest.actions.run_and_verify_update(wc_backup,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        None,
                                        svntest.tree.detect_conflict_files,
                                        extra_files)

def info_nonexisting_file(sbox):
  "get info on a file not in the repo"

  sbox.build(create_wc = False, read_only = True)
  idonotexist_url = sbox.repo_url + '/IdoNotExist'
  exit_code, output, errput = svntest.main.run_svn(1, 'info', idonotexist_url)

  # Check for the correct error message
  for line in errput:
    if re.match(".*\(Not a valid URL\).*", line):
      return

  # Else never matched the expected error output, so the test failed.
  raise svntest.main.SVNUnmatchedError


#----------------------------------------------------------------------
# Relative urls
#
# Use blame to test three specific cases for relative url support.
def basic_relative_url_using_current_dir(sbox):
  "basic relative url target using current dir"

  # We'll use blame to test relative url parsing
  sbox.build()

  # First, make a new revision of iota.
  iota = os.path.join(sbox.wc_dir, 'iota')
  svntest.main.file_append(iota, "New contents for iota\n")
  svntest.main.run_svn(None, 'ci',
                       '-m', '', iota)

  expected_output = [
    "     1    jrandom This is the file 'iota'.\n",
    "     2    jrandom New contents for iota\n",
    ]

  orig_dir = os.getcwd()
  os.chdir(sbox.wc_dir)

  exit_code, output, error = svntest.actions.run_and_verify_svn(None,
                                expected_output, [],
                                'blame', '^/iota')

  os.chdir(orig_dir)

def basic_relative_url_using_other_targets(sbox):
  "basic relative url target using other targets"

  sbox.build()

  # First, make a new revision of iota.
  iota = os.path.join(sbox.wc_dir, 'iota')
  svntest.main.file_append(iota, "New contents for iota\n")
  svntest.main.run_svn(None, 'ci',
                       '-m', '', iota)

  # Now, make a new revision of A/mu .
  mu = os.path.join(sbox.wc_dir, 'A', 'mu')
  mu_url = sbox.repo_url + '/A/mu'

  svntest.main.file_append(mu, "New contents for mu\n")
  svntest.main.run_svn(None, 'ci',
                       '-m', '', mu)


  expected_output = [
    "     1    jrandom This is the file 'iota'.\n",
    "     2    jrandom New contents for iota\n",
    "     1    jrandom This is the file 'mu'.\n",
    "     3    jrandom New contents for mu\n",
    ]

  exit_code, output, error = svntest.actions.run_and_verify_svn(None,
                                expected_output, [], 'blame',
                                '^/iota', mu_url)

def basic_relative_url_multi_repo(sbox):
  "basic relative url target with multiple repos"

  sbox.build()
  repo_url1 = sbox.repo_url
  repo_dir1 = sbox.repo_dir
  wc_dir1 = sbox.wc_dir

  repo_dir2, repo_url2 = sbox.add_repo_path("other")
  svntest.main.copy_repos(repo_dir1, repo_dir2, 1, 1)
  wc_dir2 = sbox.add_wc_path("other")
  svntest.actions.run_and_verify_svn("Unexpected error during co",
                                     svntest.verify.AnyOutput, [], "co",
                                     repo_url2,
                                     wc_dir2)

  # Don't bother with making new revisions, the command should not work.
  iota_url_repo1 = repo_url1 + '/iota'
  iota_url_repo2 = repo_url2 + '/iota'

  exit_code, output, error = svntest.actions.run_and_verify_svn(None, [],
                                svntest.verify.AnyOutput, 'blame',
                                '^/A/mu', iota_url_repo1, iota_url_repo2)

def basic_relative_url_non_canonical(sbox):
  "basic relative url non-canonical targets"

  sbox.build()

  iota_url = sbox.repo_url + '/iota'

  expected_output = [
    "B/\n",
    "C/\n",
    "D/\n",
    "mu\n",
    "iota\n"
    ]

  exit_code, output, error = svntest.actions.run_and_verify_svn(None,
                                expected_output, [], 'ls',
                                '^/A/', iota_url)

  exit_code, output, error = svntest.actions.run_and_verify_svn(None,
                                expected_output, [], 'ls',
                                '^//A/', iota_url)

def basic_relative_url_with_peg_revisions(sbox):
  "basic relative url targets with peg revisions"

  sbox.build()

  # First, make a new revision of iota.
  iota = os.path.join(sbox.wc_dir, 'iota')
  svntest.main.file_append(iota, "New contents for iota\n")
  svntest.main.run_svn(None, 'ci',
                       '-m', '', iota)

  iota_url = sbox.repo_url + '/iota'

  # Now, make a new revision of A/mu .
  mu = os.path.join(sbox.wc_dir, 'A', 'mu')
  mu_url = sbox.repo_url + '/A/mu'

  svntest.main.file_append(mu, "New contents for mu\n")
  svntest.main.run_svn(None, 'ci', '-m', '', mu)

  # Delete the file from the current revision
  svntest.main.run_svn(None, 'rm', '-m', '', mu_url)

  expected_output = [
    "B/\n",
    "C/\n",
    "D/\n",
    "mu\n",
    "iota\n"
    ]

  # Canonical version with peg revision
  exit_code, output, error = svntest.actions.run_and_verify_svn(None,
                                expected_output, [], 'ls', '-r3',
                                '^/A/@3', iota_url)

  # Non-canonical version with peg revision
  exit_code, output, error = svntest.actions.run_and_verify_svn(None,
                                expected_output, [], 'ls', '-r3',
                                '^//A/@3', iota_url)


# Issue 2242, auth cache picking up password from wrong username entry
def basic_auth_test(sbox):
  "basic auth test"

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  # Set up a custom config directory
  tmp_dir = os.path.abspath(svntest.main.temp_dir)
  config_dir = os.path.join(tmp_dir, 'auth-test-config')
  svntest.main.create_config_dir(config_dir, None)

  # Checkout with jrandom
  exit_code, output, errput = svntest.main.run_command(
    svntest.main.svn_binary, None, 1, 'co', sbox.repo_url, wc_dir,
    '--username', 'jrandom', '--password', 'rayjandom',
    '--config-dir', config_dir)

  exit_code, output, errput = svntest.main.run_command(
    svntest.main.svn_binary, None, 1, 'co', sbox.repo_url, wc_dir,
    '--username', 'jrandom', '--non-interactive', '--config-dir', config_dir)

  # Checkout with jconstant
  exit_code, output, errput = svntest.main.run_command(
    svntest.main.svn_binary, None, 1, 'co', sbox.repo_url, wc_dir,
    '--username', 'jconstant', '--password', 'rayjandom',
    '--config-dir', config_dir)

  exit_code, output, errput = svntest.main.run_command(
    svntest.main.svn_binary, None, 1, 'co', sbox.repo_url, wc_dir,
    '--username', 'jconstant', '--non-interactive',
    '--config-dir', config_dir)

  # Checkout with jrandom which should fail since we do not provide
  # a password and the above cached password belongs to jconstant
  expected_err = ["authorization failed: Could not authenticate to server:"]
  exit_code, output, errput = svntest.main.run_command(
    svntest.main.svn_binary, expected_err, 1, 'co', sbox.repo_url, wc_dir,
    '--username', 'jrandom', '--non-interactive', '--config-dir', config_dir)

def basic_add_svn_format_file(sbox):
  'test add --parents .svn/format'

  sbox.build()
  wc_dir = sbox.wc_dir

  entries_path = os.path.join(wc_dir, '.svn', 'format')

  output = svntest.actions.get_virginal_state(wc_dir, 1)

  # The .svn directory and the format file should not be added as this
  # breaks the administrative area handling, so we expect some error here
  svntest.actions.run_and_verify_svn(None, None,
                                     ".*reserved name.*",
                                     'add', '--parents', entries_path)

  svntest.actions.run_and_verify_status(wc_dir, output)

def delete_from_url_with_spaces(sbox):
  "delete a directory with ' ' using its url"
  
  sbox.build()
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'mkdir',
                                     os.path.join(sbox.wc_dir,
                                                  'Dir With Spaces'))
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'mkdir', '--parents',
                                     os.path.join(sbox.wc_dir,
                                                  'Dir With/Spaces'))

  svntest.actions.run_and_verify_svn(None, None, [],
                                      'ci', sbox.wc_dir, '-m', 'Added dir')
  
  # This fails on 1.6.11 with an escaping error.
  svntest.actions.run_and_verify_svn(None, None, [],
                                      'rm', sbox.repo_url + '/Dir%20With%20Spaces',
                                      '-m', 'Deleted')

  svntest.actions.run_and_verify_svn(None, None, [],
                                      'rm', sbox.repo_url + '/Dir%20With/Spaces',
                                      '-m', 'Deleted')

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

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

# list all tests here, starting with None:
test_list = [ None,
              basic_checkout,
              basic_status,
              basic_commit,
              basic_update,
              basic_mkdir_url,
              basic_mkdir_url_with_parents,
              basic_mkdir_wc_with_parents,
              basic_corruption,
              basic_merging_update,
              basic_conflict,
              basic_cleanup,
              basic_revert,
              basic_switch,
              basic_delete,
              basic_checkout_deleted,
              basic_node_kind_change,
              basic_import,
              basic_cat,
              basic_ls,
              nonexistent_repository,
              basic_auth_cache,
              basic_add_ignores,
              basic_add_parents,
              uri_syntax,
              basic_checkout_file,
              basic_info,
              basic_add_local_ignores,
              basic_add_no_ignores,
              repos_root,
              basic_peg_revision,
              info_nonhead,
              ls_nonhead,
              cat_added_PREV,
              ls_space_in_repo_name,
              delete_keep_local,
              delete_keep_local_twice,
              windows_paths_in_repos,
              basic_rm_urls_one_repo,
              XFail(basic_rm_urls_multi_repos),
              automatic_conflict_resolution,
              info_nonexisting_file,
              basic_relative_url_using_current_dir,
              basic_relative_url_using_other_targets,
              basic_relative_url_multi_repo,
              basic_relative_url_non_canonical,
              basic_relative_url_with_peg_revisions,
              basic_auth_test,
              basic_add_svn_format_file,
              delete_from_url_with_spaces,
             ]

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


### End of file.
