#!/usr/bin/env python
#
#  lock_tests.py:  testing versioned properties
#
#  Subversion is a tool for revision control.
#  See http://subversion.tigris.org for more information.
#
# ====================================================================
# Copyright (c) 2005-2006, 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 re, os, stat

# Our testing module
import svntest

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

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

#----------------------------------------------------------------------
# Each test refers to a section in
# notes/locking/locking-functional-spec.txt

# II.A.2, II.C.2.a: Lock a file in wc A as user FOO and make sure we
# have a representation of it.  Checkout wc B as user BAR.  Verify
# that user BAR cannot commit changes to the file nor its properties.
def lock_file(sbox):
  "lock a file and verify that it's locked"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Make a second copy of the working copy
  wc_b = sbox.add_wc_path('_b')
  svntest.actions.duplicate_dir(wc_dir, wc_b)

  # lock a file as wc_author
  fname = 'iota'
  file_path = os.path.join(sbox.wc_dir, fname)
  file_path_b = os.path.join(wc_b, fname)

  svntest.main.file_append(file_path, "This represents a binary file\n")
  svntest.main.run_svn(None, 'commit',
                       '-m', '', file_path)
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', file_path)

  # --- Meanwhile, in our other working copy... ---
  err_re = "((.*User jconstant does not own lock on path.*)|(.*423 Locked.*))"

  svntest.main.run_svn(None, 'update', wc_b)
  # -- Try to change a file --
  # change the locked file
  svntest.main.file_append(file_path_b, "Covert tweak\n")

  # attempt (and fail) to commit as user Sally
  svntest.actions.run_and_verify_commit(wc_b, None, None, err_re,
                                        '--username',
                                        svntest.main.wc_author2,
                                        '-m', '', file_path_b)

  # Revert our change that we failed to commit
  svntest.main.run_svn(None, 'revert', file_path_b)

  # -- Try to change a property --
  # change the locked file's properties
  svntest.main.run_svn(None, 'propset', 'sneakyuser', 'Sally', file_path_b)

  err_re = "((.*User jconstant does not own lock on path.*)" + \
             "|(.*At least one property change failed.*))"

  # attempt (and fail) to commit as user Sally
  svntest.actions.run_and_verify_commit(wc_b, None, None, err_re,
                                        '--username',
                                        svntest.main.wc_author2,
                                        '-m', '', file_path_b)




#----------------------------------------------------------------------
# II.C.2.b.[12]: Lock a file and commit using the lock.  Make sure the
# lock is released.  Repeat, but request that the lock not be
# released.  Make sure the lock is retained.
def commit_file_keep_lock(sbox):
  "commit a file and keep lock"

  sbox.build()
  wc_dir = sbox.wc_dir

  fname = 'A/mu'
  file_path = os.path.join(sbox.wc_dir, fname)

  # lock fname as wc_author
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', 'some lock comment', file_path)

  # make a change and commit it, holding lock
  svntest.main.file_append(file_path, "Tweak!\n")
  svntest.main.run_svn(None, 'commit', '-m', '', '--no-unlock',
                       file_path)

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak(fname, wc_rev=2)
  expected_status.tweak(fname, writelocked='K')

  # Make sure the file is still locked
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

def commit_file_unlock(sbox):
  "commit a file and release lock"

  sbox.build()
  wc_dir = sbox.wc_dir

  fname = 'A/mu'
  file_path = os.path.join(sbox.wc_dir, fname)

  # lock fname as wc_author
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', 'some lock comment', file_path)

  # make a change and commit it, allowing lock to be released
  svntest.main.file_append(file_path, "Tweak!\n")
  svntest.main.run_svn(None, 'commit', '-m', '',
                       file_path)

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak(fname, wc_rev=2)

  # Make sure the file is unlocked
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

#----------------------------------------------------------------------
def commit_propchange(sbox):
  "commit a locked file with a prop change"

  sbox.build()
  wc_dir = sbox.wc_dir

  fname = 'A/mu'
  file_path = os.path.join(sbox.wc_dir, fname)

  # lock fname as wc_author
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', 'some lock comment', file_path)

  # make a property change and commit it, allowing lock to be released
  svntest.main.run_svn(None, 'propset', 'blue', 'azul', file_path)
  svntest.main.run_svn(None, 'commit',
                             '-m', '', file_path)

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak(fname, wc_rev=2)

  # Make sure the file is unlocked
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

#----------------------------------------------------------------------
# II.C.2.c: Lock a file in wc A as user FOO.  Attempt to unlock same
# file in same wc as user BAR.  Should fail.
#
# Attempt again with --force.  Should succeed.
#
# II.C.2.c: Lock a file in wc A as user FOO.  Attempt to unlock same
# file in wc B as user FOO.  Should fail.
#
# Attempt again with --force.  Should succeed.
def break_lock(sbox):
  "lock a file and verify lock breaking behavior"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Make a second copy of the working copy
  wc_b = sbox.add_wc_path('_b')
  svntest.actions.duplicate_dir(wc_dir, wc_b)

  # lock a file as wc_author
  fname = 'iota'
  file_path = os.path.join(sbox.wc_dir, fname)
  file_path_b = os.path.join(wc_b, fname)

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', file_path)

  # --- Meanwhile, in our other working copy... ---

  svntest.main.run_svn(None, 'update', wc_b)

  # attempt (and fail) to unlock file

  # This should give a "iota' is not locked in this working copy" error
  svntest.actions.run_and_verify_svn(None, None, ".*not locked",
                                     'unlock',
                                     file_path_b)

  svntest.actions.run_and_verify_svn(None, ".*unlocked", [],
                                     'unlock', '--force',
                                     file_path_b)

