#!/usr/bin/env python
#
#  diff_tests.py:  some basic diff tests
#
#  Subversion is a tool for revision control. 
#  See http://subversion.tigris.org for more information.
#    
# ====================================================================
# Copyright (c) 2000-2006 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 string, sys, re, os.path

# Our testing module
import svntest
from svntest import SVNAnyOutput

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


######################################################################
# Diff output checker
#
# Looks for the correct filenames and a suitable number of +/- lines
# depending on whether this is an addition, modification or deletion.

def check_diff_output(diff_output, name, diff_type):
  "check diff output"

# On Windows, diffs still display / rather than \ in paths
  if svntest.main.windows == 1:
    name = string.replace(name, '\\', '/')
  i_re = re.compile('^Index:')
  d_re = re.compile('^Index: (\\./)?' + name)
  p_re = re.compile('^--- (\\./)?' + name)
  add_re = re.compile('^\\+')
  sub_re = re.compile('^-')

  i = 0
  while i < len(diff_output) - 4:

    # identify a possible diff
    if (d_re.match(diff_output[i])
        and p_re.match(diff_output[i+2])):

      # count lines added and deleted
      i += 4
      add_lines = 0
      sub_lines = 0
      while i < len(diff_output) and not i_re.match(diff_output[i]):
        if add_re.match(diff_output[i][0]):
          add_lines += 1
        if sub_re.match(diff_output[i][0]):
          sub_lines += 1
        i += 1

      #print "add:", add_lines
      #print "sub:", sub_lines
      # check if this looks like the right sort of diff
      if add_lines > 0 and sub_lines == 0 and diff_type == 'A':
        return 0
      if sub_lines > 0 and add_lines == 0 and diff_type == 'D':
        return 0
      if add_lines > 0 and sub_lines > 0 and diff_type == 'M':
        return 0

    else:
      i += 1

  # no suitable diff found
  return 1

def count_diff_output(diff_output):
  "count the number of file diffs in the output"

  i_re = re.compile('Index:')
  diff_count = 0
  i = 0
  while i < len(diff_output) - 4:
    if i_re.match(diff_output[i]):
      i += 4
      diff_count += 1
    else:
      i += 1

  return diff_count

def verify_expected_output(diff_output, expected):
  "verify given line exists in diff output"
  for line in diff_output:
    if line.find(expected) != -1:
      break
  else:
    raise svntest.Failure

def verify_excluded_output(diff_output, excluded):
  "verify given line does not exist in diff output as diff line"
  for line in diff_output:
    if re.match("^(\\+|-)%s" % re.escape(excluded), line):
      print 'Sought: %s' % excluded
      print 'Found:  %s' % line
      raise svntest.Failure

def extract_diff_path(line):
  l2 = line[(line.find("(")+1):]
  l3 = l2[0:(l2.find(")"))]
  return l3

######################################################################
# diff on a repository subset and check the output

def diff_check_repo_subset(wc_dir, repo_subset, check_fn, do_diff_r):
  "diff and check for part of the repository"

  was_cwd = os.getcwd()
  os.chdir(wc_dir)

  try:
    diff_output, err_output = svntest.main.run_svn(None, 'diff', repo_subset)
    if check_fn(diff_output):
      return 1

    if do_diff_r:
      diff_output, err_output = svntest.main.run_svn(None,
                                                     'diff', '-r', 'HEAD',
                                                     repo_subset)
      if check_fn(diff_output):
        return 1

  finally:
    os.chdir(was_cwd)

  return 0

######################################################################
# Changes makers and change checkers

def update_a_file():
  "update a file"
  open(os.path.join('A', 'B', 'E', 'alpha'), 'w').write("new atext")
  # svntest.main.file_append(, "new atext")
  return 0

def check_update_a_file(diff_output):
  "check diff for update a file"
  return check_diff_output(diff_output,
                           os.path.join('A', 'B', 'E', 'alpha'),
                           'M')

def diff_check_update_a_file_repo_subset(wc_dir):
  "diff and check update a file for a repository subset"

  repo_subset = os.path.join('A', 'B')
  if diff_check_repo_subset(wc_dir, repo_subset, check_update_a_file, 1):
    return 1
  
  repo_subset = os.path.join('A', 'B', 'E', 'alpha')
  if diff_check_repo_subset(wc_dir, repo_subset, check_update_a_file, 1):
    return 1

  return 0
  

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

def add_a_file():
  "add a file"
  svntest.main.file_append(os.path.join('A', 'B', 'E', 'theta'), "theta")
  svntest.main.run_svn(None, 'add', os.path.join('A', 'B', 'E', 'theta'))
  return 0

def check_add_a_file(diff_output):
  "check diff for add a file"
  return check_diff_output(diff_output,
                           os.path.join('A', 'B', 'E', 'theta'),
                           'A')

def check_add_a_file_reverse(diff_output):
  "check diff for add a file"
  return check_diff_output(diff_output,
                           os.path.join('A', 'B', 'E', 'theta'),
                           'D')

def diff_check_add_a_file_repo_subset(wc_dir):
  "diff and check add a file for a repository subset"

  repo_subset = os.path.join('A', 'B')
  if diff_check_repo_subset(wc_dir, repo_subset, check_add_a_file, 1):
    return 1
  
  repo_subset = os.path.join('A', 'B', 'E', 'theta')
  ### TODO: diff -r HEAD doesn't work for added file
  if diff_check_repo_subset(wc_dir, repo_subset, check_add_a_file, 0):
    return 1

def update_added_file():
  svntest.main.file_append(os.path.join('A', 'B', 'E', 'theta'), "net ttext")
  "update added file"
  return 0

def check_update_added_file(diff_output):
  "check diff for update of added file"
  return check_diff_output(diff_output,
                           os.path.join('A', 'B', 'E', 'theta'),
                           'M')

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

def add_a_file_in_a_subdir():
  "add a file in a subdir"
  os.mkdir(os.path.join('A', 'B', 'T'))
  svntest.main.run_svn(None, 'add', os.path.join('A', 'B', 'T'))
  svntest.main.file_append(os.path.join('A', 'B', 'T', 'phi'), "phi")
  svntest.main.run_svn(None, 'add', os.path.join('A', 'B', 'T', 'phi'))
  return 0

def check_add_a_file_in_a_subdir(diff_output):
  "check diff for add a file in a subdir"
  return check_diff_output(diff_output,
                           os.path.join('A', 'B', 'T', 'phi'),
                           'A')

def check_add_a_file_in_a_subdir_reverse(diff_output):
  "check diff for add a file in a subdir"
  return check_diff_output(diff_output,
                           os.path.join('A', 'B', 'T', 'phi'),
                           'D')

def diff_check_add_a_file_in_a_subdir_repo_subset(wc_dir):
  "diff and check add a file in a subdir for a repository subset"

  repo_subset = os.path.join('A', 'B', 'T')
  ### TODO: diff -r HEAD doesn't work for added subdir
  if diff_check_repo_subset(wc_dir, repo_subset,
                            check_add_a_file_in_a_subdir, 0):
    return 1
  
  repo_subset = os.path.join('A', 'B', 'T', 'phi')
  ### TODO: diff -r HEAD doesn't work for added file in subdir
  if diff_check_repo_subset(wc_dir, repo_subset,
                            check_add_a_file_in_a_subdir, 0):
    return 1

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

def replace_a_file():
  "replace a file"
  svntest.main.run_svn(None, 'rm', os.path.join('A', 'D', 'G', 'rho'))
  svntest.main.file_append(os.path.join('A', 'D', 'G', 'rho'), "new rho")
  svntest.main.run_svn(None, 'add', os.path.join('A', 'D', 'G', 'rho'))
  return 0

def check_replace_a_file(diff_output):
  "check diff for replace a file"
  return check_diff_output(diff_output,
                       os.path.join('A', 'D', 'G', 'rho'),
                       'M')
    
#----------------------------------------------------------------------

def update_three_files():
  "update three files"
  open(os.path.join('A', 'D', 'gamma'), 'w').write("new gamma")
  open(os.path.join('A', 'D', 'G', 'tau'), 'w').write("new tau")
  open(os.path.join('A', 'D', 'H', 'psi'), 'w').write("new psi")
  return 0

def check_update_three_files(diff_output):
  "check update three files"
  if check_diff_output(diff_output,
                        os.path.join('A', 'D', 'gamma'),
                        'M'):
    return 1
  if check_diff_output(diff_output,
                        os.path.join('A', 'D', 'G', 'tau'),
                        'M'):
    return 1
  if check_diff_output(diff_output,
                        os.path.join('A', 'D', 'H', 'psi'),
                        'M'):
    return 1
  return 0
                        

######################################################################
# make a change, check the diff, commit the change, check the diff

def change_diff_commit_diff(wc_dir, revision, change_fn, check_fn):
  "make a change, diff, commit, update and diff again"

  was_cwd = os.getcwd()
  os.chdir(wc_dir)

  try:
    svntest.main.run_svn(None, 'up', '-r', 'HEAD')

    change_fn()

    # diff without revision doesn't use an editor
    diff_output, err_output = svntest.main.run_svn(None, 'diff')
    if check_fn(diff_output):
      raise svntest.Failure

    # diff with revision runs an editor
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', 'HEAD')
    if check_fn(diff_output):
      raise svntest.Failure

    svntest.main.run_svn(None, 'ci', '-m', 'log msg')
    svntest.main.run_svn(None, 'up')
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', revision)
    if check_fn(diff_output):
      raise svntest.Failure

  finally:
    os.chdir(was_cwd)

######################################################################
# check the diff

def just_diff(wc_dir, rev_check, check_fn):
  "update and check that the given diff is seen"

  was_cwd = os.getcwd()
  os.chdir(wc_dir)

  try:
    diff_output, err_output = svntest.main.run_svn(None,
                                                   'diff', '-r', rev_check)
    if check_fn(diff_output):
      raise svntest.Failure

  finally:
    os.chdir(was_cwd)

######################################################################
# update, check the diff

