#!/usr/bin/env python
#
#  stat_tests.py:  testing the svn stat command
#
#  Subversion is a tool for revision control.
#  See http://subversion.apache.org for more information.
#
# ====================================================================
#    Licensed to the Apache Software Foundation (ASF) under one
#    or more contributor license agreements.  See the NOTICE file
#    distributed with this work for additional information
#    regarding copyright ownership.  The ASF licenses this file
#    to you under the Apache License, Version 2.0 (the
#    "License"); you may not use this file except in compliance
#    with the License.  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing,
#    software distributed under the License is distributed on an
#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
#    KIND, either express or implied.  See the License for the
#    specific language governing permissions and limitations
#    under the License.
######################################################################

# General modules
import os
import re
import time
import datetime
import logging

logger = logging.getLogger()

# Our testing module
import svntest
from svntest import wc

# (abbreviation)
Skip = svntest.testcase.Skip_deco
SkipUnless = svntest.testcase.SkipUnless_deco
XFail = svntest.testcase.XFail_deco
Issues = svntest.testcase.Issues_deco
Issue = svntest.testcase.Issue_deco
Wimp = svntest.testcase.Wimp_deco
Item = svntest.wc.StateItem
UnorderedOutput = svntest.verify.UnorderedOutput



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

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

def status_unversioned_file_in_current_dir(sbox):
  "status on unversioned file in current directory"

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

  os.chdir(wc_dir)

  svntest.main.file_append('foo', 'a new file')

  svntest.actions.run_and_verify_svn([ "?       foo\n" ], [],
                                     'stat', 'foo')

#----------------------------------------------------------------------
# Regression for issue #590
@Issue(590)
def status_update_with_nested_adds(sbox):
  "run 'status -u' when nested additions are pending"

  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)

  # Create newdir and newfile
  newdir_path = sbox.ospath('newdir')
  newfile_path = sbox.ospath('newdir/newfile')
  os.makedirs(newdir_path)
  svntest.main.file_append(newfile_path, 'new text')

  # Schedule newdir and newfile for addition (note that the add is recursive)
  svntest.main.run_svn(None, 'add', newdir_path)

  # Created expected output tree for commit
  expected_output = svntest.wc.State(wc_dir, {
    'newdir' : Item(verb='Adding'),
    'newdir/newfile' : Item(verb='Adding'),
    })

  # Create expected status tree; all local revisions should be at 1,
  # but newdir and newfile should be at revision 2.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.add({
    'newdir' : Item(status='  ', wc_rev=2),
    'newdir/newfile' : Item(status='  ', wc_rev=2),
    })

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

  # Now we go to the backup working copy, still at revision 1.
  # We will run 'svn st -u', and make sure that newdir/newfile is reported
  # as a nonexistent (but pending) path.
  expected_status = svntest.actions.get_virginal_state(wc_backup, 1)
  expected_status.add({
    'newdir'         : Item(status='  '),
    'newdir/newfile' : Item(status='  '),
    })
  svntest.actions.run_and_verify_unquiet_status(wc_backup,
                                                expected_status)

  # At one time an obstructing 'newdir' caused a SEGV on 'newdir/newfile'
  os.makedirs(os.path.join(wc_backup, 'newdir'))
  expected_status.tweak('newdir', status='? ')
  svntest.actions.run_and_verify_unquiet_status(wc_backup,
                                                expected_status)

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

# svn status -vN should include all entries in a directory
def status_shows_all_in_current_dir(sbox):
  "status -vN shows all items in current directory"

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

  os.chdir(wc_dir)
  exit_code, output, err = svntest.actions.run_and_verify_svn(None, [],
                                                              'stat', '-vN')

  if (len(output) != len(os.listdir("."))):
    raise svntest.Failure


#----------------------------------------------------------------------
@Issue(2127)
def status_missing_file(sbox):
  "status with a versioned file missing"

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

  os.chdir(wc_dir)

  os.remove('iota')

  exit_code, output, err = svntest.actions.run_and_verify_svn(None, [],
                                                              'status')
  for line in output:
    if not re.match("! +iota", line):
      raise svntest.Failure

  # This invocation is for issue #2127.
  exit_code, output, err = svntest.actions.run_and_verify_svn(None, [],
                                                              'status', '-u',
                                                              'iota')
  found_it = 0
  for line in output:
    if re.match("! +1 +iota", line):
      found_it = 1
  if not found_it:
    raise svntest.Failure

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

def status_type_change(sbox):
  "status on versioned items whose type has changed"

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

  os.chdir(wc_dir)

  # First replace a versioned dir with a file and a versioned file
  # with a versioned dir.
  os.rename('iota', 'was_iota')
  os.rename('A', 'iota')
  os.rename('was_iota', 'A')

  expected_output = [
        '~       A\n',
        '!       A/mu\n',
        '!       A/B\n',
        '!       A/B/lambda\n',
        '!       A/B/E\n',
        '!       A/B/E/alpha\n',
        '!       A/B/E/beta\n',
        '!       A/B/F\n',
        '!       A/C\n',
        '!       A/D\n',
        '!       A/D/gamma\n',
        '!       A/D/G\n',
        '!       A/D/G/rho\n',
        '!       A/D/G/pi\n',
        '!       A/D/G/tau\n',
        '!       A/D/H\n',
        '!       A/D/H/chi\n',
        '!       A/D/H/omega\n',
        '!       A/D/H/psi\n',
        '~       iota\n',
  ]

  expected_output = [s.replace('/', os.path.sep) for s in expected_output]

  svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output),
                                     [], 'status')

  # Now change the file that is obstructing the versioned dir into an
  # unversioned dir.
  os.remove('A')
  os.mkdir('A')

  # A is a directory again, so it is no longer missing, but it's
  # descendants are
  expected_output = [
        '!       A/mu\n',
        '!       A/B\n',
        '!       A/B/lambda\n',
        '!       A/B/E\n',
        '!       A/B/E/alpha\n',
        '!       A/B/E/beta\n',
        '!       A/B/F\n',
        '!       A/C\n',
        '!       A/D\n',
        '!       A/D/gamma\n',
        '!       A/D/G\n',
        '!       A/D/G/rho\n',
        '!       A/D/G/pi\n',
        '!       A/D/G/tau\n',
        '!       A/D/H\n',
        '!       A/D/H/chi\n',
        '!       A/D/H/omega\n',
        '!       A/D/H/psi\n',
        '~       iota\n',
  ]
  # Fix separator for Windows
  expected_output = [s.replace('/', os.path.sep) for s in expected_output]

  svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output),
                                     [], 'status')

  # Now change the versioned dir that is obstructing the file into an
  # unversioned dir.
  svntest.main.safe_rmtree('iota')
  os.mkdir('iota')

  svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output),
                                     [], 'status')

#----------------------------------------------------------------------
@SkipUnless(svntest.main.is_posix_os)
def status_type_change_to_symlink(sbox):
  "status on versioned items replaced by symlinks"

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

  os.chdir(wc_dir)

  # "broken" symlinks
  os.remove('iota')
  os.symlink('foo', 'iota')
  svntest.main.safe_rmtree('A/D')
  os.symlink('bar', 'A/D')

  expected_output = [
        '~       A/D\n',
        '!       A/D/gamma\n',
        '!       A/D/G\n',
        '!       A/D/G/rho\n',
        '!       A/D/G/pi\n',
        '!       A/D/G/tau\n',
        '!       A/D/H\n',
        '!       A/D/H/chi\n',
        '!       A/D/H/omega\n',
        '!       A/D/H/psi\n',
        '~       iota\n',
  ]

  svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output),
                                     [], 'status')

  # "valid" symlinks
  os.remove('iota')
  os.remove('A/D')
  os.symlink('A/mu', 'iota')
  os.symlink('C', 'A/D')

  svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output),
                                     [], 'status')

#----------------------------------------------------------------------
# Regression test for revision 3686.

def status_with_new_files_pending(sbox):
  "status -u with new files in the repository"

  sbox.build()
  wc_dir = sbox.wc_dir

  os.chdir(wc_dir)

  svntest.main.file_append('newfile', 'this is a new file')
  svntest.main.run_svn(None, 'add', 'newfile')
  svntest.main.run_svn(None,
                       'ci', '-m', 'logmsg')
  svntest.main.run_svn(None,
                       'up', '-r', '1')

  exit_code, output, err = svntest.actions.run_and_verify_svn(None, [],
                                                              'status', '-u')

  # The bug fixed in revision 3686 was a segmentation fault.
  # TODO: Check exit code.
  # In the meantime, no output means there was a problem.
  for line in output:
    if line.find('newfile') != -1:
      break
  else:
    raise svntest.Failure

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

def status_for_unignored_file(sbox):
  "status for unignored file and directory"

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

  os.chdir(wc_dir)

  # use a temp file to set properties with wildcards in their values
  # otherwise Win32/VS2005 will expand them
  svntest.main.file_append('proptmp', 'new*')
  svntest.main.file_append('newfile', 'this is a new file')
  os.makedirs('newdir')
  svntest.main.run_svn(None, 'propset', 'svn:ignore', '-F', 'proptmp', '.')
  os.remove('proptmp')

  # status on the directory with --no-ignore
  expected = svntest.verify.UnorderedOutput(
        ['I       newdir\n',
         'I       newfile\n',
         ' M      .\n'])
  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     'status', '--no-ignore', '.')

  # status specifying the file explicitly on the command line
  expected = svntest.verify.UnorderedOutput(
        ['I       newdir\n',
         'I       newfile\n'])
  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     'status', 'newdir', 'newfile')

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