#----------------------------------------------------------------------
# II.C.2.d: Lock a file in wc A as user FOO.  Attempt to lock same
# file in wc B as user BAR.  Should fail.
#
# Attempt again with --force.  Should succeed.
#
# II.C.2.d: Lock a file in wc A as user FOO.  Attempt to lock same
# file in wc B as user FOO.  Should fail.
#
# Attempt again with --force.  Should succeed.
def steal_lock(sbox):
  "lock a file and verify lock stealing behavior"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Make a second copy of the working copy
  wc_b = sbox.add_wc_path('_b')
  svntest.actions.duplicate_dir(wc_dir, wc_b)

  # lock a file as wc_author
  fname = 'iota'
  file_path = os.path.join(sbox.wc_dir, fname)
  file_path_b = os.path.join(wc_b, fname)

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', file_path)

  # --- Meanwhile, in our other working copy... ---

  svntest.main.run_svn(None, 'update', wc_b)

  # attempt (and fail) to lock file

  # This should give a "iota' is already locked... error, but exits 0.
  svntest.actions.run_and_verify_svn2(None, None,
                                      ".*already locked", 0,
                                      'lock',
                                      '-m', 'trying to break', file_path_b)

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [],
                                     'lock', '--force',
                                     '-m', 'trying to break', file_path_b)

#----------------------------------------------------------------------
# II.B.2, II.C.2.e: Lock a file in wc A.  Query wc for the
# lock and verify that all lock fields are present and correct.
def examine_lock(sbox):
  "examine the fields of a lockfile for correctness"

  sbox.build()
  wc_dir = sbox.wc_dir

  fname = 'iota'
  comment = 'This is a lock test.'
  file_path = os.path.join(sbox.wc_dir, fname)

  # lock a file as wc_author
  svntest.actions.run_and_validate_lock(file_path,
                                        svntest.main.wc_author)

#----------------------------------------------------------------------
# II.C.1: Lock a file in wc A.  Check out wc B.  Break the lock in wc
# B.  Verify that wc A gracefully cleans up the lock via update as
# well as via commit.
def handle_defunct_lock(sbox):
  "verify behavior when a lock in a wc is defunct"

  sbox.build()
  wc_dir = sbox.wc_dir


  fname = 'iota'
  file_path = os.path.join(sbox.wc_dir, fname)

  # set up our expected status
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)

  # lock the file
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', file_path)

  # Make a second copy of the working copy
  wc_b = sbox.add_wc_path('_b')
  svntest.actions.duplicate_dir(wc_dir, wc_b)
  file_path_b = os.path.join(wc_b, fname)

  # --- Meanwhile, in our other working copy... ---

  # Try unlocking the file in the second wc.
  svntest.actions.run_and_verify_svn(None, ".*unlocked", [], 'unlock',
                                     file_path_b)


  # update the 1st wc, which should clear the lock there
  svntest.main.run_svn(None, 'update', wc_dir)

  # Make sure the file is unlocked
  svntest.actions.run_and_verify_status(wc_dir, expected_status)



#----------------------------------------------------------------------
# II.B.1: Set "svn:needs-lock" property on file in wc A.  Checkout wc
# B and verify that that file is set as read-only.
#
# Tests propset, propdel, lock, and unlock
def enforce_lock(sbox):
  "verify svn:needs-lock read-only behavior"

  sbox.build()
  wc_dir = sbox.wc_dir

  iota_path = os.path.join(wc_dir, 'iota')
  lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
  mu_path = os.path.join(wc_dir, 'A', 'mu')

  # Set some binary properties.
  propval_path = os.path.join(wc_dir, 'propval.tmp')

  # svn:needs-lock value should be forced to a '*'
  svntest.actions.set_prop(None, 'svn:needs-lock', 'foo', iota_path,
                           propval_path)
  svntest.actions.set_prop(None, 'svn:needs-lock', '*', lambda_path,
                           propval_path)
  expected_err = ".*svn: warning: To turn off the svn:needs-lock property,.*"
  svntest.actions.set_prop(expected_err, 'svn:needs-lock', '      ',
                           mu_path, propval_path)

  # Check svn:needs-lock
  svntest.actions.check_prop('svn:needs-lock', iota_path, ['*'])
  svntest.actions.check_prop('svn:needs-lock', lambda_path, ['*'])
  svntest.actions.check_prop('svn:needs-lock', mu_path, ['*'])

  svntest.main.run_svn(None, 'commit',
                       '-m', '', iota_path, lambda_path, mu_path)

  # Now make sure that the perms were flipped on all files
  if os.name == 'posix':
    mode = stat.S_IWGRP | stat.S_IWOTH | stat.S_IWRITE
    if ((os.stat(iota_path)[0] & mode)
        or (os.stat(lambda_path)[0] & mode)
        or (os.stat(mu_path)[0] & mode)):
      print("Setting 'svn:needs-lock' property on a file failed to set")
      print("file mode to read-only.")
      raise svntest.Failure

    # obtain a lock on one of these files...
    svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                       '-m', '', iota_path)

    # ...and verify that the write bit gets set...
    if not (os.stat(iota_path)[0] & mode):
      print("Locking a file with 'svn:needs-lock' failed to set write bit.")
      raise svntest.Failure

    # ...and unlock it...
    svntest.actions.run_and_verify_svn(None, ".*unlocked", [], 'unlock',
                                       iota_path)

    # ...and verify that the write bit gets unset
    if (os.stat(iota_path)[0] & mode):
      print("Unlocking a file with 'svn:needs-lock' failed to unset write bit.")
      raise svntest.Failure

    # Verify that removing the property restores the file to read-write
    svntest.main.run_svn(None, 'propdel', 'svn:needs-lock', iota_path)
    if not (os.stat(iota_path)[0] & mode):
      print("Deleting 'svn:needs-lock' failed to set write bit.")
      raise svntest.Failure