def update_diff(wc_dir, rev_up, rev_check, check_fn):
  "update and check that the given diff is seen"

  was_cwd = os.getcwd()
  os.chdir(wc_dir)

  try:
    svntest.main.run_svn(None, 'up', '-r', rev_up)

  finally:
    os.chdir(was_cwd)

  just_diff(wc_dir, rev_check, check_fn)

######################################################################
# check a pure repository rev1:rev2 diff

def repo_diff(wc_dir, rev1, rev2, check_fn):
  "check that the given pure repository diff is seen"

  was_cwd = os.getcwd()
  os.chdir(wc_dir)

  try:
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r',
                                                   `rev2` + ':' + `rev1`)
    if check_fn(diff_output):
      raise svntest.Failure

  finally:
    os.chdir(was_cwd)

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

# test 1
def diff_update_a_file(sbox):
  "update a file"

  sbox.build()

  change_diff_commit_diff(sbox.wc_dir, 1,
                          update_a_file,
                          check_update_a_file)

# test 2
def diff_add_a_file(sbox):
  "add a file"

  sbox.build()

  change_diff_commit_diff(sbox.wc_dir, 1,
                          add_a_file,
                          check_add_a_file)

#test 3
def diff_add_a_file_in_a_subdir(sbox):
  "add a file in an added directory"

  sbox.build()

  change_diff_commit_diff(sbox.wc_dir, 1,
                          add_a_file_in_a_subdir,
                          check_add_a_file_in_a_subdir)

# test 4
def diff_replace_a_file(sbox):
  "replace a file with a file"

  sbox.build()

  change_diff_commit_diff(sbox.wc_dir, 1,
                          replace_a_file,
                          check_replace_a_file)

# test 5
def diff_multiple_reverse(sbox):
  "multiple revisions diff'd forwards and backwards"

  sbox.build()
  wc_dir = sbox.wc_dir

  # rev 2
  change_diff_commit_diff(wc_dir, 1,
                          add_a_file,
                          check_add_a_file)

  #rev 3
  change_diff_commit_diff(wc_dir, 2,
                          add_a_file_in_a_subdir,
                          check_add_a_file_in_a_subdir)

  #rev 4
  change_diff_commit_diff(wc_dir, 3,
                          update_a_file,
                          check_update_a_file)

  # check diffs both ways
  update_diff(wc_dir, 4, 1, check_update_a_file)
  just_diff(wc_dir, 1, check_add_a_file_in_a_subdir)
  just_diff(wc_dir, 1, check_add_a_file)
  update_diff(wc_dir, 1, 4, check_update_a_file)
  just_diff(wc_dir, 4, check_add_a_file_in_a_subdir_reverse)
  just_diff(wc_dir, 4, check_add_a_file_reverse)

  # check pure repository diffs
  repo_diff(wc_dir, 4, 1, check_update_a_file)
  repo_diff(wc_dir, 4, 1, check_add_a_file_in_a_subdir)
  repo_diff(wc_dir, 4, 1, check_add_a_file)
  repo_diff(wc_dir, 1, 4, check_update_a_file)
# ### TODO: directory delete doesn't work yet
#  repo_diff(wc_dir, 1, 4, check_add_a_file_in_a_subdir_reverse)
  repo_diff(wc_dir, 1, 4, check_add_a_file_reverse)

# test 6
def diff_non_recursive(sbox):
  "non-recursive behaviour"

  sbox.build()
  wc_dir = sbox.wc_dir

  change_diff_commit_diff(wc_dir, 1,
                          update_three_files,
                          check_update_three_files)

  # The changes are in:   ./A/D/gamma
  #                       ./A/D/G/tau
  #                       ./A/D/H/psi
  # When checking D recursively there are three changes. When checking
  # D non-recursively there is only one change. When checking G
  # recursively, there is only one change even though D is the anchor
  
  # full diff has three changes
  diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1',
                                                 os.path.join(wc_dir, 'A', 'D'))
  if count_diff_output(diff_output) != 3:
    raise svntest.Failure

  # non-recursive has one change
  diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1', '-N',
                                                 os.path.join(wc_dir, 'A', 'D'))
  if count_diff_output(diff_output) != 1:
    raise svntest.Failure

  # diffing a directory doesn't pick up other diffs in the anchor
  diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1',
                                                 os.path.join(wc_dir,
                                                              'A', 'D', 'G'))
  if count_diff_output(diff_output) != 1:
    raise svntest.Failure
  

# test 7
def diff_repo_subset(sbox):
  "diff only part of the repository"

  sbox.build()
  wc_dir = sbox.wc_dir

  was_cwd = os.getcwd()
  os.chdir(wc_dir)

  try:
    update_a_file()
    add_a_file()
    add_a_file_in_a_subdir()

  finally:
    os.chdir(was_cwd)

  if diff_check_update_a_file_repo_subset(wc_dir):
    raise svntest.Failure
  
  if diff_check_add_a_file_repo_subset(wc_dir):
    raise svntest.Failure
  
  if diff_check_add_a_file_in_a_subdir_repo_subset(wc_dir):
    raise svntest.Failure
  

# test 8
def diff_non_version_controlled_file(sbox):
  "non version controlled files"

  sbox.build()
  wc_dir = sbox.wc_dir

  svntest.main.file_append(os.path.join(wc_dir, 'A', 'D', 'foo'), "a new file")

  diff_output, err_output = svntest.main.run_svn(1, 'diff', 
                                                 os.path.join(wc_dir, 
                                                              'A', 'D', 'foo'))

  if count_diff_output(diff_output) != 0: raise svntest.Failure

  # At one point this would crash, so we would only get a 'Segmentation Fault' 
  # error message.  The appropriate response is a few lines of errors.  I wish 
  # there was a way to figure out if svn crashed, but all run_svn gives us is 
  # the output, so here we are...
  for line in err_output:
    if re.search("foo' is not under version control$", line):
      break
  else:
    raise svntest.Failure
  
# test 9
def diff_pure_repository_update_a_file(sbox):
  "pure repository diff update a file"

  sbox.build()
  wc_dir = sbox.wc_dir
  
  was_cwd = os.getcwd()
  os.chdir(wc_dir)

  try:
    # rev 2
    update_a_file()
    svntest.main.run_svn(None, 'ci', '-m', 'log msg')

    # rev 3
    add_a_file_in_a_subdir()
    svntest.main.run_svn(None, 'ci', '-m', 'log msg')

    # rev 4
    add_a_file()
    svntest.main.run_svn(None, 'ci', '-m', 'log msg')

    # rev 5
    update_added_file()
    svntest.main.run_svn(None, 'ci', '-m', 'log msg')

    svntest.main.run_svn(None, 'up', '-r', '2')

    url = svntest.main.current_repo_url

    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-c', '2',
                                                   '--username',
                                                   svntest.main.wc_author,
                                                   '--password',
                                                   svntest.main.wc_passwd,
                                                   url)
    if check_update_a_file(diff_output): raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1:2',
                                                   '--username',
                                                   svntest.main.wc_author,
                                                   '--password',
                                                   svntest.main.wc_passwd)
    if check_update_a_file(diff_output): raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-c', '3',
                                                   '--username',
                                                   svntest.main.wc_author,
                                                   '--password',
                                                   svntest.main.wc_passwd,
                                                   url)
    if check_add_a_file_in_a_subdir(diff_output): raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '2:3',
                                                   '--username',
                                                   svntest.main.wc_author,
                                                   '--password',
                                                   svntest.main.wc_passwd)
    if check_add_a_file_in_a_subdir(diff_output): raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-c', '5',
                                                   '--username',
                                                   svntest.main.wc_author,
                                                   '--password',
                                                   svntest.main.wc_passwd,
                                                   url)
    if check_update_added_file(diff_output): raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '4:5',
                                                   '--username',
                                                   svntest.main.wc_author,
                                                   '--password',
                                                   svntest.main.wc_passwd)
    if check_update_added_file(diff_output): raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', 'head')
    if check_add_a_file_in_a_subdir_reverse(diff_output): raise svntest.Failure

  finally:
    os.chdir(was_cwd)


# test 10
def diff_only_property_change(sbox):
  "diff when property was changed but text was not"

  sbox.build()
  wc_dir = sbox.wc_dir

  expected_output = [
    "\n",
    "Property changes on: iota\n",
    "___________________________________________________________________\n",
    "Name: svn:eol-style\n",
    "   + native\n",
    "\n" ]

  expected_reverse_output = list(expected_output)
  expected_reverse_output[4] = "   - native\n"


  current_dir = os.getcwd()
  os.chdir(sbox.wc_dir)
  try:
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset',
                                       'svn:eol-style', 'native', 'iota')

    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'empty-msg')

    svntest.actions.run_and_verify_svn(None, expected_output, [],
                                       'diff', '-r', '1:2')

    svntest.actions.run_and_verify_svn(None, expected_output, [],
                                       'diff', '-c', '2')

    svntest.actions.run_and_verify_svn(None, expected_reverse_output, [],
                                       'diff', '-r', '2:1')

    svntest.actions.run_and_verify_svn(None, expected_reverse_output, [],
                                       'diff', '-c', '-2')

    svntest.actions.run_and_verify_svn(None, expected_output, [],
                                       'diff', '-r', '1')

    svntest.actions.run_and_verify_svn(None, expected_output, [],
                                       'diff', '-r', 'PREV', 'iota')


  finally:
    os.chdir(current_dir)


#----------------------------------------------------------------------
# Regression test for issue #1019: make sure we don't try to display
# diffs when the file is marked as a binary type.  This tests all 3
# uses of 'svn diff':  wc-wc, wc-repos, repos-repos.