def status_for_nonexistent_file(sbox):
  "status on missing and unversioned file"

  sbox.build(read_only = True)

  wc_dir = sbox.wc_dir

  os.chdir(wc_dir)

  exit_code, output, err = svntest.actions.run_and_verify_svn(
    None, [], 'status', 'nonexistent-file')

  # there should *not* be a status line printed for the nonexistent file
  for line in output:
    if re.match(" +nonexistent-file", line):
      raise svntest.Failure

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

def status_nonrecursive_update_different_cwd(sbox):
  "status -v -N -u from different current directories"

  # check combination of status -u and -N
  # create A/C/J in repository
  # create A/C/K in working copy
  # check status output with -u and -N on target C
  # check status output with -u and -N on target . (in C)

  sbox.build()
  wc_dir = sbox.wc_dir

  J_url  = sbox.repo_url + '/A/C/J'
  K_path = os.path.join(wc_dir, 'A', 'C', 'K' )

  svntest.actions.run_and_verify_svn(None, [],
                                     'mkdir', '-m', 'rev 2', J_url)

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

  os.chdir(wc_dir)

  expected_output = [
    '        *                                %s\n' % os.path.join("C", "J"),
    'A                -       ?   ?           %s\n' % os.path.join("C", "K"),
    '        *        1        1 jrandom      C\n',
    'Status against revision:      2\n' ]

  os.chdir('A')
  svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output),
                                     [],
                                     'status', '-v', '-N', '-u', 'C')

  expected_output = [
    '        *                                J\n',
    'A                -       ?   ?           K\n',
    '        *        1        1 jrandom      .\n',
    'Status against revision:      2\n']

  os.chdir('C')
  svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output),
                                     [],
                                     'status', '-v', '-N', '-u', '.')


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

def status_file_needs_update(sbox):
  "status -u indicates out-of-dateness"

  # See this thread:
  #
  #    http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=27975
  #
  # Basically, Andreas was seeing inconsistent results depending on
  # whether or not he accompanied 'svn status -u' with '-v':
  #
  #    % svn st -u
  #    Head revision:     67
  #    %
  #
  # ...and yet...
  #
  #    % svn st -u -v
  #                   56        6          k   cron-daily.pl
  #           *       56       44          k   crontab.root
  #                   56        6          k   gmls-lR.pl
  #    Head revision:     67
  #    %
  #
  # The first status should show the asterisk, too.  There was never
  # any issue for this bug, so this comment and the thread are your
  # audit trail :-).

  sbox.build()
  wc_dir = sbox.wc_dir

  other_wc = sbox.add_wc_path('other')

  svntest.actions.duplicate_dir(wc_dir, other_wc)

  was_cwd = os.getcwd()

  os.chdir(wc_dir)
  svntest.main.file_append('crontab.root', 'New file crontab.root.\n')
  svntest.main.run_svn(None, 'add', 'crontab.root')
  svntest.main.run_svn(None,
                       'ci', '-m', 'log msg')
  os.chdir(was_cwd)
  os.chdir(other_wc)
  svntest.main.run_svn(None,
                       'up')

  os.chdir(was_cwd)
  os.chdir(wc_dir)
  svntest.main.file_append('crontab.root', 'New line in crontab.root.\n')
  svntest.main.run_svn(None,
                       'ci', '-m', 'log msg')

  # The `svntest.actions.run_and_verify_*_status' routines all pass
  # the -v flag, which we don't want, as this bug never appeared when
  # -v was passed.  So we run status by hand:
  os.chdir(was_cwd)
  exit_code, out, err = svntest.actions.run_and_verify_svn(None, [],
                                                           'status', '-u',
                                                           other_wc)

  for line in out:
    if re.match("\\s+\\*.*crontab\\.root$", line):
      break
  else:
    raise svntest.Failure

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

def status_uninvited_parent_directory(sbox):
  "status -u on outdated, added file shows only that"

  # To reproduce, check out working copies wc1 and wc2, then do:
  #
  #   $ cd wc1
  #   $ echo "new file" >> newfile
  #   $ svn add newfile
  #   $ svn ci -m 'log msg'
  #
  #   $ cd ../wc2
  #   $ echo "new file" >> newfile
  #   $ svn add newfile
  #
  #   $ cd ..
  #   $ svn st wc2/newfile
  #
  # You *should* get one line of status output, for newfile.  The bug
  # is that you get two instead, one for newfile, and one for its
  # parent directory, wc2/.
  #
  # This bug was originally discovered during investigations into
  # issue #1042, "fixed" in revision 4181, then later the fix was
  # reverted because it caused other status problems (see the test
  # status_file_needs_update(), which fails when 4181 is present).

  sbox.build()
  wc_dir = sbox.wc_dir

  other_wc = sbox.add_wc_path('other')

  svntest.actions.duplicate_dir(wc_dir, other_wc)

  was_cwd = os.getcwd()

  os.chdir(wc_dir)
  svntest.main.file_append('newfile', 'New file.\n')
  svntest.main.run_svn(None, 'add', 'newfile')
  svntest.main.run_svn(None,
                       'ci', '-m', 'log msg')

  os.chdir(was_cwd)
  os.chdir(other_wc)
  svntest.main.file_append('newfile', 'New file.\n')
  svntest.main.run_svn(None, 'add', 'newfile')

  os.chdir(was_cwd)

  # We don't want a full status tree here, just one line (or two, if
  # the bug is present).  So run status by hand:
  os.chdir(was_cwd)
  exit_code, out, err = svntest.actions.run_and_verify_svn(
    None, [],
    'status', '-u', os.path.join(other_wc, 'newfile'))

  for line in out:
    # The "/?" is just to allow for an optional trailing slash.
    if re.match("\\s+\\*.*\.other/?$", line):
      raise svntest.Failure

@Issue(1289)
def status_on_forward_deletion(sbox):
  "status -u on working copy deleted in HEAD"
  # See issue #1289.
  sbox.build(create_wc = False)
  wc_dir = sbox.wc_dir

  top_url = sbox.repo_url
  A_url = top_url + '/A'

  svntest.main.run_svn(None,
                       'rm', '-m', 'Remove A.', A_url)

  svntest.main.safe_rmtree(wc_dir)
  os.mkdir(wc_dir)

  os.chdir(wc_dir)

  svntest.main.run_svn(None,
                       'co', '-r1', top_url + "@1", 'wc')
  # If the bug is present, this will error with
  #
  #    subversion/libsvn_wc/lock.c:513: (apr_err=155005)
  #    svn: Working copy not locked
  #    svn: directory '' not locked
  #
  svntest.actions.run_and_verify_svn(None, [], 'st', '-u', 'wc')

  # Try again another way; the error would look like this:
  #
  #    subversion/libsvn_repos/delta.c:207: (apr_err=160005)
  #    svn: Invalid filesystem path syntax
  #    svn: svn_repos_dir_delta: invalid editor anchoring; at least \
  #       one of the input paths is not a directory and there was   \
  #       no source entry.
  #
  # (Dang!  Hope a user never has to see that :-) ).
  #
  svntest.main.safe_rmtree('wc')
  svntest.main.run_svn(None,
                       'co', '-r1', A_url + "@1", 'wc')
  svntest.actions.run_and_verify_svn(None, [], 'st', '-u', 'wc')

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

def get_last_changed_date(path):
  "get the Last Changed Date for path using svn info"
  exit_code, out, err = svntest.actions.run_and_verify_svn(None, [],
                                                           'info', path)
  for line in out:
    if re.match("^Last Changed Date", line):
      return line
  logger.warn("Didn't find Last Changed Date for %s", path)
  raise svntest.Failure

# Helper for timestamp_behaviour test
def get_text_timestamp(path):
  "get the text-time for path using svn info"
  exit_code, out, err = svntest.actions.run_and_verify_svn(None, [],
                                                           'info', path)
  for line in out:
    if re.match("^Text Last Updated", line):
      return line
  logger.warn("Didn't find text-time for %s", path)
  raise svntest.Failure("didn't find text-time")

def no_text_timestamp(path):
  "ensure no text-time for path using svn info"
  exit_code, out, err = svntest.actions.run_and_verify_svn(None, [],
                                                           'info', path)
  for line in out:
    if re.match("^Text Last Updated", line):
      logger.warn("Found text-time for %s", path)
      raise svntest.Failure("found text-time")

# Helper for timestamp_behaviour test
def text_time_behaviour(wc_dir, wc_path, status_path, expected_status, cmd):
  "text-time behaviour"

  # Pristine text and text-time
  fp = open(wc_path, 'rb')
  pre_text = fp.readlines()
  pre_text_time = get_text_timestamp(wc_path)

  # Modifying the text does not affect text-time
  svntest.main.file_append(wc_path, "some mod")
  expected_status.tweak(status_path, status='M ')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)
  text_time = get_text_timestamp(wc_path)
  if text_time != pre_text_time:
    raise svntest.Failure

  # Manually reverting the text does not affect the text-time
  fp = open(wc_path, 'wb')
  fp.writelines(pre_text)
  fp.close()
  expected_status.tweak(status_path, status='  ')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)
  text_time = get_text_timestamp(wc_path)
  if text_time != pre_text_time:
    raise svntest.Failure

  # revert/cleanup change the text-time even though the text doesn't change
  if cmd == 'cleanup':
    svntest.actions.run_and_verify_svn(None, [], cmd, wc_dir)
  else:
    svntest.actions.run_and_verify_svn(None, [], cmd, wc_path)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)
  text_time = get_text_timestamp(wc_path)
  if text_time == pre_text_time:
    raise svntest.Failure