#----------------------------------------------------------------------
# Test that updating a file with the "svn:needs-lock" property works,
# especially on Windows, where renaming A to B fails if B already
# exists and has its read-only bit set.  See also issue #2278.
def update_while_needing_lock(sbox):
  "update handles svn:needs-lock correctly"

  sbox.build()
  wc_dir = sbox.wc_dir

  iota_path = os.path.join(wc_dir, 'iota')
  svntest.main.run_svn(None,
                       'propset', 'svn:needs-lock', 'foo', iota_path)
  svntest.main.run_svn(None,
                       'commit', '-m', 'log msg', iota_path)
  svntest.main.run_svn(None,
                       'up', wc_dir)

  # Lock, modify, commit, unlock, to create r3.
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', iota_path)
  svntest.main.file_append(iota_path, "This line added in r2.\n")
  svntest.main.run_svn(None, 'commit',
                       '-m', '', iota_path) # auto-unlocks

  # Backdate to r2.
  svntest.main.run_svn(None,
                       'update', '-r2', iota_path)

  # Try updating forward to r3 again.  This is where the bug happened.
  svntest.main.run_svn(None,
                       'update', '-r3', iota_path)


#----------------------------------------------------------------------
# Tests update / checkout with changing props
def defunct_lock(sbox):
  "verify svn:needs-lock behavior with defunct lock"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Make a second copy of the working copy
  wc_b = sbox.add_wc_path('_b')
  svntest.actions.duplicate_dir(wc_dir, wc_b)

  iota_path = os.path.join(wc_dir, 'iota')
  iota_path_b = os.path.join(wc_b, 'iota')

  mode = stat.S_IWGRP | stat.S_IWOTH | stat.S_IWRITE

# Set the prop in wc a
  svntest.main.run_svn(None, 'propset', 'svn:needs-lock', 'foo', iota_path)

  # commit r2
  svntest.main.run_svn(None, 'commit',
                       '-m', '', iota_path)

  # update wc_b
  svntest.main.run_svn(None, 'update', wc_b)

  # lock iota in wc_b
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', iota_path_b)


  # break the lock iota in wc a
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock', '--force',
                                     '-m', '', iota_path)
  # update wc_b
  svntest.main.run_svn(None, 'update', wc_b)

  # make sure that iota got set to read-only
  if (os.stat(iota_path_b)[0] & mode):
    print("Upon removal of a defunct lock, a file with 'svn:needs-lock'")
    print("was not set back to read-only")
    raise svntest.Failure



#----------------------------------------------------------------------
# Tests dealing with a lock on a deleted path
def deleted_path_lock(sbox):
  "verify lock removal on a deleted path"

  sbox.build()
  wc_dir = sbox.wc_dir

  iota_path = os.path.join(wc_dir, 'iota')
  iota_url = sbox.repo_url + '/iota'

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', iota_path)

  svntest.actions.run_and_verify_svn(None, None, [], 'delete', iota_path)

  svntest.actions.run_and_verify_svn(None, None, [], 'commit',
                                     '--no-unlock',
                                     '-m', '', iota_path)

  # Now make sure that we can delete the lock from iota via a URL
  svntest.actions.run_and_verify_svn(None, ".*unlocked", [], 'unlock',
                                     iota_url)



#----------------------------------------------------------------------
# Tests dealing with locking and unlocking
def lock_unlock(sbox):
  "lock and unlock some files"

  sbox.build()
  wc_dir = sbox.wc_dir

  pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
  tau_path = os.path.join(wc_dir, 'A', 'D', 'G', 'tau')

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

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', pi_path, rho_path, tau_path)

  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  expected_status.tweak('A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', writelocked=None)

  svntest.actions.run_and_verify_svn(None, ".*unlocked", [], 'unlock',
                                     pi_path, rho_path, tau_path)

  svntest.actions.run_and_verify_status(wc_dir, expected_status)

#----------------------------------------------------------------------
# Tests dealing with directory deletion and locks
def deleted_dir_lock(sbox):
  "verify removal of a directory with locks inside"

  sbox.build()
  wc_dir = sbox.wc_dir

  parent_dir = os.path.join(wc_dir, 'A', 'D', 'G')
  pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
  tau_path = os.path.join(wc_dir, 'A', 'D', 'G', 'tau')

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', pi_path, rho_path, tau_path)

  svntest.actions.run_and_verify_svn(None, None, [], 'delete', parent_dir)

  svntest.actions.run_and_verify_svn(None, None, [], 'commit',
                                     '--no-unlock',
                                     '-m', '', parent_dir)

#----------------------------------------------------------------------
# III.c : Lock a file and check the output of 'svn stat' from the same
# working copy and another.
def lock_status(sbox):
  "verify status of lock in working copy"
  sbox.build()
  wc_dir = sbox.wc_dir

   # Make a second copy of the working copy
  wc_b = sbox.add_wc_path('_b')
  svntest.actions.duplicate_dir(wc_dir, wc_b)

  # lock a file as wc_author
  fname = 'iota'
  file_path = os.path.join(sbox.wc_dir, fname)

  svntest.main.file_append(file_path, "This is a spreadsheet\n")
  svntest.main.run_svn(None, 'commit',
                       '-m', '', file_path)

  svntest.main.run_svn(None, 'lock',
                       '-m', '', file_path)

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak(fname, wc_rev=2)
  expected_status.tweak(fname, writelocked='K')

  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Verify status again after modifying the file
  svntest.main.file_append(file_path, "check stat output after mod")

  expected_status.tweak(fname, status='M ')

  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Verify status of lock from another working copy
  svntest.main.run_svn(None, 'update', wc_b)
  expected_status = svntest.actions.get_virginal_state(wc_b, 2)
  expected_status.tweak(fname, writelocked='O')

  svntest.actions.run_and_verify_status(wc_b, expected_status)