def dont_diff_binary_file(sbox):
  "don't diff file marked as binary type"

  sbox.build()
  wc_dir = sbox.wc_dir
  
  # Add a binary file to the project.
  fp = open(os.path.join(sys.path[0], "theta.bin"))
  theta_contents = fp.read()  # suck up contents of a test .png file
  fp.close()

  theta_path = os.path.join(wc_dir, 'A', 'theta')
  fp = open(theta_path, 'w')
  fp.write(theta_contents)    # write png filedata into 'A/theta'
  fp.close()
  
  svntest.main.run_svn(None, 'add', theta_path)  

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

  # Create expected status tree
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.tweak(wc_rev=1)
  expected_status.add({
    'A/theta' : Item(status='  ', wc_rev=2),
    })

  # Commit the new binary file, creating revision 2.
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        expected_status, None,
                                        None, None, None, None, wc_dir)

  # Update the whole working copy to HEAD (rev 2)
  expected_output = svntest.wc.State(wc_dir, {})

  expected_disk = svntest.main.greek_state.copy()
  expected_disk.add({
    'A/theta' : Item(theta_contents,
                     props={'svn:mime-type' : 'application/octet-stream'}),
    })

  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.add({
    'A/theta' : Item(status='  ', wc_rev=2),
    })

  svntest.actions.run_and_verify_update(wc_dir,
                                        expected_output,
                                        expected_disk,
                                        expected_status,
                                        None, None, None, None, None,
                                        1)  # verify props, too.

  # Make a local mod to the binary file.
  svntest.main.file_append(theta_path, "some extra junk")

  # First diff use-case: plain old 'svn diff wc' will display any
  # local changes in the working copy.  (diffing working
  # vs. text-base)

  re_nodisplay = re.compile('^Cannot display:')

  stdout, stderr = svntest.main.run_svn(None, 'diff', wc_dir)

  for line in stdout:
    if (re_nodisplay.match(line)):
      break
  else:
    raise svntest.Failure

  # Second diff use-case: 'svn diff -r1 wc' compares the wc against a
  # the first revision in the repository.

  stdout, stderr = svntest.main.run_svn(None, 'diff', '-r', '1', wc_dir)

  for line in stdout:
    if (re_nodisplay.match(line)):
      break
  else:
    raise svntest.Failure

  # Now commit the local mod, creating rev 3.
  expected_output = svntest.wc.State(wc_dir, {
    'A/theta' : Item(verb='Sending'),
    })

  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
  expected_status.tweak(wc_rev=2)
  expected_status.add({
    'A/theta' : Item(status='  ', wc_rev=3),
    })

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

  # Third diff use-case: 'svn diff -r2:3 wc' will compare two
  # repository trees.

  stdout, stderr = svntest.main.run_svn(None, 'diff', '-r', '2:3', wc_dir)

  for line in stdout:
    if (re_nodisplay.match(line)):
      break
  else:
    raise svntest.Failure


def diff_nonextant_urls(sbox):
  "svn diff errors against a non-existent URL"

  sbox.build(create_wc = False)
  non_extant_url = sbox.repo_url + '/A/does_not_exist'
  extant_url = sbox.repo_url + '/A/mu'

  diff_output, err_output = svntest.main.run_svn(1, 'diff',
                                                 '--old', non_extant_url,
                                                 '--new', extant_url)
  for line in err_output:
    if re.search('was not found in the repository at revision', line):
      break
  else:
    raise svntest.Failure

  diff_output, err_output = svntest.main.run_svn(1, 'diff',
                                                 '--old', extant_url,
                                                 '--new', non_extant_url)
  for line in err_output:
    if re.search('was not found in the repository at revision', line):
      break
  else:
    raise svntest.Failure

def diff_head_of_moved_file(sbox):
  "diff against the head of a moved file"

  sbox.build()
  mu_path = os.path.join(sbox.wc_dir, 'A', 'mu')
  new_mu_path = mu_path + '.new'

  svntest.main.run_svn(None, 'mv', mu_path, new_mu_path)

  # Modify the file to ensure that the diff is non-empty.
  svntest.main.file_append(new_mu_path, "\nActually, it's a new mu.")

  svntest.actions.run_and_verify_svn(None, SVNAnyOutput, [],
                                     'diff', '-r', 'HEAD', new_mu_path)



#----------------------------------------------------------------------
# Regression test for issue #977: make 'svn diff -r BASE:N' compare a
# repository tree against the wc's text-bases, rather than the wc's
# working files.  This is a long test, which checks many variations.

def diff_base_to_repos(sbox):
  "diff text-bases against repository"

  sbox.build()
  wc_dir = sbox.wc_dir

  iota_path = os.path.join(sbox.wc_dir, 'iota')
  newfile_path = os.path.join(sbox.wc_dir, 'A', 'D', 'newfile')
  mu_path = os.path.join(sbox.wc_dir, 'A', 'mu')

  # Make changes to iota, commit r2, update to HEAD (r2).
  svntest.main.file_append(iota_path, "some rev2 iota text.\n")

  expected_output = svntest.wc.State(wc_dir, {
    'iota' : Item(verb='Sending'),
    })
  
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.tweak(wc_rev=1)
  expected_status.tweak('iota', wc_rev=2)

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

  expected_output = svntest.wc.State(wc_dir, {})
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.tweak ('iota',
                       contents=\
                       "This is the file 'iota'.\nsome rev2 iota text.\n")
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  svntest.actions.run_and_verify_update(wc_dir, expected_output,
                                        expected_disk, expected_status)
  
  # Now make another local mod to iota.
  svntest.main.file_append(iota_path, "an iota local mod.\n")

  # If we run 'svn diff -r 1', we should see diffs that include *both*
  # the rev2 changes and local mods.  That's because the working files
  # are being compared to the repository.
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff',
                                                        '-r', '1', wc_dir)

  # Makes diff output look the same on all platforms.
  def strip_eols(lines):
    return [x.replace("\r", "").replace("\n", "") for x in lines]

  expected_output_lines = [
    "Index: svn-test-work/working_copies/diff_tests-14/iota\n",
    "===================================================================\n",
    "--- svn-test-work/working_copies/diff_tests-14/iota\t(revision 1)\n",
    "+++ svn-test-work/working_copies/diff_tests-14/iota\t(working copy)\n",
    "@@ -1 +1,3 @@\n",
    " This is the file 'iota'.\n",
    "+some rev2 iota text.\n",
    "+an iota local mod.\n"]

  if strip_eols(diff_output) != strip_eols(expected_output_lines):
    raise svntest.Failure

  # If we run 'svn diff -r BASE:1', we should see diffs that only show
  # the rev2 changes and NOT the local mods.  That's because the
  # text-bases are being compared to the repository.
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff', '-r', 'BASE:1',
                                                        wc_dir)
  expected_output_lines = [
    "Index: svn-test-work/working_copies/diff_tests-14/iota\n",
    "===================================================================\n",
    "--- svn-test-work/working_copies/diff_tests-14/iota\t(working copy)\n",
    "+++ svn-test-work/working_copies/diff_tests-14/iota\t(revision 1)\n",
    "@@ -1,2 +1 @@\n",
    " This is the file 'iota'.\n",
    "-some rev2 iota text.\n"]

  if strip_eols(diff_output) != strip_eols(expected_output_lines):
    raise svntest.Failure

  # But that's not all folks... no, no, we're just getting started
  # here!  There are so many other tests to do.

  # For example, we just ran 'svn diff -rBASE:1'.  The output should
  # look exactly the same as 'svn diff -r2:1'.  (If you remove the
  # header commentary)  
  diff_output2, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                         'diff', '-r', '2:1',
                                                         wc_dir)

  diff_output[2:4] = []
  diff_output2[2:4] = []

  if (diff_output2 != diff_output):
    raise svntest.Failure

  # and similarly, does 'svn diff -r1:2' == 'svn diff -r1:BASE' ?
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff',
                                                        '-r', '1:2', wc_dir)
  
  diff_output2, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                         'diff',
                                                         '-r', '1:BASE',
                                                         wc_dir)

  diff_output[2:4] = []
  diff_output2[2:4] = []

  if (diff_output2 != diff_output):
    raise svntest.Failure

  # Now we schedule an addition and a deletion.
  svntest.main.file_append(newfile_path, "Contents of newfile\n")
  svntest.main.run_svn(None, 'add', newfile_path)
  svntest.main.run_svn(None, 'rm', mu_path)

  expected_output = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_output.add({
    'A/D/newfile' : Item(status='A ', wc_rev=0),
    })
  expected_output.tweak('A/mu', status='D ')
  expected_output.tweak('iota', status='M ')
  svntest.actions.run_and_verify_status (wc_dir, expected_output)

  # once again, verify that -r1:2 and -r1:BASE look the same, as do
  # -r2:1 and -rBASE:1.  None of these diffs should mention the
  # scheduled addition or deletion.
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff', '-r',
                                                        '1:2', wc_dir)

  diff_output2, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                         'diff', '-r',
                                                         '1:BASE', wc_dir)

  diff_output3, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                         'diff', '-r',
                                                         '2:1', wc_dir)

  diff_output4, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                         'diff', '-r',
                                                         'BASE:1', wc_dir)

  diff_output[2:4] = []
  diff_output2[2:4] = []
  diff_output3[2:4] = []
  diff_output4[2:4] = []

  if (diff_output != diff_output2):
    raise svntest.Failure

  if (diff_output3 != diff_output4):
    raise svntest.Failure

  # Great!  So far, so good.  Now we commit our three changes (a local
  # mod, an addition, a deletion) and update to HEAD (r3).
  expected_output = svntest.wc.State(wc_dir, {
    'iota' : Item(verb='Sending'),
    'A/mu' : Item(verb='Deleting'),
    'A/D/newfile' : Item(verb='Adding')
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
  expected_status.tweak(wc_rev=2)
  expected_status.tweak('iota', wc_rev=3)
  expected_status.remove('A/mu')
  expected_status.add({
    'A/D/newfile' : Item(status='  ', wc_rev=3),
    })
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        expected_status, None,
                                        None, None, None, None, wc_dir)

  expected_output = svntest.wc.State(wc_dir, {})
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.tweak('iota',
                      contents="This is the file 'iota'.\n" + \
                      "some rev2 iota text.\nan iota local mod.\n")
  expected_disk.add({'A/D/newfile' : Item("Contents of newfile\n")})
  expected_disk.remove ('A/mu')

  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
  expected_status.remove('A/mu')
  expected_status.add({
    'A/D/newfile' : Item(status='  ', wc_rev=3),
    })
  svntest.actions.run_and_verify_update(wc_dir, expected_output,
                                        expected_disk, expected_status)
  
  # Now 'svn diff -r3:2' should == 'svn diff -rBASE:2', showing the
  # removal of changes to iota, the adding of mu, and deletion of newfile.
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff', '-r',
                                                        '3:2', wc_dir)

  diff_output2, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                         'diff', '-r',
                                                         'BASE:2', wc_dir)

  # to do the comparison, remove all output lines starting with +++ or ---
  re_infoline = re.compile('^(\+\+\+|---).*$')
  list1 = []
  list2 = []

  for line in diff_output:
    if not re_infoline.match(line):
      list1.append(line)

  for line in diff_output2:
    if not re_infoline.match(line):
      list2.append(line)

  if list1 != list2:
    raise svntest.Failure
  