# Is this really a status test?  I'm not sure, but I don't know where
# else to put it.
@Issue(3773)
def timestamp_behaviour(sbox):
  "timestamp behaviour"

  sbox.build()
  wc_dir = sbox.wc_dir

  A_path = sbox.ospath('A')
  iota_path = sbox.ospath('iota')

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Sleep to ensure timestamps change
  time.sleep(1.1)

  # Check behaviour of revert on text-time
  text_time_behaviour(wc_dir, iota_path, 'iota', expected_status, 'revert')

  # Sleep to ensure timestamps change
  time.sleep(1.1)

  # Check behaviour of cleanup on text-time
  text_time_behaviour(wc_dir, iota_path, 'iota', expected_status, 'cleanup')

  # Create a config to enable use-commit-times
  config_contents = '''\
[auth]
password-stores =

[miscellany]
use-commit-times = yes
'''
  config_dir = sbox.create_config_dir(config_contents)

  other_wc = sbox.add_wc_path('other')
  svntest.actions.run_and_verify_svn(None, [],
                                     'co', sbox.repo_url,
                                     other_wc,
                                     '--config-dir', config_dir)

  other_iota_path = os.path.join(other_wc, 'iota')
  iota_text_timestamp = get_text_timestamp(other_iota_path)
  iota_last_changed = get_last_changed_date(other_iota_path)
  if (iota_text_timestamp[17] != ':' or
      iota_text_timestamp[17:] != iota_last_changed[17:]):
    raise svntest.Failure

  # remove iota, run an update to restore it, and check the times
  os.remove(other_iota_path)
  expected_output = svntest.wc.State(other_wc, {
    'iota': Item(verb='Restored'),
    })
  expected_disk = svntest.main.greek_state.copy()
  expected_status = svntest.actions.get_virginal_state(other_wc, 1)
  svntest.actions.run_and_verify_update(other_wc, expected_output,
                                        expected_disk, expected_status,
                                        [], False,
                                        other_wc, '--config-dir', config_dir)
  iota_text_timestamp = get_text_timestamp(other_iota_path)
  if (iota_text_timestamp[17] != ':' or
      iota_text_timestamp[17:] != iota_last_changed[17:]):
    raise svntest.Failure

  iota_ts = iota_text_timestamp[19:44]

  class TZ(datetime.tzinfo):
    "A tzinfo to convert a time to iota's timezone."
    def utcoffset(self, dt):
      offset = (int(iota_ts[21:23]) * 60 + int(iota_ts[23:25]))
      if iota_ts[20] == '-':
        return datetime.timedelta(minutes=-offset)
      return datetime.timedelta(minutes=offset)
    def dst(self, dt):
      return datetime.timedelta(0)

  # get the timestamp on the file. whack any microseconds value, as svn
  # doesn't record to that precision. we also use the TZ class to shift
  # the timestamp into the same timezone as the expected timestamp.
  mtime = datetime.datetime.fromtimestamp(os.path.getmtime(other_iota_path),
                                          TZ()).replace(microsecond=0)
  fmt = mtime.isoformat(' ')

  # iota_ts looks like: 2009-04-13 14:30:57 +0200
  #     fmt looks like: 2009-04-13 14:30:57+02:00
  if (fmt[:19] != iota_ts[:19]
      or fmt[19:22] != iota_ts[20:23]
      or fmt[23:25] != iota_ts[23:25]):
    # NOTE: the two strings below won't *exactly* match (see just above),
    #   but the *numeric* portions of them should.
    logger.warn("File timestamp on 'iota' does not match.")
    logger.warn("  EXPECTED: %s", iota_ts)
    logger.warn("    ACTUAL: %s", fmt)
    raise svntest.Failure

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

@Issues(1617,2030)
def status_on_unversioned_dotdot(sbox):
  "status on '..' where '..' is not versioned"
  # See issue #1617 (and #2030).
  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  new_dir = sbox.ospath('new')
  new_sub = sbox.ospath('new/sub')
  new_subsub = sbox.ospath('new/sub/sub')
  os.mkdir(new_dir)
  os.mkdir(new_sub)
  os.mkdir(new_subsub)

  os.chdir(new_subsub)
  svntest.actions.run_and_verify_svn2(None,
                                     "svn: warning: W155(010|007):.*'.*'.*not",
                                      0, 'st', '..')

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

@Issue(2122)
def status_on_partially_nonrecursive_wc(sbox):
  "status -u in partially non-recursive wc"
  # Based on issue #2122.
  #
  #    $ svn co -N -r 213 svn://svn.debian.org/pkg-kde .
  #    A  README
  #    Checked out revision 213.
  #
  #    $ svn up -r 213 scripts www
  #    [ List of scripts/* files.]
  #    Updated to revision 213.
  #    [ List of www/* files.]
  #    Updated to revision 213.
  #
  #    $ svn st -u
  #       *      213   www/IGNORE-ME
  #       *      213   www
  #    svn: subversion/libsvn_wc/status.c:910: tweak_statushash:         \
  #         Assertion `repos_text_status == svn_wc_status_added' failed. \
  #         Aborted (core dumped)
  #
  # You might think that the intermediate "svn up -r 213 scripts www"
  # step is unnecessary, but when I tried eliminating it, I got
  #
  #    $ svn st -u
  #    subversion/libsvn_wc/lock.c:642: (apr_err=155005)
  #    svn: Working copy 'www' not locked
  #    $
  #
  # instead of the assertion error.

  sbox.build()
  wc_dir = sbox.wc_dir

  top_url = sbox.repo_url
  A_url = top_url + '/A'
  D_url = top_url + '/A/D'
  G_url = top_url + '/A/D/G'
  H_url = top_url + '/A/D/H'
  rho = sbox.ospath('A/D/G/rho')

  # Commit a change to A/D/G/rho.  This will be our equivalent of
  # whatever change it was that happened between r213 and HEAD in the
  # reproduction recipe.  For us, it's r2.
  svntest.main.file_append(rho, 'Whan that Aprille with his shoores soote\n')
  svntest.main.run_svn(None,
                       'ci', '-m', 'log msg', rho)

  # Make the working copy weird in the right way, then try status -u.
  D_wc = sbox.add_wc_path('D')
  svntest.main.run_svn(None,
                       'co', '-r1', '-N', D_url, D_wc)

  os.chdir(D_wc)
  svntest.main.run_svn(None,
                       'up', '-r1', 'H')
  svntest.main.run_svn(None,
                       'st', '-u')


def missing_dir_in_anchor(sbox):
  "a missing dir in the anchor"

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

  foo_path = sbox.ospath('foo')
  svntest.actions.run_and_verify_svn(None, [], 'mkdir', foo_path)
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.add({
    'foo' : Item(status='A ', wc_rev=0),
    })
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # At one point this caused a "foo not locked" error
  svntest.main.safe_rmtree(foo_path)
  expected_status.tweak('foo', status='! ', entry_status='A ',
                               wc_rev='-',  entry_rev='0')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)


def status_in_xml(sbox):
  "status output in XML format"

  sbox.build()
  wc_dir = sbox.wc_dir

  file_name = "iota"
  file_path = os.path.join(wc_dir, file_name)
  svntest.main.file_append(file_path, "test status --xml\n")

  # Retrieve last changed date from svn log
  exit_code, output, error = svntest.actions.run_and_verify_svn(
    None, [], 'log', file_path, '--xml', '-rHEAD')

  info_msg = "<date>"
  for line in output:
    if line.find(info_msg) >= 0:
      time_str = line[:len(line)]
      break
  else:
    raise svntest.Failure

  expected_entries = {file_path : {'wcprops' : 'none',
                                   'wcitem' : 'modified',
                                   'wcrev' : '1',
                                   'crev' : '1',
                                   'author' : svntest.main.wc_author}}

  svntest.actions.run_and_verify_status_xml(expected_entries, file_path, '-u')

  svntest.actions.run_and_verify_svn(None, [],
                                     'cp', '-m', 'repo-to-repo copy',
                                     sbox.repo_url + '/iota',
                                     sbox.repo_url + '/iota2')

  file_path = sbox.ospath('iota2')

  expected_entries = {file_path : {'wcprops' : 'none',
                                   'wcitem' : 'none',
                                   'rprops' : 'none',
                                   'ritem' : 'added'}}

  svntest.actions.run_and_verify_status_xml(expected_entries, file_path, '-u')

  svntest.actions.run_and_verify_svn(None, [],
                                     'rm', '-m', 'repo delete',
                                     sbox.repo_url + '/A/B/E/alpha')

  expected_entries = {sbox.ospath('A/B/E/alpha')
                      : {'wcprops' : 'none',
                         'wcitem' : 'normal',
                         'wcrev' : '1',
                         'crev' : '1',
                         'author' : svntest.main.wc_author,
                         'rprops' : 'none',
                         'ritem' : 'deleted'}}

  svntest.actions.run_and_verify_status_xml(expected_entries,
                                            sbox.ospath('A/B/E/alpha'), '-u')

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