#----------------------------------------------------------------------
# III.c : Steal lock on a file from another working copy with 'svn lock
# --force', and check the status of lock in the repository from the
# working copy in which the file was initially locked.
def stolen_lock_status(sbox):
  "verify status of stolen lock"
  sbox.build()
  wc_dir = sbox.wc_dir

  # Make a second copy of the working copy
  wc_b = sbox.add_wc_path('_b')
  svntest.actions.duplicate_dir(wc_dir, wc_b)

  # lock a file as wc_author
  fname = 'iota'
  file_path = os.path.join(sbox.wc_dir, fname)
  file_path_b = os.path.join(wc_b, fname)

  svntest.main.file_append(file_path, "This is a spreadsheet\n")
  svntest.main.run_svn(None, 'commit',
                       '-m', '', file_path)

  svntest.main.run_svn(None, 'lock',
                       '-m', '', file_path)

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak(fname, wc_rev=2)
  expected_status.tweak(fname, writelocked='K')

  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Forcibly lock same file (steal lock) from another working copy
  svntest.main.run_svn(None, 'update', wc_b)
  svntest.main.run_svn(None, 'lock',
                       '-m', '', '--force', file_path_b)

  # Verify status from working copy where file was initially locked
  expected_status.tweak(fname, writelocked='T')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

#----------------------------------------------------------------------
# III.c : Break lock from another working copy with 'svn unlock --force'
# and verify the status of the lock in the repository with 'svn stat -u'
# from the working copy in the file was initially locked
def broken_lock_status(sbox):
  "verify status of broken lock"
  sbox.build()
  wc_dir = sbox.wc_dir

  # Make a second copy of the working copy
  wc_b = sbox.add_wc_path('_b')
  svntest.actions.duplicate_dir(wc_dir, wc_b)

  # lock a file as wc_author
  fname = 'iota'
  file_path = os.path.join(sbox.wc_dir, fname)
  file_path_b = os.path.join(wc_b, fname)

  svntest.main.file_append(file_path, "This is a spreadsheet\n")
  svntest.main.run_svn(None, 'commit',
                       '-m', '', file_path)
  svntest.main.run_svn(None, 'lock',
                       '-m', '', file_path)

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak(fname, wc_rev=2)
  expected_status.tweak(fname, writelocked='K')

  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Forcibly unlock the same file (break lock) from another working copy
  svntest.main.run_svn(None, 'update', wc_b)
  svntest.main.run_svn(None, 'unlock',
                       '--force', file_path_b)

  # Verify status from working copy where file was initially locked
  expected_status.tweak(fname, writelocked='B')

  svntest.actions.run_and_verify_status(wc_dir, expected_status)

#----------------------------------------------------------------------
# Invalid input test - lock non-existent file
def lock_non_existent_file(sbox):
  "verify error on locking non-existent file"

  sbox.build()
  fname = 'A/foo'
  file_path = os.path.join(sbox.wc_dir, fname)

  exit_code, output, error = svntest.main.run_svn(1, 'lock',
                                                  '-m', '', file_path)

  error_msg = "foo' is not under version control"
  for line in error:
    if line.find(error_msg) != -1:
      break
  else:
    print("Error: %s : not found in: %s" % (error_msg, error))
    raise svntest.Failure

#----------------------------------------------------------------------
# Check that locking an out-of-date file fails.
def out_of_date(sbox):
  "lock an out-of-date file and ensure failure"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Make a second copy of the working copy
  wc_b = sbox.add_wc_path('_b')
  svntest.actions.duplicate_dir(wc_dir, wc_b)

  fname = 'iota'
  file_path = os.path.join(sbox.wc_dir, fname)
  file_path_b = os.path.join(wc_b, fname)

  # Make a new revision of the file in the first WC.
  svntest.main.file_append(file_path, "This represents a binary file\n")
  svntest.main.run_svn(None, 'commit',
                       '-m', '', file_path)

  # --- Meanwhile, in our other working copy... ---
  svntest.actions.run_and_verify_svn2(None, None,
                                      ".*newer version of '/iota' exists", 0,
                                      'lock',
                                      '--username', svntest.main.wc_author2,
                                      '-m', '', file_path_b)