#----------------------------------------------------------------------
# This is a simple regression test for issue #891, whereby ra_dav's
# REPORT request would fail, because the object no longer exists in HEAD.

def diff_deleted_in_head(sbox):
  "repos-repos diff on item deleted from HEAD"

  sbox.build()
  wc_dir = sbox.wc_dir

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

  # Make a change to mu, commit r2, update.
  svntest.main.file_append(mu_path, "some rev2 mu text.\n")

  expected_output = svntest.wc.State(wc_dir, {
    'A/mu' : Item(verb='Sending'),
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.tweak(wc_rev=1)
  expected_status.tweak('A/mu', wc_rev=2)

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

  expected_output = svntest.wc.State(wc_dir, {})
  expected_disk = svntest.main.greek_state.copy()
  expected_disk.tweak ('A/mu',
                       contents="This is the file 'mu'.\nsome rev2 mu text.\n")
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  svntest.actions.run_and_verify_update(wc_dir, expected_output,
                                        expected_disk, expected_status)

  # Now delete the whole directory 'A', and commit as r3.
  svntest.main.run_svn(None, 'rm', A_path)
  expected_output = svntest.wc.State(wc_dir, {
    'A' : Item(verb='Deleting'),
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
  expected_status.tweak(wc_rev=2)
  expected_status.remove('A', 'A/B', 'A/B/E', 'A/B/E/beta', 'A/B/E/alpha',
                         'A/B/F', 'A/B/lambda', 'A/D', 'A/D/G', 'A/D/G/rho',
                         'A/D/G/pi', 'A/D/G/tau', 'A/D/H', 'A/D/H/psi',
                         'A/D/H/omega', 'A/D/H/chi', 'A/D/gamma', 'A/mu',
                         'A/C')

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

  # Doing an 'svn diff -r1:2' on the URL of directory A should work,
  # especially over the DAV layer.
  the_url = sbox.repo_url + '/A'
  diff_output = svntest.actions.run_and_verify_svn(None, None, [],
                                                   'diff', '-r',
                                                   '1:2', the_url + "@2")


#----------------------------------------------------------------------
def diff_targets(sbox):
  "select diff targets"

  sbox.build()
  was_cwd = os.getcwd()
  os.chdir(sbox.wc_dir)
  try:
    update_a_file()
    add_a_file()

    update_path = os.path.join('A', 'B', 'E', 'alpha')
    add_path = os.path.join('A', 'B', 'E', 'theta')
    parent_path = os.path.join('A', 'B', 'E')
    update_url = svntest.main.current_repo_url + '/A/B/E/alpha'
    parent_url = svntest.main.current_repo_url + '/A/B/E'

    diff_output, err_output = svntest.main.run_svn(None, 'diff',
                                                   update_path, add_path)
    if check_update_a_file(diff_output) or check_add_a_file(diff_output):
      raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(None, 'diff',
                                                   update_path)
    if check_update_a_file(diff_output) or not check_add_a_file(diff_output):
      raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(None, 'diff',
                                                   '--old', parent_path,
                                                   'alpha', 'theta')
    if check_update_a_file(diff_output) or check_add_a_file(diff_output):
      raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(None, 'diff',
                                                   '--old', parent_path,
                                                   'theta')
    if not check_update_a_file(diff_output) or check_add_a_file(diff_output):
      raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(None, 'ci', '-m', 'log msg')

    diff_output, err_output = svntest.main.run_svn(1, 'diff', '-r1:2',
                                                   update_path, add_path)

    regex = 'svn: Unable to find repository location for \'.*\''
    for line in err_output:
      if re.match(regex, line):
        break
    else:
      raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(1, 'diff', '-r1:2',
                                                   add_path)
    for line in err_output:
      if re.match(regex, line):
        break
    else:
      raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(1, 'diff', '-r1:2',
                                                   '--old', parent_path,
                                                   'alpha', 'theta')
    regex = 'svn: \'.*\' was not found in the repository'
    for line in err_output:
      if re.match(regex, line):
        break
    else:
      raise svntest.Failure

    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r1:2',
                                                   '--old', parent_path,
                                                   'alpha')
    if check_update_a_file(diff_output) or not check_add_a_file(diff_output):
      raise svntest.Failure

  finally:
    os.chdir(was_cwd)
  

#----------------------------------------------------------------------
def diff_branches(sbox):
  "diff for branches"

  sbox.build()

  A_url = svntest.main.current_repo_url + '/A'
  A2_url = svntest.main.current_repo_url + '/A2'

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'cp', '-m', 'log msg',
                                     A_url, A2_url)

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

  A_alpha = os.path.join(sbox.wc_dir, 'A', 'B', 'E', 'alpha')
  A2_alpha = os.path.join(sbox.wc_dir, 'A2', 'B', 'E', 'alpha')

  svntest.main.file_append(A_alpha, "\nfoo\n")
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'log msg', sbox.wc_dir)

  svntest.main.file_append(A2_alpha, "\nbar\n")
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'log msg', sbox.wc_dir)

  svntest.main.file_append(A_alpha, "zig\n")

  # Compare repository file on one branch against repository file on
  # another branch
  rel_path = os.path.join('B', 'E', 'alpha')
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff',
                                                        '--old', A_url,
                                                        '--new', A2_url,
                                                        rel_path)
  verify_expected_output(diff_output, "-foo")
  verify_expected_output(diff_output, "+bar")

  # Same again but using whole branch
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff',
                                                        '--old', A_url,
                                                        '--new', A2_url)
  verify_expected_output(diff_output, "-foo")
  verify_expected_output(diff_output, "+bar")

  # Compare two repository files on different branches
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff',
                                                        A_url + '/B/E/alpha',
                                                        A2_url + '/B/E/alpha')
  verify_expected_output(diff_output, "-foo")
  verify_expected_output(diff_output, "+bar")

  # Compare two versions of a file on a single branch
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff',
                                                        A_url + '/B/E/alpha@2',
                                                        A_url + '/B/E/alpha@3')
  verify_expected_output(diff_output, "+foo")

  # Compare identical files on different branches
  diff_output, err = svntest.actions.run_and_verify_svn(
    None, [], [],
    'diff', A_url + '/B/E/alpha@2', A2_url + '/B/E/alpha@3')


#----------------------------------------------------------------------
def diff_repos_and_wc(sbox):
  "diff between repos URLs and WC paths"

  sbox.build()

  A_url = svntest.main.current_repo_url + '/A'
  A2_url = svntest.main.current_repo_url + '/A2'

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'cp', '-m', 'log msg',
                                     A_url, A2_url)

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

  A_alpha = os.path.join(sbox.wc_dir, 'A', 'B', 'E', 'alpha')
  A2_alpha = os.path.join(sbox.wc_dir, 'A2', 'B', 'E', 'alpha')

  svntest.main.file_append(A_alpha, "\nfoo\n")
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'log msg', sbox.wc_dir)

  svntest.main.file_append(A2_alpha, "\nbar\n")
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'log msg', sbox.wc_dir)

  svntest.main.file_append(A_alpha, "zig\n")

  # Compare working file on one branch against repository file on
  # another branch
  A_path = os.path.join(sbox.wc_dir, 'A')
  rel_path = os.path.join('B', 'E', 'alpha')
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff',
                                                        '--old', A2_url,
                                                        '--new', A_path,
                                                        rel_path)
  verify_expected_output(diff_output, "-bar")
  verify_expected_output(diff_output, "+foo")
  verify_expected_output(diff_output, "+zig")

  # Same again but using whole branch
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff',
                                                        '--old', A2_url,
                                                        '--new', A_path)
  verify_expected_output(diff_output, "-bar")
  verify_expected_output(diff_output, "+foo")
  verify_expected_output(diff_output, "+zig")

#----------------------------------------------------------------------
def diff_file_urls(sbox):
  "diff between two file URLs (issue #1311)"

  sbox.build()

  iota_path = os.path.join(sbox.wc_dir, 'iota')
  iota_url = svntest.main.current_repo_url + '/iota'
  iota_copy_path = os.path.join(sbox.wc_dir, 'A', 'iota')
  iota_copy_url = svntest.main.current_repo_url + '/A/iota'
  iota_copy2_url = svntest.main.current_repo_url + '/A/iota2'

  # Put some different text into iota, and commit.
  os.remove(iota_path)
  svntest.main.file_append(iota_path, "foo\nbar\nsnafu\n")
  
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'log msg', iota_path)

  # Now, copy the file elsewhere, twice.
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'cp', '-m', 'log msg',
                                     iota_url, iota_copy_url)

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'cp', '-m', 'log msg',
                                     iota_url, iota_copy2_url)

  # Update (to get the copies)
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'up', sbox.wc_dir)

  # Now, make edits to one of the copies of iota, and commit.
  os.remove(iota_copy_path)
  svntest.main.file_append(iota_copy_path, "foo\nsnafu\nabcdefg\nopqrstuv\n")

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'log msg', iota_copy_path)

  # Finally, do a diff between the first and second copies of iota,
  # and verify that we got the expected lines.  And then do it in reverse!
  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'diff',
                                                iota_copy_url, iota_copy2_url)

  verify_expected_output(out, "+bar")
  verify_expected_output(out, "-abcdefg")
  verify_expected_output(out, "-opqrstuv")

  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'diff',
                                                iota_copy2_url, iota_copy_url)

  verify_expected_output(out, "-bar")
  verify_expected_output(out, "+abcdefg")
  verify_expected_output(out, "+opqrstuv")
  