def status_ignored_dir(sbox):
  "status on ignored directory"
  sbox.build()
  wc_dir = sbox.wc_dir
  new_dir = sbox.ospath('dir.o')
  new_dir_url = sbox.repo_url + "/dir.o"

  svntest.actions.run_and_verify_svn(["Committing transaction...\n",
                                      "Committed revision 2.\n"], [],
                                     'mkdir', new_dir_url, '-m', 'msg')

  # Make a dir that is ignored by the default ignore patterns.
  os.mkdir(new_dir)

  # run_and_verify_status doesn't handle this weird kind of entry.
  svntest.actions.run_and_verify_svn(['I       *            ' + new_dir + "\n",
                                      '        *        1   ' + wc_dir + "\n",
                                      'Status against revision:      2\n'], [],
                                     "status", "-u", wc_dir)

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

def status_unversioned_dir_in_wc(sbox):
  "status on unversioned dir in working copy"
  sbox.build(read_only = True)

  # Create two unversioned directories within the test working copy
  path = sbox.ospath('1/2')
  os.makedirs(path)

  expected_err = "svn: warning: (W155007|W155010): .*'.*(/|\\\\)" + \
                 os.path.basename(path) + \
                 "' was not found"
  svntest.actions.run_and_verify_svn2([], expected_err, 0,
                                      "status", path)

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

def status_missing_dir(sbox):
  "status with a versioned directory missing"
  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir
  a_d_g = sbox.ospath('A/D/G')

  # ok, blow away the A/D/G directory
  svntest.main.safe_rmtree(a_d_g)

  expected = [
                 '!       A/D/G\n',
                 '!       A/D/G/rho\n',
                 '!       A/D/G/pi\n',
                 '!       A/D/G/tau\n',
  ]
  expected = [ s.replace('A/D/G', a_d_g).replace('/', os.path.sep)
               for s in expected ]

  svntest.actions.run_and_verify_svn(UnorderedOutput(expected), [],
                                     "status", wc_dir)

  expected = [
          "!                1   " + a_d_g + "\n",
          "!                1   " + os.path.join(a_d_g, "rho") + "\n",
          "!                1   " + os.path.join(a_d_g, "pi") + "\n",
          "!                1   " + os.path.join(a_d_g, "tau") + "\n",
          "Status against revision:      1\n"
  ]

  # now run status -u, we should be able to do this without crashing
  svntest.actions.run_and_verify_svn(UnorderedOutput(expected), [],
                                     "status", "-u", wc_dir)

  # Finally run an explicit status request directly on the missing directory.
  expected = [
                  "!       A/D/G\n",
                  "!       A/D/G/rho\n",
                  "!       A/D/G/pi\n",
                  "!       A/D/G/tau\n",
               ]
  expected = [ s.replace('A/D/G', a_d_g).replace('/', os.path.sep)
               for s in expected ]

  svntest.actions.run_and_verify_svn(UnorderedOutput(expected), [],
                                     "status", a_d_g)

def status_add_plus_conflict(sbox):
  "status on conflicted added file"
  sbox.build()
  svntest.actions.do_sleep_for_timestamps()

  wc_dir = sbox.wc_dir

  branch_url  = sbox.repo_url + '/branch'
  trunk_url  = sbox.repo_url + '/trunk'

  svntest.actions.run_and_verify_svn(None, [],
                                     'mkdir', '-m', 'rev 2',
                                     branch_url, trunk_url)

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

  branch_file = sbox.ospath('branch/file')

  svntest.main.file_write(branch_file, "line 1\nline2\nline3\n", 'wb+')

  svntest.actions.run_and_verify_svn(None, [], 'add', branch_file)

  svntest.actions.run_and_verify_svn(None, [],
                                     'commit',
                                     branch_file, '-m', 'rev 3')

  svntest.main.file_write(branch_file, "line 1\nline3\n", 'wb')

  svntest.actions.run_and_verify_svn(None, [],
                                     'commit',
                                     branch_file, '-m', 'rev 4')

  svntest.main.file_write(branch_file, "line 1\nline2\n", 'wb')

  svntest.actions.run_and_verify_svn(None, [],
                                     'commit',
                                     branch_file, '-m', 'rev 5')

  trunk_dir = sbox.ospath('trunk')

  svntest.actions.run_and_verify_svn(None, [],
                                     'merge',
                                     branch_url, '-r', '2:3', trunk_dir)

  svntest.actions.run_and_verify_svn(None, [],
                                     'merge',
                                     branch_url, '-r', '4:5', trunk_dir)

  lines = [
    "?       " + sbox.ospath('trunk/file.merge-left.r4') + "\n",
    "?       " + sbox.ospath('trunk/file.merge-right.r5') + "\n",
    "?       " + sbox.ospath('trunk/file.working') + "\n",
    "C  +    " + sbox.ospath('trunk/file') + "\n",
  ] + svntest.main.summary_of_conflicts(text_conflicts=1)
  if svntest.main.server_has_mergeinfo():
    lines.append(" M      " + sbox.ospath('trunk') + "\n")

  expected_output = svntest.verify.UnorderedOutput(lines)

  svntest.actions.run_and_verify_svn(expected_output, [],
                                     'status', wc_dir)

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

def inconsistent_eol(sbox):
  "status with inconsistent eol style"

  sbox.build()
  wc_dir = sbox.wc_dir
  iota_path = sbox.ospath('iota')

  svntest.main.file_write(iota_path, "line 1\nline 2\n", "wb")

  svntest.actions.run_and_verify_svn("property 'svn:eol-style' set on.*iota",
                                     [],
                                     'propset', 'svn:eol-style', 'native',
                                     sbox.ospath('iota'))

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

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

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

  # Make the eol style inconsistent and verify that status says nothing.
  svntest.main.file_write(iota_path, "line 1\nline 2\r\n", "wb")
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

#----------------------------------------------------------------------
# Test for issue #2533
@Issue(2533)
def status_update_with_incoming_props(sbox):
  "run 'status -u' variations w/ incoming propchanges"

  sbox.build()
  wc_dir = sbox.wc_dir
  A_path = sbox.ospath('A')

  # Add a property to the root folder and a subdir
  svntest.main.run_svn(None, 'propset', 'red', 'rojo', wc_dir)
  svntest.main.run_svn(None, 'propset', 'black', 'bobo', A_path)

  # Create expected output tree.
  expected_output = svntest.wc.State(wc_dir, {
    ''  : Item(verb='Sending'),
    'A' : Item(verb='Sending'),
    })

  # Created expected status tree.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('', wc_rev=2, status='  ')
  expected_status.tweak('A', wc_rev=2, status='  ')

  # Commit the working copy
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        expected_status)

  # Create expected trees for an update to revision 1.
  expected_output = svntest.wc.State(wc_dir, {
    ''  : Item(status=' U'),
    'A' : Item(status=' U'),
    })
  expected_disk = svntest.main.greek_state.copy()
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)

  # Do the update and check the results in three ways... INCLUDING PROPS
  svntest.actions.run_and_verify_update(wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        [], True,
                                        '-r', '1', wc_dir)

  # Can't use run_and_verify_status here because the out-of-date
  # information in the status output isn't copied in the status tree.
  expected = svntest.verify.UnorderedOutput(
         ["        *        1   " + A_path + "\n",
          "        *        1   " + wc_dir + "\n",
          "Status against revision:      2\n" ])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u",
                                     wc_dir)

  expected = svntest.verify.UnorderedOutput(
         ["                 1        1 jrandom      " +
          sbox.ospath('iota') + "\n",
          "        *        1        1 jrandom      " + A_path + "\n",
          "        *        1        1 jrandom      " + wc_dir + "\n",
          "Status against revision:      2\n" ])

  svntest.actions.run_and_verify_svn(expected, [],
                                     "status", "-uvN",
                                     wc_dir)

  # Retrieve last changed date from svn log
  exit_code, output, error = svntest.actions.run_and_verify_svn(None, [],
                                                                'log', wc_dir,
                                                                '--xml', '-r1')

  info_msg = "<date>"
  for line in output:
    if line.find(info_msg) >= 0:
      time_str = line[:len(line)]
      break
  else:
    raise svntest.Failure

  expected_entries ={wc_dir : {'wcprops' : 'none',
                               'wcitem' : 'normal',
                               'wcrev' : '1',
                               'crev' : '1',
                               'author' : svntest.main.wc_author,
                               'rprops' : 'modified',
                               'ritem' : 'none'},
                     A_path : {'wcprops' : 'none',
                               'wcitem' : 'normal',
                               'wcrev' : '1',
                               'crev' : '1',
                               'author' : svntest.main.wc_author,
                               'rprops' : 'modified',
                               'ritem' : 'none'},
                     }

  svntest.actions.run_and_verify_status_xml(expected_entries, wc_dir, '-uN')