#----------------------------------------------------------------------
# Tests reverting a svn:needs-lock file
def revert_lock(sbox):
  "verify svn:needs-lock behavior with revert"

  sbox.build()
  wc_dir = sbox.wc_dir

  iota_path = os.path.join(wc_dir, 'iota')

  mode = stat.S_IWGRP | stat.S_IWOTH | stat.S_IWRITE

  # set the prop in wc
  svntest.actions.run_and_verify_svn(None, None, [], 'propset',
                                  'svn:needs-lock', 'foo', iota_path)

  # commit r2
  svntest.actions.run_and_verify_svn(None, None, [], 'commit',
                       '-m', '', iota_path)

  # make sure that iota got set to read-only
  if (os.stat(iota_path)[0] & mode):
    print("Committing a file with 'svn:needs-lock'")
    print("did not set the file to read-only")
    raise svntest.Failure

  # verify status is as we expect
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('iota', wc_rev=2)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # remove read-only-ness
  svntest.actions.run_and_verify_svn(None, None, [], 'propdel',
                                  'svn:needs-lock', iota_path)

  # make sure that iota got read-only-ness removed
  if (os.stat(iota_path)[0] & mode == 0):
    print("Deleting the 'svn:needs-lock' property ")
    print("did not remove read-only-ness")
    raise svntest.Failure

  # revert the change
  svntest.actions.run_and_verify_svn(None, None, [], 'revert', iota_path)

  # make sure that iota got set back to read-only
  if (os.stat(iota_path)[0] & mode):
    print("Reverting a file with 'svn:needs-lock'")
    print("did not set the file back to read-only")
    raise svntest.Failure

  # try propdel and revert from a different directory so
  # full filenames are used
  extra_name = 'xx'

  # now lock the file
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                       '-m', '', iota_path)

  # modify it
  svntest.main.file_append(iota_path, "This line added\n")

  expected_status.tweak(wc_rev=1)
  expected_status.tweak('iota', wc_rev=2)
  expected_status.tweak('iota', status='M ', writelocked='K')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # revert it
  svntest.actions.run_and_verify_svn(None, None, [], 'revert', iota_path)

  # make sure it is still writable since we have the lock
  if (os.stat(iota_path)[0] & mode == 0):
    print("Reverting a 'svn:needs-lock' file (with lock in wc) ")
    print("did not leave the file writable")
    raise svntest.Failure


#----------------------------------------------------------------------
def examine_lock_via_url(sbox):
  "examine the fields of a lock from a URL"

  sbox.build()
  wc_dir = sbox.wc_dir

  fname = 'iota'
  comment = 'This is a lock test.'
  file_path = os.path.join(sbox.wc_dir, fname)
  file_url = sbox.repo_url + '/' + fname

  # lock the file url and check the contents of lock
  svntest.actions.run_and_validate_lock(file_url,
                                        svntest.main.wc_author2)

#----------------------------------------------------------------------
def lock_several_files(sbox):
  "lock/unlock several files in one go"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Deliberately have no direct child of A as a target
  iota_path = os.path.join(sbox.wc_dir, 'iota')
  lambda_path = os.path.join(sbox.wc_dir, 'A', 'B', 'lambda')
  alpha_path = os.path.join(sbox.wc_dir, 'A', 'B', 'E', 'alpha')

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '--username', svntest.main.wc_author2,
                                     '-m', 'lock several',
                                     iota_path, lambda_path, alpha_path)

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('iota', 'A/B/lambda', 'A/B/E/alpha', writelocked='K')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  svntest.actions.run_and_verify_svn(None, ".*unlocked", [], 'unlock',
                                     '--username', svntest.main.wc_author2,
                                     iota_path, lambda_path, alpha_path)

  expected_status.tweak('iota', 'A/B/lambda', 'A/B/E/alpha', writelocked=None)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

#----------------------------------------------------------------------
def lock_switched_files(sbox):
  "lock/unlock switched files"

  sbox.build()
  wc_dir = sbox.wc_dir

  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
  lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
  iota_URL = sbox.repo_url + '/iota'
  alpha_URL = sbox.repo_url + '/A/B/E/alpha'

  svntest.actions.run_and_verify_svn(None, None, [], 'switch',
                                     iota_URL, gamma_path)
  svntest.actions.run_and_verify_svn(None, None, [], 'switch',
                                     alpha_URL, lambda_path)

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/D/gamma', 'A/B/lambda', switched='S')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', 'lock several',
                                     gamma_path, lambda_path)

  expected_status.tweak('A/D/gamma', 'A/B/lambda', writelocked='K')
  expected_status.tweak('A/B/E/alpha', 'iota', writelocked='O')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  svntest.actions.run_and_verify_svn(None, ".*unlocked", [], 'unlock',
                                     gamma_path, lambda_path)

  expected_status.tweak('A/D/gamma', 'A/B/lambda', writelocked=None)
  expected_status.tweak('A/B/E/alpha', 'iota', writelocked=None)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

def lock_uri_encoded(sbox):
  "lock and unlock a file with an URI-unsafe name"

  sbox.build()
  wc_dir = sbox.wc_dir

  # lock a file as wc_author
  fname = 'amazing space'
  file_path = os.path.join(wc_dir, fname)

  svntest.main.file_append(file_path, "This represents a binary file\n")
  svntest.actions.run_and_verify_svn(None, None, [], "add", file_path)

  expected_output = svntest.wc.State(wc_dir, {
    fname : Item(verb='Adding'),
    })

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.add({ fname: Item(wc_rev=2, status='  ') })

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

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', file_path)

  # Make sure that the file was locked.
  expected_status.tweak(fname, writelocked='K')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  svntest.actions.run_and_verify_svn(None, ".*unlocked", [], 'unlock',
                                     file_path)

  # Make sure it was successfully unlocked again.
  expected_status.tweak(fname, writelocked=None)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # And now the URL case.
  file_url = sbox.repo_url + '/' + fname
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', file_url)

  # Make sure that the file was locked.
  expected_status.tweak(fname, writelocked='O')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  svntest.actions.run_and_verify_svn(None, ".*unlocked", [], 'unlock',
                                     file_url)

  # Make sure it was successfully unlocked again.
  expected_status.tweak(fname, writelocked=None)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)