#----------------------------------------------------------------------
def diff_prop_change_local_edit(sbox):
  "diff a property change plus a local edit"

  sbox.build()

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

  # Change a property on iota, and commit.
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'propset', 'pname', 'pvalue', iota_path)
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'log msg', iota_path)

  # Make local edits to iota.
  svntest.main.file_append(iota_path, "\nMore text.\n")

  # diff r1:COMMITTED should show the property change but not the local edit.
  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'diff', '-r1:COMMITTED', iota_path)
  for line in out:
    if line.find("+More text.") != -1:
      raise svntest.Failure
  verify_expected_output(out, "   + pvalue")

  # diff r1:BASE should show the property change but not the local edit.
  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'diff', '-r1:BASE', iota_path)
  for line in out:
    if line.find("+More text.") != -1:
      raise svntest.Failure                   # fails at r7481
  verify_expected_output(out, "   + pvalue")  # fails at r7481

  # diff r1:WC should show the local edit as well as the property change.
  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'diff', '-r1', iota_path)
  verify_expected_output(out, "+More text.")  # fails at r7481
  verify_expected_output(out, "   + pvalue")

#----------------------------------------------------------------------
def check_for_omitted_prefix_in_path_component(sbox):
  "check for omitted prefix in path component"

  sbox.build()

  prefix_path = os.path.join(sbox.wc_dir, 'prefix_mydir')
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'mkdir', prefix_path)
  other_prefix_path = os.path.join(sbox.wc_dir, 'prefix_other')
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'mkdir', other_prefix_path)

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'log msg', sbox.wc_dir)


  file_path = os.path.join(prefix_path, "test.txt")
  f = open(file_path, "w")
  f.write("Hello\nThere\nIota\n")
  f.close()

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

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'log msg', sbox.wc_dir)


  prefix_url = svntest.main.current_repo_url + "/prefix_mydir"
  other_prefix_url = svntest.main.current_repo_url + "/prefix_other/mytag"
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'cp', '-m', 'log msg', prefix_url,
                                     other_prefix_url)

  f = open(file_path, "w")
  f.write("Hello\nWorld\nIota\n")
  f.close()

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'log msg', prefix_path)

  out, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                'diff', prefix_url,
                                                other_prefix_url)

  src = extract_diff_path(out[2])
  dest = extract_diff_path(out[3])

  good_src = ".../prefix_mydir"
  good_dest = ".../prefix_other/mytag"

  if ((src != good_src) or (dest != good_dest)):
    print("src is '%s' instead of '%s' and dest is '%s' instead of '%s'" %
          (src, good_src, dest, good_dest))
    raise svntest.Failure

#----------------------------------------------------------------------
def diff_renamed_file(sbox):
  "diff a file that has been renamed"

  sbox.build()

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

  try:
    pi_path = os.path.join('A', 'D', 'G', 'pi')
    pi2_path = os.path.join('A', 'D', 'pi2')
    open(pi_path, 'w').write("new pi")

    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'log msg')

    svntest.main.file_append(pi_path, "even more pi")

    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'log msg')

    svntest.main.run_svn(None, 'mv', pi_path, pi2_path)

    # Repos->WC diff of the file
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1',
                                                   pi2_path)

    if check_diff_output(diff_output,
                         pi2_path,
                         'M') :
      raise svntest.Failure

    svntest.main.file_append(pi2_path, "new pi")

    # Repos->WC of the directory
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1',
                                                   os.path.join('A', 'D'))

    if check_diff_output(diff_output,
                         pi_path,
                         'D') :
      raise svntest.Failure

    if check_diff_output(diff_output,
                         pi2_path,
                         'M') :
      raise svntest.Failure

    # WC->WC of the file
    diff_output, err_output = svntest.main.run_svn(None, 'diff',
                                                   pi2_path)
    if check_diff_output(diff_output,
                         pi2_path,
                         'M') :
      raise svntest.Failure


    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'log msg')

    # Repos->WC diff of file after the rename.
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1',
                                                   pi2_path)
    if check_diff_output(diff_output,
                         pi2_path,
                         'M') :
      raise svntest.Failure

    # Repos->repos diff after the rename.
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '2:3',
                                                   pi2_path)
    if check_diff_output(diff_output,
                         os.path.join('A', 'D', 'pi'),
                         'M') :
      raise svntest.Failure

  finally:
    os.chdir(was_cwd)

#----------------------------------------------------------------------
def diff_within_renamed_dir(sbox):
  "diff a file within a renamed directory"

  sbox.build()

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

  try:
    svntest.main.run_svn(None, 'mv', os.path.join('A', 'D', 'G'),
                                     os.path.join('A', 'D', 'I'))
    # svntest.main.run_svn(None, 'ci', '-m', 'log_msg')
    open(os.path.join('A', 'D', 'I', 'pi'), 'w').write("new pi")

    # Check a repos->wc diff
    diff_output, err_output = svntest.main.run_svn(None, 'diff',
                                                   os.path.join('A', 'D', 'I', 'pi'))
    if check_diff_output(diff_output,
                         os.path.join('A', 'D', 'I', 'pi'),
                         'M') :
      raise svntest.Failure

    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'log msg')

    # Check repos->wc after commit
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1',
                                                   os.path.join('A', 'D', 'I', 'pi'))
    if check_diff_output(diff_output,
                         os.path.join('A', 'D', 'I', 'pi'),
                         'M') :
      raise svntest.Failure

    # Test the diff while within the moved directory
    os.chdir(os.path.join('A','D','I'))

    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1')

    if check_diff_output(diff_output, 'pi', 'M') :
      raise svntest.Failure

    # Test a repos->repos diff while within the moved directory
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1:2')

    if check_diff_output(diff_output, 'pi', 'M') :
      raise svntest.Failure

  finally:
    os.chdir(was_cwd)

#----------------------------------------------------------------------
def diff_prop_on_named_dir(sbox):
  "diff a prop change on a dir named explicitly"

  # Diff of a property change or addition should contain a "+" line.
  # Diff of a property change or deletion should contain a "-" line.
  # On a diff between repository revisions (not WC) of a dir named
  # explicitly, the "-" line was missing.  (For a file, and for a dir
  # recursed into, the result was correct.)

  sbox.build()
  wc_dir = sbox.wc_dir

  current_dir = os.getcwd()
  os.chdir(sbox.wc_dir)
  try:
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset', 'p', 'v', 'A')
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', '')

    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propdel', 'p', 'A')
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', '')

    diff_output, err_output = svntest.main.run_svn(None,
                                                   'diff', '-r2:3', 'A')
    # Check that the result contains a "-" line.
    verify_expected_output(diff_output, "   - v")

  finally:
    os.chdir(current_dir)

#----------------------------------------------------------------------
def diff_keywords(sbox):
  "ensure that diff won't show keywords"

  sbox.build()

  iota_path = os.path.join(sbox.wc_dir, 'iota')
  
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ps',
                                     'svn:keywords',
                                     'Id Rev Date',
                                     iota_path)

  fp = open(iota_path, 'w')
  fp.write("$Date$\n")
  fp.write("$Id$\n")
  fp.write("$Rev$\n")
  fp.write("$Date::%s$\n" % (' ' * 80))
  fp.write("$Id::%s$\n"   % (' ' * 80))
  fp.write("$Rev::%s$\n"  % (' ' * 80))
  fp.close()
  
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'keywords', sbox.wc_dir)

  svntest.main.file_append(iota_path, "bar\n")
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'added bar', sbox.wc_dir)

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

  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff',
                                                        '-r', 'prev:head',
                                                        sbox.wc_dir)
  verify_expected_output(diff_output, "+bar")
  verify_excluded_output(diff_output, "$Date:")
  verify_excluded_output(diff_output, "$Rev:")
  verify_excluded_output(diff_output, "$Id:")
  
  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff',
                                                        '-r', 'head:prev',
                                                        sbox.wc_dir)
  verify_expected_output(diff_output, "-bar")
  verify_excluded_output(diff_output, "$Date:")
  verify_excluded_output(diff_output, "$Rev:")
  verify_excluded_output(diff_output, "$Id:")

  # Check fixed length keywords will show up
  # when the length of keyword has changed
  fp = open(iota_path, 'w')
  fp.write("$Date$\n")
  fp.write("$Id$\n")
  fp.write("$Rev$\n")
  fp.write("$Date::%s$\n" % (' ' * 79))
  fp.write("$Id::%s$\n"   % (' ' * 79))
  fp.write("$Rev::%s$\n"  % (' ' * 79))
  fp.close()

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ci', '-m', 'keywords 2', sbox.wc_dir)
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'up', sbox.wc_dir)

  diff_output, err = svntest.actions.run_and_verify_svn(None, None, [],
                                                        'diff',
                                                        '-r', 'prev:head',
                                                        sbox.wc_dir)
  # these should show up
  verify_expected_output(diff_output, "+$Id:: ")
  verify_expected_output(diff_output, "-$Id:: ")
  verify_expected_output(diff_output, "-$Rev:: ")
  verify_expected_output(diff_output, "+$Rev:: ")
  verify_expected_output(diff_output, "-$Date:: ")
  verify_expected_output(diff_output, "+$Date:: ")
  # ... and these won't
  verify_excluded_output(diff_output, "$Date: ")
  verify_excluded_output(diff_output, "$Rev: ")
  verify_excluded_output(diff_output, "$Id: ")