# more incoming prop updates.
def status_update_verbose_with_incoming_props(sbox):
  "run 'status -uv' w/ incoming propchanges"

  sbox.build()
  wc_dir = sbox.wc_dir
  A_path = sbox.ospath('A')
  D_path = os.path.join(A_path, 'D')
  B_path = os.path.join(A_path, 'B')
  E_path = os.path.join(A_path, 'B', 'E')
  G_path = os.path.join(A_path, 'D', 'G')
  H_path = os.path.join(A_path, 'D', 'H')
  # Add a property to the root folder and a subdir
  svntest.main.run_svn(None, 'propset', 'red', 'rojo', D_path)
  svntest.main.run_svn(None, 'propset', 'black', 'bobo', E_path)
  svntest.main.run_svn(None, 'propset', 'black', 'bobo', wc_dir)

  # Create expected output tree.
  expected_output = svntest.wc.State(wc_dir, {
    'A/D'   : Item(verb='Sending'),
    'A/B/E' : Item(verb='Sending'),
    ''      : Item(verb='Sending'),
    })
  # Created expected status tree.
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/D', wc_rev=2, status='  ')
  expected_status.tweak('A/B/E', wc_rev=2, status='  ')
  expected_status.tweak('', wc_rev=2, status='  ')

  # Commit the working copy
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        expected_status)

  # Create expected trees for an update to revision 1.
  expected_output = svntest.wc.State(wc_dir, {
    'A/D'   : Item(status=' U'),
    'A/B/E' : Item(status=' U'),
    ''      : Item(status=' U'),
    })
  expected_disk = svntest.main.greek_state.copy()
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)

  # Do the update and check the results in three ways... INCLUDING PROPS
  svntest.actions.run_and_verify_update(wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        [], True,
                                        '-r', '1', wc_dir)

  # Can't use run_and_verify_status here because the out-of-date
  # information in the status output isn't copied in the status tree.
  common = "        1        1 jrandom      "
  expected = svntest.verify.UnorderedOutput(
         ["         " + common + os.path.join(E_path, 'alpha') + "\n",
          "         " + common + os.path.join(E_path, 'beta') + "\n",
          "        *" + common + os.path.join(E_path) + "\n",
          "         " + common + os.path.join(B_path, 'lambda') + "\n",
          "         " + common + os.path.join(B_path, 'F') + "\n",
          "         " + common + B_path + "\n",
          "         " + common + os.path.join(G_path, 'pi') + "\n",
          "         " + common + os.path.join(G_path, 'rho') + "\n",
          "         " + common + os.path.join(G_path, 'tau') + "\n",
          "         " + common + G_path + "\n",
          "         " + common + os.path.join(H_path, 'chi') + "\n",
          "         " + common + os.path.join(H_path, 'omega') + "\n",
          "         " + common + os.path.join(H_path, 'psi') + "\n",
          "         " + common + H_path + "\n",
          "         " + common + os.path.join(D_path, 'gamma') + "\n",
          "        *" + common + D_path + "\n",
          "         " + common + os.path.join(A_path, 'mu') + "\n",
          "         " + common + os.path.join(A_path, 'C') + "\n",
          "         " + common + A_path + "\n",
          "         " + common + sbox.ospath('iota') + "\n",
          "        *" + common + wc_dir  + "\n",
          "Status against revision:      2\n" ])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-uv", wc_dir)

#----------------------------------------------------------------------
# Test for issue #2468
@Issue(2468)
def status_nonrecursive_update(sbox):
  "run 'status -uN' with incoming changes"

  sbox.build()
  wc_dir = sbox.wc_dir
  A_path = sbox.ospath('A')
  D_path = os.path.join(A_path, 'D')
  mu_path = os.path.join(A_path, 'mu')
  gamma_path = os.path.join(D_path, 'gamma')

  # Change files in A and D and commit
  svntest.main.file_append(mu_path, "new line of text")
  svntest.main.file_append(gamma_path, "new line of text")

  # Create expected trees for commit
  expected_output = svntest.wc.State(wc_dir, {
    'A/mu' : Item(verb='Sending'),
    'A/D/gamma' : Item(verb='Sending')
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/mu', wc_rev=2, status='  ')
  expected_status.tweak('A/D/gamma', wc_rev=2, status='  ')

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

  # Create expected trees for an update to revision 1.
  expected_output = svntest.wc.State(wc_dir, {
    'A/mu' : Item(status='U '),
    'A/D/gamma' : Item(status='U '),
    })
  expected_disk = svntest.main.greek_state.copy()
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)

  # Do the update and check the results in three ways
  svntest.actions.run_and_verify_update(wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        [], False,
                                        '-r', '1', wc_dir)

  # Check the remote status of folder A (non-recursively)
  xout = ["        *        1   " + sbox.ospath('A/mu') + "\n",
          "Status against revision:      2\n" ]

  svntest.actions.run_and_verify_svn(xout,
                                     [],
                                     "status", "-uN", A_path)

def change_files(wc_dir, files):
  """Make a basic change to the files.
     files = a list of paths relative to the wc root directory
  """

  for file in files:
    filepath = os.path.join(wc_dir, file)
    svntest.main.file_append(filepath, "new line of text")

def change_files_and_commit(wc_dir, files, baserev=1):
  """Make a basic change to the files and commit them.
     files = a list of paths relative to the wc root directory
  """

  change_files(wc_dir, files)

  # Prepare expected trees for commit
  expected_output = svntest.wc.State(wc_dir, {
    'A/mu' : Item(verb='Sending'),
    'A/D/gamma' : Item(verb='Sending')
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)

  commitrev = baserev + 1
  for file in files:
    expected_output.add({file : Item(verb='Sending')})
    expected_status.tweak(file, wc_rev=commitrev, status='  ')

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

def status_depth_local(sbox):
  "run 'status --depth=X' with local changes"

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir
  A_path = sbox.ospath('A')
  D_path = os.path.join(A_path, 'D')

  mu_path = os.path.join(A_path, 'mu')
  gamma_path = os.path.join(D_path, 'gamma')

  # make some changes to the greek tree
  change_files(wc_dir, ['A/mu', 'A/D/gamma'])
  svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', A_path)
  svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', D_path)

  # for all the possible types of depth, check the status

  # depth=empty
  expected = svntest.verify.UnorderedOutput(
                  [" M      %s\n" % A_path])
  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "--depth=empty", A_path)

  # depth=files
  expected = svntest.verify.UnorderedOutput(
                  [" M      %s\n" % A_path,
                   "M       %s\n" % mu_path])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "--depth=files", A_path)

  # depth=immediates
  expected = svntest.verify.UnorderedOutput(
                  [" M      %s\n" % A_path,
                   " M      %s\n" % D_path,
                   "M       %s\n" % mu_path])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "--depth=immediates", A_path)

  # depth=infinity (the default)
  expected = svntest.verify.UnorderedOutput(
                  [" M      %s\n" % A_path,
                   " M      %s\n" % D_path,
                   "M       %s\n" % mu_path,
                   "M       %s\n" % gamma_path])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "--depth=infinity", A_path)

def status_depth_update(sbox):
  "run 'status --depth=X -u' with incoming changes"

  sbox.build()
  wc_dir = sbox.wc_dir
  A_path = sbox.ospath('A')
  D_path = os.path.join(A_path, 'D')

  mu_path = os.path.join(A_path, 'mu')
  gamma_path = os.path.join(D_path, 'gamma')

  # add some files, change directory properties
  change_files_and_commit(wc_dir, ['A/mu', 'A/D/gamma'])
  svntest.main.run_svn(None, 'up', wc_dir)
  svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', A_path)
  svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', D_path)
  sbox.simple_commit(message='log message')

  # update to r1
  svntest.main.run_svn(None, 'up', '-r', '1', wc_dir)

  # for all the possible types of depth, check the status

  # depth=empty
  expected = svntest.verify.UnorderedOutput(
                  ["        *        1   %s\n" % A_path,
                   "Status against revision:      3\n"])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "--depth=empty", A_path)

  # depth=files
  expected = svntest.verify.UnorderedOutput(
                  ["        *        1   %s\n" % mu_path,
                   "        *        1   %s\n" % A_path,
                   "Status against revision:      3\n"])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "--depth=files",
                                     A_path)

  # depth=immediates
  expected = svntest.verify.UnorderedOutput(
                  ["        *        1   %s\n" % A_path,
                   "        *        1   %s\n" % D_path,
                   "        *        1   %s\n" % mu_path,
                   "Status against revision:      3\n"])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "--depth=immediates",
                                     A_path)

  # depth=infinity (the default)
  expected = svntest.verify.UnorderedOutput(
                  ["        *        1   %s\n" % A_path,
                   "        *        1   %s\n" % D_path,
                   "        *        1   %s\n" % mu_path,
                   "        *        1   %s\n" % gamma_path,
                   "Status against revision:      3\n"])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "--depth=infinity",
                                     A_path)


#----------------------------------------------------------------------
def status_depth_update_local_modifications(sbox):
  "run 'status --depth=X -u' with local changes"

  sbox.build()
  wc_dir = sbox.wc_dir
  A_path = sbox.ospath('A')
  D_path = os.path.join(A_path, 'D')

  mu_path = os.path.join(A_path, 'mu')
  gamma_path = os.path.join(D_path, 'gamma')

  svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', A_path)
  svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', D_path)

  svntest.main.file_append(mu_path, 'modified')
  svntest.main.file_append(gamma_path, 'modified')

  # depth=empty
  expected = svntest.verify.UnorderedOutput(
                  [" M               1   %s\n" % A_path,
                   "Status against revision:      1\n"])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "--depth=empty", A_path)

  expected = svntest.verify.UnorderedOutput(
                  ["M                1   %s\n" % mu_path,
                   "Status against revision:      1\n"])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "--depth=empty", mu_path)

  # depth=files
  expected = svntest.verify.UnorderedOutput(
                  ["M                1   %s\n" % mu_path,
                   " M               1   %s\n" % A_path,
                   "Status against revision:      1\n"])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "--depth=files",
                                     A_path)

  # depth=immediates
  expected = svntest.verify.UnorderedOutput(
                  [" M               1   %s\n" % A_path,
                   " M               1   %s\n" % D_path,
                   "M                1   %s\n" % mu_path,
                   "Status against revision:      1\n"])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "--depth=immediates",
                                     A_path)

  # depth=infinity (the default)
  expected = svntest.verify.UnorderedOutput(
                  [" M               1   %s\n" % A_path,
                   " M               1   %s\n" % D_path,
                   "M                1   %s\n" % mu_path,
                   "M                1   %s\n" % gamma_path,
                   "Status against revision:      1\n"])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "--depth=infinity",
                                     A_path)