#----------------------------------------------------------------------
# A regression test for a bug when svn:needs-lock and svn:executable
# interact badly. The bug was fixed in trunk @ r14859.
def lock_and_exebit1(sbox):
  "svn:needs-lock and svn:executable, part I"

  mode_w = stat.S_IWUSR
  mode_x = stat.S_IXUSR
  mode_r = stat.S_IRUSR

  sbox.build()
  wc_dir = sbox.wc_dir

  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')

  expected_err = ".*svn: warning: To turn off the svn:needs-lock property,.*"
  svntest.actions.run_and_verify_svn2(None, None, expected_err, 0,
                                      'ps', 'svn:needs-lock', ' ', gamma_path)

  expected_err = ".*svn: warning: To turn off the svn:executable property,.*"
  svntest.actions.run_and_verify_svn2(None, None, expected_err, 0,
                                      'ps', 'svn:executable', ' ', gamma_path)

  # commit
  svntest.actions.run_and_verify_svn(None, None, [], 'commit',
                                     '-m', '', gamma_path)
  # mode should be +r, -w, +x
  gamma_stat = os.stat(gamma_path)[0]
  if (not gamma_stat & mode_r
      or gamma_stat & mode_w
      or not gamma_stat & mode_x):
    print("Committing a file with 'svn:needs-lock, svn:executable'")
    print("did not set the file to read-only, executable")
    raise svntest.Failure

  # lock
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', gamma_path)
  # mode should be +r, +w, +x
  gamma_stat = os.stat(gamma_path)[0]
  if (not gamma_stat & mode_r
      or not gamma_stat & mode_w
      or not gamma_stat & mode_x):
    print("Locking a file with 'svn:needs-lock, svn:executable'")
    print("did not set the file to read-write, executable")
    raise svntest.Failure

  # modify
  svntest.main.file_append(gamma_path, "check stat output after mod & unlock")

  # unlock
  svntest.actions.run_and_verify_svn(None, ".*unlocked", [], 'unlock',
                                     gamma_path)

  # Mode should be +r, -w, +x
  gamma_stat = os.stat(gamma_path)[0]
  if (not gamma_stat & mode_r
      or gamma_stat & mode_w
      or not gamma_stat & mode_x):
    print("Unlocking a file with 'svn:needs-lock, svn:executable'")
    print("did not set the file to read-only, executable")
    raise svntest.Failure

  # ci
  svntest.actions.run_and_verify_svn(None, None, [], 'commit',
                                     '-m', '', gamma_path)

  # Mode should be still +r, -w, +x
  gamma_stat = os.stat(gamma_path)[0]
  if (not gamma_stat & mode_r
      or gamma_stat & mode_w
      or not gamma_stat & mode_x):
    print("Commiting a file with 'svn:needs-lock, svn:executable'")
    print("after unlocking modified file's permissions")
    raise svntest.Failure


#----------------------------------------------------------------------
# A variant of lock_and_exebit1: same test without unlock
def lock_and_exebit2(sbox):
  "svn:needs-lock and svn:executable, part II"

  mode_w = stat.S_IWUSR
  mode_x = stat.S_IXUSR
  mode_r = stat.S_IRUSR

  sbox.build()
  wc_dir = sbox.wc_dir

  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')

  expected_err = ".*svn: warning: To turn off the svn:needs-lock property,.*"
  svntest.actions.run_and_verify_svn2(None, None, expected_err, 0,
                                      'ps', 'svn:needs-lock', ' ', gamma_path)

  expected_err = ".*svn: warning: To turn off the svn:executable property,.*"
  svntest.actions.run_and_verify_svn2(None, None, expected_err, 0,
                                     'ps', 'svn:executable', ' ', gamma_path)

  # commit
  svntest.actions.run_and_verify_svn(None, None, [], 'commit',
                                     '-m', '', gamma_path)
  # mode should be +r, -w, +x
  gamma_stat = os.stat(gamma_path)[0]
  if (not gamma_stat & mode_r
      or gamma_stat & mode_w
      or not gamma_stat & mode_x):
    print("Committing a file with 'svn:needs-lock, svn:executable'")
    print("did not set the file to read-only, executable")
    raise svntest.Failure

  # lock
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', gamma_path)
  # mode should be +r, +w, +x
  gamma_stat = os.stat(gamma_path)[0]
  if (not gamma_stat & mode_r
      or not gamma_stat & mode_w
      or not gamma_stat & mode_x):
    print("Locking a file with 'svn:needs-lock, svn:executable'")
    print("did not set the file to read-write, executable")
    raise svntest.Failure

  # modify
  svntest.main.file_append(gamma_path, "check stat output after mod & unlock")

  # commit
  svntest.actions.run_and_verify_svn(None, None, [], 'commit',
                                     '-m', '', gamma_path)

  # Mode should be +r, -w, +x
  gamma_stat = os.stat(gamma_path)[0]
  if (not gamma_stat & mode_r
      or gamma_stat & mode_w
      or not gamma_stat & mode_x):
    print("Commiting a file with 'svn:needs-lock, svn:executable'")
    print("did not set the file to read-only, executable")
    raise svntest.Failure

def commit_xml_unsafe_file_unlock(sbox):
  "commit file with xml-unsafe name and release lock"

  sbox.build()
  wc_dir = sbox.wc_dir

  fname = 'foo & bar'
  file_path = os.path.join(sbox.wc_dir, fname)
  svntest.main.file_append(file_path, "Initial data.\n")
  svntest.main.run_svn(None, 'add', file_path)
  svntest.main.run_svn(None,
                       'commit', '-m', '', file_path)

  # lock fname as wc_author
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', 'some lock comment', file_path)

  # make a change and commit it, allowing lock to be released
  svntest.main.file_append(file_path, "Followup data.\n")
  svntest.main.run_svn(None,
                       'commit', '-m', '', file_path)

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.add({ fname : Item(status='  ', wc_rev=3), })

  # Make sure the file is unlocked
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