def diff_force(sbox):
  "show diffs for binary files with --force"

  sbox.build()
  wc_dir = sbox.wc_dir
  
  iota_path = os.path.join(wc_dir, 'iota')
  
  # Append a line to iota and make it binary.
  svntest.main.file_append(iota_path, "new line")
  svntest.main.run_svn(None, 'propset', 'svn:mime-type',
                       'application/octet-stream', iota_path)  

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

  # Create expected status tree
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.tweak(wc_rev=1)
  expected_status.add({
    'iota' : Item(status='  ', wc_rev=2),
    })

  # Commit iota, creating revision 2.
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        expected_status, None,
                                        None, None, None, None, wc_dir)

  # Add another line, while keeping he file as binary.
  svntest.main.file_append(iota_path, "another line")

  # Commit creating rev 3.
  expected_output = svntest.wc.State(wc_dir, {
    'iota' : Item(verb='Sending'),
    })

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

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

  # Check that we get diff when the first, the second and both files are
  # marked as binary.

  re_nodisplay = re.compile('^Cannot display:')

  stdout, stderr = svntest.main.run_svn(None, 'diff', '-r1:2', iota_path,
                                        '--force')

  for line in stdout:
    if (re_nodisplay.match(line)):
      raise svntest.Failure

  stdout, stderr = svntest.main.run_svn(None, 'diff', '-r2:1', iota_path,
                                        '--force')

  for line in stdout:
    if (re_nodisplay.match(line)):
      raise svntest.Failure

  stdout, stderr = svntest.main.run_svn(None, 'diff', '-r2:3', iota_path,
                                        '--force')

  for line in stdout:
    if (re_nodisplay.match(line)):
      raise svntest.Failure

#----------------------------------------------------------------------
# Regression test for issue #2333: Renaming a directory should produce
# deletion and addition diffs for each included file.
def diff_renamed_dir(sbox):
  "diff a renamed directory"

  sbox.build()

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

  try:
    svntest.main.run_svn(None, 'mv', os.path.join('A', 'D', 'G'),
                                     os.path.join('A', 'D', 'I'))

    # Check a repos->wc diff
    diff_output, err_output = svntest.main.run_svn(None, 'diff',
                                                   os.path.join('A', 'D'))
    if check_diff_output(diff_output,
                         os.path.join('A', 'D', 'G', 'pi'),
                         'D') :
      raise svntest.Failure
    if check_diff_output(diff_output,
                         os.path.join('A', 'D', 'I', 'pi'),
                         'A') :
      raise svntest.Failure

    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'log msg')

    # Check repos->wc after commit
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1',
                                                   os.path.join('A', 'D'))
    if check_diff_output(diff_output,
                         os.path.join('A', 'D', 'G', 'pi'),
                         'D') :
      raise svntest.Failure
    if check_diff_output(diff_output,
                         os.path.join('A', 'D', 'I', 'pi'),
                         'A') :
      raise svntest.Failure

    # Test a repos->repos diff after commit
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1:2')
    if check_diff_output(diff_output,
                         os.path.join('A', 'D', 'G', 'pi'),
                         'D') :
      raise svntest.Failure
    if check_diff_output(diff_output,
                         os.path.join('A', 'D', 'I', 'pi'),
                         'A') :
      raise svntest.Failure

    # Test the diff while within the moved directory
    os.chdir(os.path.join('A','D','I'))

    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1')

    if check_diff_output(diff_output, 'pi', 'A') :
      raise svntest.Failure

    # Test a repos->repos diff while within the moved directory
    diff_output, err_output = svntest.main.run_svn(None, 'diff', '-r', '1:2')

    if check_diff_output(diff_output, 'pi', 'A') :
      raise svntest.Failure

  finally:
    os.chdir(was_cwd)


#----------------------------------------------------------------------
def diff_property_changes_to_base(sbox):
  "diff to BASE with local property mods"

  sbox.build()
  wc_dir = sbox.wc_dir

  expected_output_r1_r2 = [
    "\n",
    "Property changes on: A\n",
    "___________________________________________________________________\n",
    "Name: dirprop\n",
    "   + r2value\n",
    "\n",
    "\n",
    "Property changes on: iota\n",
    "___________________________________________________________________\n",
    "Name: fileprop\n",
    "   + r2value\n",
    "\n" ]

  expected_output_r2_r1 = list(expected_output_r1_r2)
  expected_output_r2_r1[4] = "   - r2value\n"
  expected_output_r2_r1[10] = "   - r2value\n"


  current_dir = os.getcwd()
  os.chdir(sbox.wc_dir)
  try:
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset',
                                       'fileprop', 'r2value', 'iota')

    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset',
                                       'dirprop', 'r2value', 'A')

    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'empty-msg')

    # Check that forward and reverse repos-repos diffs are as expected.
    svntest.actions.run_and_verify_svn(None, expected_output_r1_r2, [],
                                       'diff', '-r', '1:2')

    svntest.actions.run_and_verify_svn(None, expected_output_r2_r1, [],
                                       'diff', '-r', '2:1')

    # Now check repos->WORKING, repos->BASE, and BASE->repos.
    # (BASE is r1, and WORKING has no local mods, so this should produce
    # the same output as above).
    svntest.actions.run_and_verify_svn(None, expected_output_r1_r2, [],
                                       'diff', '-r', '1')

    svntest.actions.run_and_verify_svn(None, expected_output_r1_r2, [],
                                       'diff', '-r', '1:BASE')

    svntest.actions.run_and_verify_svn(None, expected_output_r2_r1, [],
                                       'diff', '-r', 'BASE:1')

    # Modify some properties.
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset',
                                       'fileprop', 'workingvalue', 'iota')

    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset',
                                       'dirprop', 'workingvalue', 'A')

    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset',
                                       'fileprop', 'workingvalue', 'A/mu')

    # Check that the earlier diffs against BASE are unaffected by the
    # presence of local mods.
    svntest.actions.run_and_verify_svn(None, expected_output_r1_r2, [],
                                       'diff', '-r', '1:BASE')

    svntest.actions.run_and_verify_svn(None, expected_output_r2_r1, [],
                                       'diff', '-r', 'BASE:1')


  finally:
    os.chdir(current_dir)

def diff_schedule_delete(sbox):
  "scheduled deleted"
  
  sbox.build()

  expected_output_r2_working = [
  "Index: foo\n",
  "===================================================================\n",
  "--- foo\t(revision 2)\n",
  "+++ foo\t(working copy)\n",
  "@@ -1 +0,0 @@\n",
  "-xxx\n"
  ]

  expected_output_r2_base = [
  "Index: foo\n",
  "===================================================================\n",
  "--- foo\t(revision 2)\n",
  "+++ foo\t(working copy)\n",
  "@@ -1 +1,2 @@\n",
  " xxx\n",
  "+yyy\n"
  ]
  expected_output_base_r2 = [
  "Index: foo\n",
  "===================================================================\n",
  "--- foo\t(working copy)\n",
  "+++ foo\t(revision 2)\n",
  "@@ -1,2 +1 @@\n",
  " xxx\n",
  "-yyy\n"
  ]

  expected_output_r1_base = [
  "Index: foo\n",
  "===================================================================\n",
  "--- foo\t(revision 0)\n",
  "+++ foo\t(revision 3)\n",
  "@@ -0,0 +1,2 @@\n",
  "+xxx\n",
  "+yyy\n"
  ]
  expected_output_base_r1 = [
  "Index: foo\n",
  "===================================================================\n",
  "--- foo\t(working copy)\n",
  "+++ foo\t(revision 1)\n",
  "@@ -1,2 +0,0 @@\n",
  "-xxx\n",
  "-yyy\n"
  ]
  expected_output_base_working = expected_output_base_r1[:]
  expected_output_base_working[2] = "--- foo\t(revision 3)\n"
  expected_output_base_working[3] = "+++ foo\t(working copy)\n"

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

  try:
    svntest.main.file_append('foo', "xxx\n")
    svntest.main.run_svn(None, 'add', 'foo')
    svntest.main.run_svn(None, 'ci', '-m', 'log msg r2')

    svntest.main.file_append('foo', "yyy\n")
    svntest.main.run_svn(None, 'ci', '-m', 'log msg r3')

    # Update everyone's BASE to r3, and mark 'foo' as schedule-deleted.
    svntest.main.run_svn(None, 'up')
    svntest.main.run_svn(None, 'rm', 'foo')

    # A file marked as schedule-delete should act as if were not present
    # in WORKING, but diffs against BASE should remain unaffected.

    # 1. repos-wc diff: file not present in repos.
    svntest.actions.run_and_verify_svn(None, [], [],
                                       'diff', '-r', '1')
    svntest.actions.run_and_verify_svn(None, expected_output_r1_base, [],
                                       'diff', '-r', '1:BASE')
    svntest.actions.run_and_verify_svn(None, expected_output_base_r1, [],
                                       'diff', '-r', 'BASE:1')

    # 2. repos-wc diff: file present in repos.
    svntest.actions.run_and_verify_svn(None, expected_output_r2_working, [],
                                       'diff', '-r', '2')
    svntest.actions.run_and_verify_svn(None, expected_output_r2_base, [],
                                       'diff', '-r', '2:BASE')
    svntest.actions.run_and_verify_svn(None, expected_output_base_r2, [],
                                       'diff', '-r', 'BASE:2')

    # 3. wc-wc diff.
    svntest.actions.run_and_verify_svn(None, expected_output_base_working, [],
                                       'diff')

  finally:
    os.chdir(current_dir)

#----------------------------------------------------------------------
def diff_mime_type_changes(sbox):
  "repos-wc diffs with local svn:mime-type prop mods"

  sbox.build()

  expected_output_r1_wc = [
    "Index: iota\n",
    "===================================================================\n",
    "--- iota\t(revision 1)\n",
    "+++ iota\t(working copy)\n",
    "@@ -1 +1,2 @@\n",
    " This is the file 'iota'.\n",
    "+revision 2 text.\n" ]

  expected_output_wc_r1 = [
    "Index: iota\n",
    "===================================================================\n",
    "--- iota\t(working copy)\n",
    "+++ iota\t(revision 1)\n",
    "@@ -1,2 +1 @@\n",
    " This is the file 'iota'.\n",
    "-revision 2 text.\n" ]


  current_dir = os.getcwd()
  os.chdir(sbox.wc_dir)
  try:
    # Append some text to iota (r2).
    svntest.main.file_append('iota', "revision 2 text.\n")

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

    # Check that forward and reverse repos-BASE diffs are as expected.
    svntest.actions.run_and_verify_svn(None, expected_output_r1_wc, [],
                                       'diff', '-r', '1:BASE')

    svntest.actions.run_and_verify_svn(None, expected_output_wc_r1, [],
                                       'diff', '-r', 'BASE:1')

    # Mark iota as a binary file in the working copy.
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset', 'svn:mime-type',
                                       'application/octet-stream', 'iota')

    # Check that the earlier diffs against BASE are unaffected by the
    # presence of local svn:mime-type property mods.
    svntest.actions.run_and_verify_svn(None, expected_output_r1_wc, [],
                                       'diff', '-r', '1:BASE')

    svntest.actions.run_and_verify_svn(None, expected_output_wc_r1, [],
                                       'diff', '-r', 'BASE:1')

    # Commit the change (r3) (so that BASE has the binary MIME type), then
    # mark iota as a text file again in the working copy.
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'log_msg')
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propdel', 'svn:mime-type', 'iota')

    # Now diffs against BASE will fail, but diffs against WORKNG should be
    # fine.
    svntest.actions.run_and_verify_svn(None, expected_output_r1_wc, [],
                                       'diff', '-r', '1')


  finally:
    os.chdir(current_dir)