#----------------------------------------------------------------------
# Test for issue #2420
@Issue(2420)
def status_dash_u_deleted_directories(sbox):
  "run 'status -u' with locally deleted directories"

  sbox.build()
  wc_dir = sbox.wc_dir
  A_path = sbox.ospath('A')
  B_path = os.path.join(A_path, 'B')

  # delete the B directory
  svntest.actions.run_and_verify_svn(None, [],
                                     'rm', B_path)

  # now run status -u on B and its children
  was_cwd = os.getcwd()

  os.chdir(A_path)

  # check status -u of B
  expected = svntest.verify.UnorderedOutput(
         ["D                1        1 jrandom      %s\n" % \
                                        "B",
          "D                1        1 jrandom      %s\n" % \
                                        os.path.join("B", "lambda"),
          "D                1        1 jrandom      %s\n" % \
                                        os.path.join("B", "E"),
          "D                1        1 jrandom      %s\n" % \
                                        os.path.join("B", "E", "alpha"),
          "D                1        1 jrandom      %s\n" % \
                                        os.path.join("B", "E", "beta"),
          "D                1        1 jrandom      %s\n" % 
          os.path.join("B", "F"),
          "Status against revision:      1\n" ])
  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "-v", "B")

  expected = \
         ["D                1   %s\n" % "B",
          "Status against revision:      1\n" ]
  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "B")


  # again, but now from inside B, should give the same output
  if not os.path.exists('B'):
    os.mkdir('B')
  os.chdir("B")
  expected = svntest.verify.UnorderedOutput(
         ["D                1        1 jrandom      %s\n" % \
                                        ".",
          "D                1        1 jrandom      %s\n" % \
                                        "lambda",
          "D                1        1 jrandom      %s\n" % \
                                        "E",
          "D                1        1 jrandom      %s\n" % \
                                        os.path.join("E", "alpha"),
          "D                1        1 jrandom      %s\n" % \
                                        os.path.join("E", "beta"),
          "D                1        1 jrandom      %s\n" % \
                                        "F",
          "Status against revision:      1\n" ])
  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "-v", ".")

  expected = \
         ["D                1   %s\n" % ".",
          "Status against revision:      1\n" ]
  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", ".")

  # check status -u of B/E
  expected = svntest.verify.UnorderedOutput(
         ["D                1        1 jrandom      %s\n" % \
                                        os.path.join("B", "E"),
          "D                1        1 jrandom      %s\n" % \
                                        os.path.join("B", "E", "alpha"),
          "D                1        1 jrandom      %s\n" % \
                                        os.path.join("B", "E", "beta"),
          "Status against revision:      1\n" ])

  os.chdir(was_cwd)
  os.chdir(A_path)
  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u", "-v",
                                     os.path.join("B", "E"))


  expected = [ "Status against revision:      1\n" ]
  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u",
                                     os.path.join("B", "E"))

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

# Test for issue #2737: show obstructed status for versioned directories
# replaced by local directories.
@Issue(2737)
def status_dash_u_type_change(sbox):
  "status -u on versioned items whose type changed"

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

  os.chdir(wc_dir)

  # Change the versioned file iota into an unversioned dir.
  os.remove('iota')
  os.mkdir('iota')

  xout = ["~                1   iota\n",
          "Status against revision:      1\n" ]

  svntest.actions.run_and_verify_svn(xout,
                                     [],
                                     "status", "-u")

  # Change the versioned directory A into an unversioned dir.
  svntest.main.safe_rmtree('A')
  os.mkdir('A')

  output = [
               "!                1   A/mu\n",
               "!                1   A/B\n",
               "!                1   A/B/lambda\n",
               "!                1   A/B/E\n",
               "!                1   A/B/E/alpha\n",
               "!                1   A/B/E/beta\n",
               "!                1   A/B/F\n",
               "!                1   A/C\n",
               "!                1   A/D\n",
               "!                1   A/D/gamma\n",
               "!                1   A/D/G\n",
               "!                1   A/D/G/rho\n",
               "!                1   A/D/G/pi\n",
               "!                1   A/D/G/tau\n",
               "!                1   A/D/H\n",
               "!                1   A/D/H/chi\n",
               "!                1   A/D/H/omega\n",
               "!                1   A/D/H/psi\n",
               "~                1   iota\n",
               "Status against revision:      1\n"
    ]

  expected = svntest.verify.UnorderedOutput(
                        [s.replace('/', os.path.sep)
                            for s in  output])

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-u")

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

def status_with_tree_conflicts(sbox):
  "status with tree conflicts"

  # Status messages reflecting tree conflict status.
  # These tests correspond to use cases 1-3 in
  # notes/tree-conflicts/use-cases.txt.

  svntest.actions.build_greek_tree_conflicts(sbox)
  wc_dir = sbox.wc_dir
  G = sbox.ospath('A/D/G')
  pi = os.path.join(G, 'pi')
  rho = os.path.join(G, 'rho')
  tau = os.path.join(G, 'tau')

  # check status of G
  expected = svntest.verify.UnorderedOutput(
         ["A  +  C %s\n" % rho,
          "      >   local file edit, incoming file delete or move upon update\n",
          "D     C %s\n" % pi,
          "      >   local file delete, incoming file edit upon update\n",
          "!     C %s\n" % tau,
          "      >   local file delete, incoming file delete or move upon update\n",
         ] + svntest.main.summary_of_conflicts(tree_conflicts=3))

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", G)

  # check status of G, with -v
  expected = svntest.verify.UnorderedOutput(
         ["                 2        2 jrandom      %s\n" % G,
          "D     C          2        2 jrandom      %s\n" % pi,
          "      >   local file delete, incoming file edit upon update\n",
          "A  +  C          -        1 jrandom      %s\n" % rho,
          "      >   local file edit, incoming file delete or move upon update\n",
          "!     C                                  %s\n" % tau,
          "      >   local file delete, incoming file delete or move upon update\n",
         ] + svntest.main.summary_of_conflicts(tree_conflicts=3))

  svntest.actions.run_and_verify_svn(expected,
                                     [],
                                     "status", "-v", G)

  # check status of G, with -xml
  exit_code, output, error = svntest.main.run_svn(None, 'status', G, '--xml',
                                                  '-v')

  should_be_victim = {
    G:   False,
    pi:  True,
    rho: True,
    tau: True,
    }

  real_entry_count = 0
  output_str = r"".join(output)
  # skip the first string, which contains only 'status' and 'target' elements
  entries = output_str.split("<entry")[1:]

  for entry in entries:
    # get the entry's path
    m = re.search('path="([^"]+)"', entry)
    if m:
      real_entry_count += 1
      path = m.group(1)
      # check if the path should be a victim
      m = re.search('tree-conflicted="true"', entry)
      if (m is None) and should_be_victim[path]:
        logger.warn("ERROR: expected '%s' to be a tree conflict victim.", path)
        logger.warn("ACTUAL STATUS OUTPUT:")
        logger.warn(output_str)
        raise svntest.Failure
      if m and not should_be_victim[path]:
        logger.warn("ERROR: did NOT expect '%s' to be a tree conflict victim.",
                    path)
        logger.warn("ACTUAL STATUS OUTPUT:")
        logger.warn(output_str)
        raise svntest.Failure

  if real_entry_count != len(should_be_victim):
    logger.warn("ERROR: 'status --xml' output is incomplete.")
    raise svntest.Failure


#----------------------------------------------------------------------
# Regression for issue #3742
@Issue(3742)
def status_nested_wc_old_format(sbox):
  "status on wc with nested old-format wc"

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir
  os.mkdir(sbox.ospath('subdir'))
  os.mkdir(sbox.ospath('subdir/.svn'))
  svntest.main.file_append(sbox.ospath('subdir/.svn/format'),
                           '10\n') # format 10 was the Subversion 1.6 format
  os.chdir(wc_dir)
  svntest.actions.run_and_verify_svn([ "?       subdir\n" ], [], 'st')

#----------------------------------------------------------------------
# Regression test for issue #3855 "status doesn't show 'K' on a locked
# deleted node".
@Issue(3855)
def status_locked_deleted(sbox):
  "status with locked deleted file"

  sbox.build()
  iota_path = sbox.ospath('iota')

  sbox.simple_rm('iota')
  svntest.actions.run_and_verify_svn(None, [], 'lock',
                                     os.path.join(sbox.wc_dir, 'iota'))
  svntest.actions.run_and_verify_svn(['D    K  %s\n' % iota_path], [],
                                     'status', iota_path)