#----------------------------------------------------------------------
def repos_lock_with_info(sbox):
  "verify info path@X or path -rY return repos lock"

  sbox.build()
  wc_dir = sbox.wc_dir

  fname = 'iota'
  comment = 'This is a lock test.'
  file_path = os.path.join(sbox.wc_dir, fname)
  file_url = sbox.repo_url + '/' + fname

  # lock wc file
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '--username', svntest.main.wc_author2,
                                     '-m', comment, file_path)
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak(fname, writelocked='K')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Steal lock on wc file
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '--username', svntest.main.wc_author2,
                                     '--force',
                                     '-m', comment, file_url)
  expected_status.tweak(fname, writelocked='T')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Get repository lock token
  exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                              'info', file_url)
  for line in output:
    if line.find("Lock Token:") != -1:
      repos_lock_token = line[12:]
      break
  else:
    print("Error: Lock token not found")
    raise svntest.Failure

  # info with revision option
  exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                              'info',
                                                              file_path, '-r1')

  for line in output:
    if line.find("Lock Token:") != -1:
      lock_token = line[12:]
      break
  else:
    print("Error: Lock token not found")
    raise svntest.Failure

  if (repos_lock_token != lock_token):
    print("Error: expected repository lock information not found.")
    raise svntest.Failure

  # info with peg revision
  exit_code, output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                              'info',
                                                              file_path + '@1')
  for line in output:
    if line.find("Lock Token:") != -1:
      lock_token = line[12:]
      break
  else:
    print("Error: Lock token not found")
    raise svntest.Failure

  if (repos_lock_token != lock_token):
    print("Error: expected repository lock information not found.")
    raise svntest.Failure

#----------------------------------------------------------------------
def unlock_already_unlocked_files(sbox):
  "(un)lock set of files, one already (un)locked"

  sbox.build()
  wc_dir = sbox.wc_dir

  # Deliberately have no direct child of A as a target
  iota_path = os.path.join(wc_dir, 'iota')
  lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
  alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '--username', svntest.main.wc_author2,
                                     '-m', 'lock several',
                                     iota_path, lambda_path, alpha_path)

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('iota', 'A/B/lambda', 'A/B/E/alpha', writelocked='K')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  error_msg = ".*Path '/A/B/E/alpha' is already locked by user '" + \
              svntest.main.wc_author2 + "'.*"
  svntest.actions.run_and_verify_svn2(None, None, error_msg, 0,
                                      'lock',
                                      '--username', svntest.main.wc_author2,
                                      alpha_path, gamma_path)
  expected_status.tweak('A/D/gamma', writelocked='K')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  svntest.actions.run_and_verify_svn(None, ".*unlocked", [], 'unlock',
                                     '--username', svntest.main.wc_author2,
                                     lambda_path)

  expected_status.tweak('A/B/lambda', writelocked=None)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  error_msg = "(.*No lock on path '/A/B/lambda'.*)" + \
              "|(.*'A/B/lambda' is not locked.*)"
  svntest.actions.run_and_verify_svn2(None, None, error_msg, 0,
                                      'unlock',
                                      '--username', svntest.main.wc_author2,
                                      '--force',
                                      iota_path, lambda_path, alpha_path)


  expected_status.tweak('iota', 'A/B/E/alpha', writelocked=None)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

#----------------------------------------------------------------------
def info_moved_path(sbox):
  "show correct lock info on moved path"

  sbox.build()
  wc_dir = sbox.wc_dir
  fname = os.path.join(wc_dir, "iota")
  fname2 = os.path.join(wc_dir, "iota2")

  # Move iota, creating r2.
  svntest.actions.run_and_verify_svn(None, None, [],
                                     "mv", fname, fname2)
  expected_output = svntest.wc.State(wc_dir, {
    'iota2' : Item(verb='Adding'),
    'iota' : Item(verb='Deleting'),
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.add({
    "iota2" : Item(status='  ', wc_rev=2)
    })
  expected_status.remove("iota")
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output,
                                        expected_status,
                                        None,
                                        wc_dir)

  # Create a new, unrelated iota, creating r3.
  svntest.main.file_append(fname, "Another iota")
  svntest.actions.run_and_verify_svn(None, None, [],
                                     "add", fname)
  expected_output = svntest.wc.State(wc_dir, {
    'iota' : Item(verb='Adding'),
    })
  expected_status.add({
    "iota" : Item(status='  ', wc_rev=3)
    })
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output,
                                        expected_status,
                                        None,
                                        wc_dir)

  # Lock the new iota.
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [],
                                     "lock", fname)
  expected_status.tweak("iota", writelocked="K")
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Get info for old iota at r1. This shouldn't give us any lock info.
  exit_code, output, errput = svntest.actions.run_and_verify_svn(
    None, None, [], 'info', fname2, '-r1')

  # Since we want to make sure that there is *no* lock info, to make this
  # more robust, we also check that the info command actually output some info.
  got_url = 0
  for line in output:
    if line.find("URL:") >= 0:
      got_url = 1
    if line.find("Lock Token:") >= 0:
      print(fname2 + " was reported as locked.")
      raise svntest.Failure
  if not got_url:
    print("Info didn't output an URL.")
    raise svntest.Failure