#----------------------------------------------------------------------
# Test a repos-WORKING diff, with different versions of the same property
# at repository, BASE, and WORKING.
def diff_prop_change_local_propmod(sbox):
  "diff a property change plus a local prop edit"

  sbox.build()

  expected_output_r2_wc = [
    "\n",
    "Property changes on: A\n",
    "___________________________________________________________________\n",
    "Name: dirprop\n",
    "   - r2value\n",
    "   + workingvalue\n",
    "Name: newdirprop\n",
    "   + newworkingvalue\n",
    "\n",
    "\n",
    "Property changes on: iota\n",
    "___________________________________________________________________\n",
    "Name: fileprop\n",
    "   - r2value\n",
    "   + workingvalue\n",
    "Name: newfileprop\n",
    "   + newworkingvalue\n",
    "\n" ]

  current_dir = os.getcwd()
  os.chdir(sbox.wc_dir)
  try:
    # Set a property on A/ and iota, and commit them (r2).
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset', 'dirprop',
                                       'r2value', 'A')
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset', 'fileprop',
                                       'r2value', 'iota')
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'log_msg')

    # Change the property values on A/ and iota, and commit them (r3).
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset', 'dirprop',
                                       'r3value', 'A')
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset', 'fileprop',
                                       'r3value', 'iota')
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'log_msg')

    # Finally, change the property values one last time.
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset', 'dirprop',
                                       'workingvalue', 'A')
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset', 'fileprop',
                                       'workingvalue', 'iota')
    # And also add some properties that only exist in WORKING.
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset', 'newdirprop',
                                       'newworkingvalue', 'A')
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset', 'newfileprop',
                                       'newworkingvalue', 'iota')

    # Now, if we diff r2 to WORKING, we've got three property values
    # to consider: r2value (in the repository), r3value (in BASE), and
    # workingvalue (in WORKING).
    # The diff should only show the r2->WORKING change.
    #
    # We also need to make sure that the 'new' (WORKING only) properties
    # are included in the output, since they won't be listed in a simple
    # BASE->r2 diff.
    svntest.actions.run_and_verify_svn(None, expected_output_r2_wc, [],
                                       'diff', '-r', '2')

  finally:
    os.chdir(current_dir)


#----------------------------------------------------------------------
# repos->wc and BASE->repos diffs that add files or directories with
# properties should show the added properties.
def diff_repos_wc_add_with_props(sbox):
  "repos-wc diff showing added entries with props"

  sbox.build()

  expected_output_r1_r3 = [
    "Index: foo\n",
    "===================================================================\n",
    "--- foo\t(revision 0)\n",
    "+++ foo\t(revision 3)\n",
    "@@ -0,0 +1 @@\n",
    "+content\n",
    "\n",
    "Property changes on: foo\n",
    "___________________________________________________________________\n",
    "Name: propname\n",
    "   + propvalue\n",
    "\n",
    "\n",
    "Property changes on: X\n",
    "___________________________________________________________________\n",
    "Name: propname\n",
    "   + propvalue\n",
    "\n",
    "Index: X/bar\n",
    "===================================================================\n",
    "--- X/bar\t(revision 0)\n",
    "+++ X/bar\t(revision 3)\n",
    "@@ -0,0 +1 @@\n",
    "+content\n",
    "\n",
    "Property changes on: " + os.path.join('X', 'bar') + "\n",
    "___________________________________________________________________\n",
    "Name: propname\n",
    "   + propvalue\n",
    "\n" ]
  # The output from the BASE->repos diff is the same content, but in a
  # different order.
  expected_output_r1_r3_a = expected_output_r1_r3[:12] + \
    expected_output_r1_r3[18:] + expected_output_r1_r3[12:18]

  current_dir = os.getcwd()
  os.chdir(sbox.wc_dir)
  try:
    # Create directory X, file foo, and file X/bar, and commit them (r2).
    os.makedirs('X')
    svntest.main.file_append('foo', "content\n")
    svntest.main.file_append(os.path.join('X', 'bar'), "content\n")
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'add', 'X', 'foo')
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'log_msg')

    # Set a property on all three items, and commit them (r3).
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'propset', 'propname',
                                       'propvalue', 'X', 'foo',
                                       os.path.join('X', 'bar'))
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'log_msg')

    # Now, if we diff r1 to WORKING or BASE, we should see the content
    # addition for foo and X/bar, and property additions for all three.
    svntest.actions.run_and_verify_svn(None, expected_output_r1_r3, [],
                                       'diff', '-r', '1')
    svntest.actions.run_and_verify_svn(None, expected_output_r1_r3, [],
                                       'diff', '-r', '1:BASE')

    # Update the BASE and WORKING revisions to r1.
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'up', '-r', '1')

    # If we diff BASE to r3, we should see the same output as above.
    svntest.actions.run_and_verify_svn(None, expected_output_r1_r3_a, [],
                                       'diff', '-r', 'BASE:3')


  finally:
    os.chdir(current_dir)


#----------------------------------------------------------------------
# repos-wc diffs on a non-recursively checked out wc that would normally
# (if recursively checked out) include a directory that is not present in
# the repos version should not segfault.
def diff_nonrecursive_checkout_deleted_dir(sbox):
  "nonrecursive diff + deleted directories"
  sbox.build()

  url = svntest.main.current_repo_url
  A_url = url + '/A'
  A_prime_url = url + '/A_prime'

  svntest.main.run_svn(None, 'cp', '-m', 'log msg', A_url, A_prime_url)

  svntest.main.run_svn(None, 'mkdir', '-m', 'log msg', A_prime_url + '/Q')

  wc = sbox.add_wc_path('wc')

  svntest.main.run_svn(None, 'co', '-N', A_prime_url, wc)

  saved_cwd = os.getcwd()

  try:
    os.chdir(wc)

    # We don't particular care about the output here, just that it doesn't
    # segfault.
    svntest.main.run_svn(None, 'diff', '-r1')

  finally:
    os.chdir(saved_cwd)


#----------------------------------------------------------------------
# repos->WORKING diffs that include directories with local mods that are
# not present in the repos version should work as expected (and not, for
# example, show an extraneous BASE->WORKING diff for the added directory
# after the repos->WORKING output).
def diff_repos_working_added_dir(sbox):
  "repos->WORKING diff showing added modifed dir"

  sbox.build()

  expected_output_r1_BASE = [
    "Index: X/bar\n",
    "===================================================================\n",
    "--- X/bar\t(revision 0)\n",
    "+++ X/bar\t(revision 2)\n",
    "@@ -0,0 +1 @@\n",
    "+content\n" ]
  expected_output_r1_WORKING = [
    "Index: X/bar\n",
    "===================================================================\n",
    "--- X/bar\t(revision 0)\n",
    "+++ X/bar\t(revision 2)\n",
    "@@ -0,0 +1,2 @@\n",
    "+content\n",
    "+more content\n" ]

  current_dir = os.getcwd()
  os.chdir(sbox.wc_dir)
  try:
    # Create directory X and file X/bar, and commit them (r2).
    os.makedirs('X')
    svntest.main.file_append(os.path.join('X', 'bar'), "content\n")
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'add', 'X')
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'ci', '-m', 'log_msg')

    # Make a local modification to X/bar.
    svntest.main.file_append(os.path.join('X', 'bar'), "more content\n")

    # Now, if we diff r1 to WORKING or BASE, we should see the content
    # addition for X/bar, and (for WORKING) the local modification.
    svntest.actions.run_and_verify_svn(None, expected_output_r1_BASE, [],
                                       'diff', '-r', '1:BASE')
    svntest.actions.run_and_verify_svn(None, expected_output_r1_WORKING, [],
                                       'diff', '-r', '1')

  finally:
    os.chdir(current_dir)


#----------------------------------------------------------------------
# A base->repos diff of a moved file used to output an all-lines-deleted diff
def diff_base_repos_moved(sbox):
  "base->repos diff of moved file"

  sbox.build()

  current_dir = os.getcwd()
  os.chdir(sbox.wc_dir)
  try:
    oldfile = 'iota'
    newfile = 'kappa'

    # Move, modify and commit a file
    svntest.main.run_svn(None, 'mv', oldfile, newfile)
    open(newfile, 'w').write("new content\n")
    svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m', '')

    # Check that a base->repos diff shows deleted and added lines.
    # It's not clear whether we expect a file-change diff or
    # a file-delete plus file-add.  The former is currently produced if we
    # explicitly request a diff of the file itself, and the latter if we
    # request a tree diff which just happens to contain the file.
    out, err = svntest.actions.run_and_verify_svn(None, SVNAnyOutput, [],
                                                  'diff', '-rBASE:1', newfile)
    if check_diff_output(out, newfile, 'M'):
      raise svntest.Failure

    # Diff should recognise that the item's name has changed, and mention both
    # the current and the old name in parentheses, in the right order.
    if (out[2][:3] != '---' or out[2].find('kappa)') == -1 or
        out[3][:3] != '+++' or out[3].find('iota)') == -1):
      raise svntest.Failure

  finally:
    os.chdir(current_dir)