@Issue(3774)
def wc_wc_copy_timestamp(sbox):
  "timestamp on wc-wc copies"

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

  time.sleep(1.1)
  svntest.main.file_append(sbox.ospath('A/D/H/psi'), 'modified\n')
  svntest.actions.run_and_verify_svn(None, [], 'copy',
                                     sbox.ospath('A/D/H'),
                                     sbox.ospath('A/D/H2'))

  expected_output = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_output.tweak('A/D/H/psi', status='M ')
  expected_output.add({
      'A/D/H2'       : Item(status='A ', copied='+', wc_rev='-'),
      'A/D/H2/chi'   : Item(status='  ', copied='+', wc_rev='-'),
      'A/D/H2/omega' : Item(status='  ', copied='+', wc_rev='-'),
      'A/D/H2/psi'   : Item(status='M ', copied='+', wc_rev='-'),
      })
  svntest.actions.run_and_verify_status(wc_dir, expected_output)

  # Since copied chi is unmodified the text_timestamp should "match"
  # the working file but it's not easy to confirm that directly.  We
  # can confirm that the copied is different from the source.
  chi_src_timestamp = get_text_timestamp(sbox.ospath('A/D/H/chi'))
  chi_dst_timestamp1 = get_text_timestamp(sbox.ospath('A/D/H2/chi'))
  if chi_src_timestamp == chi_dst_timestamp1:
    raise svntest.Failure("chi timestamps should be different")

  # Since copied psi is modified the text_timestamp should not "match"
  # the working file, again difficult to confirm directly.  It happens
  # that the current implementation leaves it equal to the source.
  psi_src_timestamp = get_text_timestamp(sbox.ospath('A/D/H/psi'))
  psi_dst_timestamp = get_text_timestamp(sbox.ospath('A/D/H2/psi'))
  if psi_src_timestamp != psi_dst_timestamp:
    raise svntest.Failure("psi timestamps should be the same")

  # Cleanup repairs timestamps, so this should be a no-op.
  svntest.actions.run_and_verify_svn(None, [], 'cleanup', wc_dir)
  chi_dst_timestamp2 = get_text_timestamp(sbox.ospath('A/D/H2/chi'))
  if chi_dst_timestamp2 != chi_dst_timestamp1:
    raise svntest.Failure("chi timestamps should be the same")

  svntest.actions.run_and_verify_status(wc_dir, expected_output)

@Issue(3908)
def wclock_status(sbox):
  "verbose/non-verbose on locked working copy"

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

  # Recursive lock
  svntest.actions.lock_admin_dir(sbox.ospath('A/D'), True)

  # Verbose status
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/D', 'A/D/G', 'A/D/H', locked='L')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Non-verbose status
  expected_output = svntest.verify.UnorderedOutput([
      '  L     %s\n' % sbox.ospath(path) for path in ['A/D',
                                                      'A/D/G',
                                                      'A/D/H']
      ])
  svntest.actions.run_and_verify_svn(expected_output, [],
                                     'status', wc_dir)

  # Second non-recursive lock
  svntest.actions.lock_admin_dir(sbox.ospath('A/B'))

  expected_status.tweak('A/B', locked='L')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)
  expected_output = svntest.verify.UnorderedOutput([
      '  L     %s\n' % sbox.ospath(path) for path in ['A/B',
                                                      'A/D',
                                                      'A/D/G',
                                                      'A/D/H']
      ])
  svntest.actions.run_and_verify_svn(expected_output, [],
                                     'status', wc_dir)


@Issue(4072)
@XFail()
def modified_modulo_translation(sbox):
  "modified before translation, unmodified after"

  sbox.build()
  wc_dir = sbox.wc_dir

  # iota is a shell script.
  sbox.simple_propset('svn:eol-style', 'LF', 'iota')
  sbox.simple_commit()

  # CRLF it.
  with open(sbox.ospath('iota'), 'wb') as f:
    f.write("This is the file 'iota'.\r\n")

  # Run status.  Expect some output.
  # TODO: decide how such files should show in the output; whether they
  #       always show, or only with some --flag; and adjust this accordingly.
  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [],
                                     'status', wc_dir)

  # Expect the file to be renormalized (to LF) after a revert.
  sbox.simple_revert('iota')
  svntest.actions.run_and_verify_svn([], [], 'status', wc_dir)

def status_not_present(sbox):
  "no status on not-present and excluded nodes"

  sbox.build()
  wc_dir = sbox.wc_dir

  # iota is a shell script.
  sbox.simple_rm('iota', 'A/C')
  svntest.main.run_svn(None, 'up', '--set-depth', 'exclude',
                       sbox.ospath('A/mu'), sbox.ospath('A/B'))
  sbox.simple_commit()

  svntest.actions.run_and_verify_svn([], [],'status',
                                     sbox.ospath('iota'),
                                     sbox.ospath('A/B'),
                                     sbox.ospath('A/C'),
                                     sbox.ospath('A/mu'),
                                     sbox.ospath('no-file'))

# Skip this test is a .svn dir exists in the root directory
@Skip(lambda: os.path.exists("/%s" % svntest.main.get_admin_name()))
def status_unversioned_dir(sbox):
  "status on unversioned dir"
  sbox.build(read_only = True, create_wc = False)

  # Run svn status on "/", which we assume exists and isn't a WC.
  # This should work on UNIX-like systems and Windows systems
  expected_err = "svn: warning: W1550(07|10): .*'.*(/|\\\\)" + \
                 "' is not a working copy"
  svntest.actions.run_and_verify_svn2([], expected_err, 0,
                                      "status", "/")

def status_case_changed(sbox):
  "status reporting on case changed nodes directly"

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

  os.rename(sbox.ospath('iota'), sbox.ospath('iOTA'))

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.add({
    'iOTA'         : Item(status='? '),
  })
  expected_status.tweak('iota', status='! ')

  # First run status on the directory
  svntest.actions.run_and_verify_unquiet_status(wc_dir,
                                                expected_status)

  # Now on the missing iota directly, which should give the same
  # result, even on case insenstive filesystems
  expected_status = svntest.wc.State(wc_dir, {
    'iota'         : Item(status='! ', wc_rev=1),
  })
  svntest.actions.run_and_verify_unquiet_status(sbox.ospath('iota'),
                                                expected_status)

  # And on the unversioned iOTA
  expected_status = svntest.wc.State(wc_dir, {
    'iOTA'         : Item(status='? '),
  })
  svntest.actions.run_and_verify_unquiet_status(sbox.ospath('iOTA'),
                                                expected_status)


def move_update_timestamps(sbox):
  "timestamp behaviour for move-update"

  sbox.build()
  wc_dir = sbox.wc_dir

  sbox.simple_append('A/B/E/beta', 'X\nY\nZ\n', truncate=True)
  sbox.simple_commit()
  sbox.simple_append('A/B/E/alpha', 'modified alpha')
  sbox.simple_append('A/B/E/beta', 'XX\nY\nZ\n', truncate=True)
  sbox.simple_commit()
  sbox.simple_update('', 2)

  sbox.simple_append('A/B/E/beta', 'local beta')
  src_time = get_text_timestamp(sbox.ospath('A/B/E/alpha'))
  sbox.simple_move("A/B/E", "A/B/E2")
  alpha_dst_time = get_text_timestamp(sbox.ospath('A/B/E2/alpha'))
  beta_dst_time = get_text_timestamp(sbox.ospath('A/B/E2/beta'))
  if src_time != alpha_dst_time:
    raise svntest.Failure("move failed to copy timestamp")

  expected_output = svntest.wc.State(wc_dir, {
    'A/B/E'       : Item(status='  ', treeconflict='C'),
    'A/B/E/alpha' : Item(status='  ', treeconflict='U'),
    'A/B/E/beta'  : Item(status='  ', treeconflict='U'),
  })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
  expected_status.tweak('A/B/E',
                        status='D ', treeconflict='C', moved_to='A/B/E2')
  expected_status.tweak('A/B/E/alpha', 'A/B/E/beta', status='D ')
  expected_status.add({
      'A/B/E2'       : Item(status='A ', wc_rev='-', copied='+',
                            moved_from='A/B/E'),
      'A/B/E2/alpha' : Item(status='  ', wc_rev='-', copied='+'),
      'A/B/E2/beta'  : Item(status='M ', wc_rev='-', copied='+'),
      })
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
  expected_disk.add({
      'A/B/E2'       : Item(),
      'A/B/E2/alpha' : Item("This is the file 'alpha'.\n"),
      'A/B/E2/beta'  : Item("X\nY\nZ\nlocal beta"),
      })
  svntest.actions.run_and_verify_update(wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status)

  time.sleep(1.1)
  svntest.actions.run_and_verify_svn(None, [],
                                     'resolve',
                                     '--accept=mine-conflict',
                                     sbox.ospath('A/B/E'))
  expected_status.tweak('A/B/E', treeconflict=None)
  expected_status.tweak('A/B/E2/beta', status='M ')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)
  expected_disk.tweak('A/B/E2/beta', contents="XX\nY\nZ\nlocal beta")
  expected_disk.tweak('A/B/E2/alpha', contents="This is the file 'alpha'.\nmodified alpha")
  svntest.actions.verify_disk(wc_dir, expected_disk)

  # alpha is pristine so gets a new timestamp
  new_time = get_text_timestamp(sbox.ospath('A/B/E2/alpha'))
  if new_time == alpha_dst_time:
    raise svntest.Failure("move failed to update timestamp")

  # beta is modified so timestamp is removed
  no_text_timestamp(sbox.ospath('A/B/E2/beta'))