#----------------------------------------------------------------------
def ls_url_encoded(sbox):
  "ls locked path needing URL encoding"

  sbox.build()
  wc_dir = sbox.wc_dir
  dirname = os.path.join(wc_dir, "space dir")
  fname = os.path.join(dirname, "f")

  # Create a dir with a space in its name and a file therein.
  svntest.actions.run_and_verify_svn(None, None, [],
                                     "mkdir", dirname)
  svntest.main.file_append(fname, "someone was here")
  svntest.actions.run_and_verify_svn(None, None, [],
                                     "add", fname)
  expected_output = svntest.wc.State(wc_dir, {
    'space dir' : Item(verb='Adding'),
    'space dir/f' : Item(verb='Adding'),
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.add({
    "space dir" : Item(status='  ', wc_rev=2),
    "space dir/f" : Item(status='  ', wc_rev=2),
    })
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output,
                                        expected_status,
                                        None,
                                        wc_dir)

  # Lock the file.
  svntest.actions.run_and_verify_svn("Lock space dir/f", ".*locked by user",
                                     [], "lock", fname)

  # Make sure ls shows it being locked.
  expected_output = " +2 " + re.escape(svntest.main.wc_author) + " +O .+f|" \
                    " +2 " + re.escape(svntest.main.wc_author) + "    .+\./"
  svntest.actions.run_and_verify_svn("List space dir",
                                     expected_output, [],
                                     "list", "-v", dirname)

#----------------------------------------------------------------------
# Make sure unlocking a path with the wrong lock token fails.
def unlock_wrong_token(sbox):
  "verify unlocking with wrong lock token"

  sbox.build()
  wc_dir = sbox.wc_dir

  # lock a file as wc_author
  fname = 'iota'
  file_path = os.path.join(sbox.wc_dir, fname)
  file_url = sbox.repo_url + "/iota"

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     file_path)

  # Steal the lock as the same author, but using an URL to keep the old token
  # in the WC.
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     "--force", file_url)

  # Then, unlocking the WC path should fail.
  # ### The error message returned is actually this, but let's worry about that
  # ### another day...
  svntest.actions.run_and_verify_svn2(
    None, None, ".*((No lock on path)|(400 Bad Request))", 0,
    'unlock', file_path)

#----------------------------------------------------------------------
# Verify that info shows lock info for locked files with URI-unsafe names
# when run in recursive mode.
def examine_lock_encoded_recurse(sbox):
  "verify recursive info shows lock info"

  sbox.build()
  wc_dir = sbox.wc_dir

  fname = 'A/B/F/one iota'
  file_path = os.path.join(sbox.wc_dir, fname)

  svntest.main.file_append(file_path, "This represents a binary file\n")
  svntest.actions.run_and_verify_svn(None, None, [], "add", file_path)

  expected_output = svntest.wc.State(wc_dir, {
    fname : Item(verb='Adding'),
    })

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.add({ fname: Item(wc_rev=2, status='  ') })

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

  # lock the file and validate the contents
  svntest.actions.run_and_validate_lock(file_path,
                                        svntest.main.wc_author)

# Trying to unlock someone else's lock with --force should fail.
def unlocked_lock_of_other_user(sbox):
  "unlock file locked by other user"

  sbox.build()
  wc_dir = sbox.wc_dir

  # lock a file with user jrandom
  pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/D/G/pi', writelocked='K')

  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', '', pi_path)

  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # now try to unlock with user jconstant, should fail but exit 0.
  if sbox.repo_url.startswith("http"):
    expected_err = ".*403 Forbidden.*"
  else:
    expected_err = "svn: warning: User '%s' is trying to use a lock owned by "\
                   "'%s'.*" % (svntest.main.wc_author2, svntest.main.wc_author)
  svntest.actions.run_and_verify_svn2(None, [], expected_err, 0,
                                      'unlock',
                                      '--username', svntest.main.wc_author2,
                                      pi_path)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)


#----------------------------------------------------------------------
def lock_funky_comment_chars(sbox):
  "lock a file using a comment with xml special chars"

  sbox.build()
  wc_dir = sbox.wc_dir

  # lock a file as wc_author
  fname = 'iota'
  file_path = os.path.join(sbox.wc_dir, fname)

  svntest.main.file_append(file_path, "This represents a binary file\n")
  svntest.main.run_svn(None, 'commit',
                       '-m', '', file_path)
  svntest.actions.run_and_verify_svn(None, ".*locked by user", [], 'lock',
                                     '-m', 'lock & load', file_path)


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

# list all tests here, starting with None:
test_list = [ None,
              lock_file,
              commit_file_keep_lock,
              commit_file_unlock,
              commit_propchange,
              break_lock,
              steal_lock,
              examine_lock,
              handle_defunct_lock,
              enforce_lock,
              defunct_lock,
              deleted_path_lock,
              lock_unlock,
              deleted_dir_lock,
              lock_status,
              stolen_lock_status,
              broken_lock_status,
              lock_non_existent_file,
              out_of_date,
              update_while_needing_lock,
              revert_lock,
              examine_lock_via_url,
              lock_several_files,
              lock_switched_files,
              lock_uri_encoded,
              SkipUnless(lock_and_exebit1, svntest.main.is_posix_os),
              SkipUnless(lock_and_exebit2, svntest.main.is_posix_os),
              commit_xml_unsafe_file_unlock,
              repos_lock_with_info,
              unlock_already_unlocked_files,
              info_moved_path,
              ls_url_encoded,
              XFail(unlock_wrong_token, svntest.main.is_ra_type_dav),
              examine_lock_encoded_recurse,
              XFail(unlocked_lock_of_other_user,
                    svntest.main.is_ra_type_dav),
              lock_funky_comment_chars,
            ]

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


### End of file.