#----------------------------------------------------------------------
# A diff of an added file within an added directory should work, and
# shouldn't produce an error.
def diff_added_subtree(sbox):
  "wc->repos diff of added subtree"

  sbox.build()

  current_dir = os.getcwd()
  os.chdir(sbox.wc_dir)
  try:
    # Roll the wc back to r0 (i.e. an empty wc).
    svntest.actions.run_and_verify_svn(None, None, [],
                                       'up', '-r0')

    # We shouldn't get any errors when we request a diff showing the
    # addition of the greek tree.  The diff contains additions of files
    # and directories with parents that don't currently exist in the wc,
    # which is what we're testing here.
    svntest.actions.run_and_verify_svn(None, SVNAnyOutput, [],
                                       'diff', '-r', 'BASE:1')

  finally:
    os.chdir(current_dir)

#----------------------------------------------------------------------
def basic_diff_summarize(sbox):
  "basic diff summarize"

  sbox.build()
  wc_dir = sbox.wc_dir

  # A content modification.
  svntest.main.file_append(os.path.join(wc_dir, "A", "mu"), "New mu content")

  # A prop modification.
  svntest.main.run_svn(None, "propset", "prop", "val",
                          os.path.join(wc_dir, 'iota'))

  # Both content and prop mods.
  tau_path = os.path.join(wc_dir, "A", "D", "G", "tau")
  svntest.main.file_append(tau_path, "tautau")
  svntest.main.run_svn(None, "propset", "prop", "val", tau_path)

  # A file addition.
  newfile_path = os.path.join(wc_dir, 'newfile')
  svntest.main.file_append(newfile_path, 'newfile')
  svntest.main.run_svn(None, 'add', newfile_path)

  # A file deletion.
  svntest.main.run_svn(None, "delete", os.path.join(wc_dir, 'A', 'B',
                                                    'lambda'))

  expected_output = svntest.wc.State(wc_dir, {
    'A/mu': Item(verb='Sending'),
    'iota': Item(verb='Sending'),
    'newfile': Item(verb='Adding'),
    'A/D/G/tau': Item(verb='Sending'),
    'A/B/lambda': Item(verb='Deleting'),
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.add({
    'newfile': Item(status='  ', wc_rev=2),
    })
  expected_status.tweak("A/mu", "iota", "A/D/G/tau", 'newfile', wc_rev=2)
  expected_status.remove("A/B/lambda")

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

  expected_diff = svntest.wc.State(wc_dir, {
    'A/mu': Item(status='M '),
    'iota': Item(status=' M'),
    'A/D/G/tau': Item(status='MM'),
    'newfile': Item(status='A '),
    'A/B/lambda': Item(status='D '),
    })
  svntest.actions.run_and_verify_diff_summarize(expected_diff, None,
                                                None, None, None, None,
                                                wc_dir, '-r1:2')

def diff_weird_author(sbox):
  "diff with svn:author that has < in it"

  sbox.build()

  svntest.actions.enable_revprop_changes(svntest.main.current_repo_dir)

  open(os.path.join(sbox.wc_dir, 'A', 'mu'), 'w').write("new content\n")

  expected_output = svntest.wc.State(sbox.wc_dir, {
    'A/mu': Item(verb='Sending'),
    })

  expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
  expected_status.tweak("A/mu", wc_rev=2)

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

  svntest.main.run_svn(None, "propset", "--revprop", "-r", "2", "svn:author",
                       "J. Random <jrandom@example.com>", sbox.repo_url)

  svntest.actions.run_and_verify_svn(None,
                                     ["J. Random <jrandom@example.com>\n"],
                                     [],
                                     "pget", "--revprop", "-r" "2",
                                     "svn:author", sbox.repo_url)

  expected_output = [
    "Index: A/mu\n",
    "===================================================================\n",
    "--- A/mu\t(revision 1)\n",
    "+++ A/mu\t(revision 2)\n",
    "@@ -1 +1 @@\n",
    "-This is the file 'mu'.\n",
    "+new content\n"
  ]

  svntest.actions.run_and_verify_svn(None, expected_output, [],
                                     'diff', '-r1:2', sbox.repo_url)

# test for issue 2121, use -x -w option for ignoring whitespace during diff
def diff_ignore_whitespace(sbox):
  "ignore whitespace when diffing"

  sbox.build()
  wc_dir = sbox.wc_dir

  file_name = "iota"
  file_path = os.path.join(wc_dir, file_name)

  open(file_path, 'w').write("Aa\n"
                             "Bb\n"
                             "Cc\n")
  expected_output = svntest.wc.State(wc_dir, {
      'iota' : Item(verb='Sending'),
      })
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        None, None, None, None,
                                        None, None, wc_dir)

  # only whitespace changes, should return no changes
  open(file_path, 'w').write(" A  a   \n"
                             "   B b  \n"
                             "    C    c    \n")

  svntest.actions.run_and_verify_svn(None, [], [],
                                     'diff', '-x', '-w', file_path)
  
  # some changes + whitespace
  open(file_path, 'w').write(" A  a   \n"
                             "Xxxx X\n"
                             "   Bb b  \n"
                             "    C    c    \n")
  expected_output = [
    "Index: svn-test-work/working_copies/diff_tests-39/iota\n",
    "===================================================================\n",
    "--- svn-test-work/working_copies/diff_tests-39/iota\t(revision 2)\n",
    "+++ svn-test-work/working_copies/diff_tests-39/iota\t(working copy)\n",
    "@@ -1,3 +1,4 @@\n",
    " Aa\n",
    "-Bb\n",
    "+Xxxx X\n",
    "+   Bb b  \n",
    " Cc\n" ]

  svntest.actions.run_and_verify_svn(None, expected_output, [],
                                     'diff', '-x', '-w', file_path)

def diff_ignore_eolstyle(sbox):
  "ignore eol styles when diffing"

  sbox.build()
  wc_dir = sbox.wc_dir

  file_name = "iota"
  file_path = os.path.join(wc_dir, file_name)

  open(file_path, 'w').write("Aa\n"
                             "Bb\n"
                             "Cc\n")
  expected_output = svntest.wc.State(wc_dir, {
      'iota' : Item(verb='Sending'),
      })
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        None, None, None, None,
                                        None, None, wc_dir)

  # commit only eol changes
  open(file_path, 'w').write("Aa\r"
                             "Bb\r"
                             "Cc")

  expected_output = [
    "Index: svn-test-work/working_copies/diff_tests-40/iota\n",
    "===================================================================\n",
    "--- svn-test-work/working_copies/diff_tests-40/iota\t(revision 2)\n",
    "+++ svn-test-work/working_copies/diff_tests-40/iota\t(working copy)\n",
    "@@ -1,3 +1,3 @@\n",
    " Aa\n",
    " Bb\n",
    "-Cc\n",
    "+Cc\n",
    "\ No newline at end of file\n" ]
    
  svntest.actions.run_and_verify_svn(None, expected_output, [],
                                     'diff', '-x', '--ignore-eol-style', 
                                     file_path)

# test for issue 2600, diff revision of a file in a renamed folder
def diff_in_renamed_folder(sbox):
  "diff a revision of a file in a renamed folder"

  sbox.build()
  wc_dir = sbox.wc_dir

  C_path = os.path.join(wc_dir, "A", "C")
  D_path = os.path.join(wc_dir, "A", "D")
  kappa_path = os.path.join(D_path, "C", "kappa")

  # add a new file to a renamed (moved in this case) folder.
  svntest.main.run_svn(None, 'mv', C_path, D_path)

  svntest.main.file_append(kappa_path, "this is file kappa.\n")
  svntest.main.run_svn(None, 'add', kappa_path)

  expected_output = svntest.wc.State(wc_dir, {
      'A/C' : Item(verb='Deleting'),
      'A/D/C' : Item(verb='Adding'),
      'A/D/C/kappa' : Item(verb='Adding'),
  })
  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                        None, None, None, None,
                                        None, None, wc_dir)

  expected_output = svntest.wc.State(wc_dir, {
      'A/D/C/kappa' : Item(verb='Sending'),
  })

  # modify the file two times so we have something to diff.
  for i in range(3, 5):
    svntest.main.file_append(kappa_path, str(i) + "\n")
    svntest.actions.run_and_verify_commit(wc_dir, expected_output,
                                          None, None, None, None,
                                          None, None, wc_dir)  

  expected_output = [
    "Index: svn-test-work/working_copies/diff_tests-41/A/D/C/kappa\n",
    "===================================================================\n",
    "--- svn-test-work/working_copies/diff_tests-41/A/D/C/kappa\t(revision 3)\n",
    "+++ svn-test-work/working_copies/diff_tests-41/A/D/C/kappa\t(revision 4)\n",
    "@@ -1,2 +1,3 @@\n",
    " this is file kappa.\n",
    " 3\n",
    "+4\n"
  ]

  svntest.actions.run_and_verify_svn(None, expected_output, [],
                                     'diff', '-r3:4', kappa_path)

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


# list all tests here, starting with None:
test_list = [ None,
              diff_update_a_file,
              diff_add_a_file,
              diff_add_a_file_in_a_subdir,
              diff_replace_a_file,
              diff_multiple_reverse,
              diff_non_recursive,
              diff_repo_subset,
              diff_non_version_controlled_file,
              diff_pure_repository_update_a_file,
              diff_only_property_change,
              dont_diff_binary_file,
              diff_nonextant_urls,
              diff_head_of_moved_file,
              diff_base_to_repos,
              diff_deleted_in_head,
              diff_targets,
              diff_branches,
              diff_repos_and_wc,
              diff_file_urls,
              diff_prop_change_local_edit,
              check_for_omitted_prefix_in_path_component,
              diff_renamed_file,
              diff_within_renamed_dir,
              diff_prop_on_named_dir,
              diff_keywords,
              diff_force,
              diff_schedule_delete,
              XFail(diff_renamed_dir),
              diff_property_changes_to_base,
              diff_mime_type_changes,
              diff_prop_change_local_propmod,
              diff_repos_wc_add_with_props,
              diff_nonrecursive_checkout_deleted_dir,
              diff_repos_working_added_dir,
              diff_base_repos_moved,
              diff_added_subtree,
              basic_diff_summarize,
              diff_weird_author,
              diff_ignore_whitespace,
              diff_ignore_eolstyle,
              diff_in_renamed_folder,
              ]

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


### End of file.


syntax highlighted by Code2HTML, v. 0.9.1