@Issue(4398)
def status_path_handling(sbox):
  "relative/absolute path handling"

  sbox.build(read_only=True)

  # target is a relative path to a subdir
  wc_dir = sbox.wc_dir
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # target is an absolute path to a subdir
  cwd = os.getcwd()
  abs_wc_dir = os.path.join(cwd, wc_dir)
  expected_status = svntest.actions.get_virginal_state(abs_wc_dir, 1)
  svntest.actions.run_and_verify_status(abs_wc_dir, expected_status)

  # target is an absolute path to a parent dir
  os.chdir(sbox.ospath('A/B'))
  expected_status = svntest.actions.get_virginal_state(abs_wc_dir, 1)
  svntest.actions.run_and_verify_status(abs_wc_dir, expected_status)

  # target is a relative path to a parent dir
  rel_wc_dir = os.path.join('..', '..')
  expected_status = svntest.actions.get_virginal_state(rel_wc_dir, 1)
  svntest.actions.run_and_verify_status(rel_wc_dir, expected_status)

def status_move_missing_direct(sbox):
  "move information when status is called directly"

  sbox.build()
  sbox.simple_copy('A', 'Z')
  sbox.simple_commit('')
  sbox.simple_update('')

  sbox.simple_move('Z', 'ZZ')
  sbox.simple_move('A', 'Z')
  sbox.simple_move('Z/B', 'ZB')
  sbox.simple_mkdir('Z/B')
  sbox.simple_move('ZB/E', 'Z/B/E')

  # Somehow 'svn status' now shows different output for 'ZB/E'
  # when called directly and via an ancestor, as this handles
  # multi-layer in a different way

  # Note that the status output may change over different Subversion revisions,
  # but the status on a node should be identical anyway 'svn status' is called
  # on it.

  expected_output = [
    'A  +    %s\n' % sbox.ospath('ZB'),
    '        > moved from %s\n' % os.path.join('..', 'Z', 'B'),
    'D  +    %s\n' % sbox.ospath('ZB/E'),
    '        > moved to %s\n' % os.path.join('..', 'Z', 'B', 'E'),
  ]
  svntest.actions.run_and_verify_svn(expected_output, [], 'status',
                                     sbox.ospath('ZB'), '--depth', 'immediates')

  # And calling svn status on just 'ZB/E' should have the same result for this node
  # except that we calculate the relative path from a different base
  expected_output = [
    'D  +    %s\n' % sbox.ospath('ZB/E'),
    '        > moved to %s\n' % os.path.join('..', '..', 'Z', 'B', 'E'),
  ]
  svntest.actions.run_and_verify_svn(expected_output, [], 'status',
                                     sbox.ospath('ZB/E'), '--depth', 'empty')

def status_move_missing_direct_base(sbox):
  "move when status is called directly with base"

  sbox.build()
  sbox.simple_copy('A', 'Z')
  sbox.simple_mkdir('Q')
  sbox.simple_mkdir('Q/ZB')
  sbox.simple_mkdir('Q/ZB/E')
  sbox.simple_commit('')
  sbox.simple_update('')

  sbox.simple_rm('Q')
  sbox.simple_mkdir('Q')

  sbox.simple_move('Z', 'ZZ')
  sbox.simple_move('A', 'Z')
  sbox.simple_move('Z/B', 'Q/ZB')
  sbox.simple_mkdir('Z/B')
  sbox.simple_move('Q/ZB/E', 'Z/B/E')

  # Somehow 'svn status' now shows different output for 'Q/ZB/E'
  # when called directly and via an ancestor, as this handles
  # multi-layer in a different way

  # Note that the status output may change over different Subversion revisions,
  # but the status on a node should be identical anyway 'svn status' is called
  # on it.

  # This test had a different result as status_move_missing_direct at the time of
  # writing this test.

  expected_output = [
    'A  +    %s\n' % sbox.ospath('Q/ZB'),
    '        > moved from %s\n' % os.path.join('..', '..', 'Z', 'B'),
    'D  +    %s\n' % sbox.ospath('Q/ZB/E'),
    '        > moved to %s\n' % os.path.join('..', '..', 'Z', 'B', 'E'),
  ]
  svntest.actions.run_and_verify_svn(expected_output, [], 'status',
                                     sbox.ospath('Q/ZB'), '--depth', 'immediates')

  # And calling svn status on just 'ZB/E' should have the same result for this node,
  # except that the moved_to information is calculated from the node itself
  expected_output = [
    'D  +    %s\n' % sbox.ospath('Q/ZB/E'),
    '        > moved to %s\n' % os.path.join('..', '..', '..', 'Z', 'B', 'E'),
  ]
  svntest.actions.run_and_verify_svn(expected_output, [], 'status',
                                     sbox.ospath('Q/ZB/E'), '--depth', 'empty')

def status_missing_conflicts(sbox):
  "status missing certain conflicts"

  sbox.build()
  wc_dir = sbox.wc_dir
  sbox.simple_propset('q', 'r', 'A/B/E/alpha', 'A/B/E/beta')
  sbox.simple_commit()

  sbox.simple_move('A/B/E/alpha', 'alpha')
  sbox.simple_move('A/B/E/beta', 'beta')

  sbox.simple_rm('A/B/E')

  svntest.main.run_svn(False, 'update', sbox.ospath('A/B/E'), '-r', '1',
    '--accept=postpone')

  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/B/E', status='D ', treeconflict='C', wc_rev=1)
  expected_status.tweak('A/B/E/alpha', status='D ', treeconflict='C', wc_rev=1,
                        moved_to='alpha')
  expected_status.tweak('A/B/E/beta', status='D ', treeconflict='C', wc_rev=1,
                        moved_to='beta')
  expected_status.add({
    'alpha' : Item(status='A ', copied='+', moved_from='A/B/E/alpha', wc_rev='-'),
    'beta'  : Item(status='A ', copied='+', moved_from='A/B/E/beta', wc_rev='-')
  })

  svntest.actions.run_and_verify_status(wc_dir, expected_status)
  expected_info = [
    {
        'Tree conflict': 'local file moved away, incoming file edit upon update.*'
    },
    {
        'Tree conflict': 'local file moved away, incoming file edit upon update.*'
    }
  ]
  svntest.actions.run_and_verify_info(expected_info,
                                      sbox.ospath('A/B/E/alpha'),
                                      sbox.ospath('A/B/E/beta'))

  svntest.actions.run_and_verify_svn(None, [],
                                     'resolve', '--accept=mine-conflict',
                                     '--depth=empty', sbox.ospath('A/B/E'))
  expected_status.tweak('A/B/E', treeconflict=None)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Now replace with directory
  sbox.simple_mkdir('A/B/E')
  expected_status.tweak('A/B/E', status='R ')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  svntest.actions.run_and_verify_status(wc_dir, expected_status)
  svntest.actions.run_and_verify_info(expected_info,
                                      sbox.ospath('A/B/E/alpha'),
                                      sbox.ospath('A/B/E/beta'))

  #Recreate scenario for file
  sbox.simple_rm('A/B/E', 'alpha', 'beta')
  svntest.actions.run_and_verify_svn(None, [],
                                     'revert', '-R', sbox.ospath('A/B/E'))

  sbox.simple_update('A/B/E', revision=2)

  sbox.simple_move('A/B/E/alpha', 'alpha')
  sbox.simple_move('A/B/E/beta', 'beta')

  sbox.simple_rm('A/B/E')
  svntest.main.run_svn(False, 'update', sbox.ospath('A/B/E'), '-r', '1',
    '--accept=postpone')
  svntest.actions.run_and_verify_svn(None, [],
                                     'resolve', '--accept=mine-conflict',
                                     '--depth=empty', sbox.ospath('A/B/E'))

  sbox.simple_append('A/B/E', 'something')
  expected_status.tweak('A/B/E', status='D ')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)
  sbox.simple_add('A/B/E')

  # In the entries world A/B/E doesn't have children..
  expected_status.tweak('A/B/E', status='R ', entry_kind='file')

  # Tree conflicts still in db
  svntest.actions.run_and_verify_info(expected_info,
                                      sbox.ospath('A/B/E/alpha'),
                                      sbox.ospath('A/B/E/beta'))

  # But not in status!
  svntest.actions.run_and_verify_status(wc_dir, expected_status)




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


# list all tests here, starting with None:
test_list = [ None,
              status_unversioned_file_in_current_dir,
              status_update_with_nested_adds,
              status_shows_all_in_current_dir,
              status_missing_file,
              status_type_change,
              status_type_change_to_symlink,
              status_with_new_files_pending,
              status_for_unignored_file,
              status_for_nonexistent_file,
              status_file_needs_update,
              status_uninvited_parent_directory,
              status_on_forward_deletion,
              timestamp_behaviour,
              status_on_unversioned_dotdot,
              status_on_partially_nonrecursive_wc,
              missing_dir_in_anchor,
              status_in_xml,
              status_ignored_dir,
              status_unversioned_dir_in_wc,
              status_missing_dir,
              status_nonrecursive_update_different_cwd,
              status_add_plus_conflict,
              inconsistent_eol,
              status_update_with_incoming_props,
              status_update_verbose_with_incoming_props,
              status_nonrecursive_update,
              status_dash_u_deleted_directories,
              status_depth_local,
              status_depth_update,
              status_depth_update_local_modifications,
              status_dash_u_type_change,
              status_with_tree_conflicts,
              status_nested_wc_old_format,
              status_locked_deleted,
              wc_wc_copy_timestamp,
              wclock_status,
              modified_modulo_translation,
              status_not_present,
              status_unversioned_dir,
              status_case_changed,
              move_update_timestamps,
              status_path_handling,
              status_move_missing_direct,
              status_move_missing_direct_base,
              status_missing_conflicts,
             ]

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


### End of file.
