You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by pr...@apache.org on 2013/02/13 11:21:36 UTC

svn commit: r1445542 [15/16] - in /subversion/branches/verify-keep-going: ./ build/generator/ build/generator/swig/ build/generator/templates/ build/win32/ contrib/server-side/fsfsfixer/fixer/ contrib/server-side/svncutter/ notes/ notes/api-errata/1.7/...

Modified: subversion/branches/verify-keep-going/subversion/tests/cmdline/svnauthz_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/tests/cmdline/svnauthz_tests.py?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/tests/cmdline/svnauthz_tests.py (original)
+++ subversion/branches/verify-keep-going/subversion/tests/cmdline/svnauthz_tests.py Wed Feb 13 10:21:33 2013
@@ -144,11 +144,9 @@ def svnauthz_validate_repo_test(sbox):
                                           1, False, "validate", iota_url)
 
   # Non-existant authz url
-  # TODO: This should be exit code 2 but svnauthz is misbehaving and
-  # returning 1 for now.
   # exit code 2, operational error since we can't test the file.
   svntest.actions.run_and_verify_svnauthz("Non-existant authz file", None,
-                                          None, 1, False, "validate",
+                                          None, 2, False, "validate",
                                           repo_url + "/zilch")
 
 def svnauthz_validate_txn_test(sbox):
@@ -327,6 +325,160 @@ def svnauthz_accessof_repo_test(sbox):
                                           "--username", "groucho",
                                           "--repository", "comedy")
  
+def svnauthz_accessof_groups_file_test(sbox):
+  "test 'svnauthz accessof --groups-file' on files"
+
+  # build an authz file
+  (authz_fd, authz_path) = tempfile.mkstemp()
+  authz_content = "[/]\n@musicians = rw\n@comedians = \n" + \
+      "[comedy:/jokes]\n@musicians = \n@comedians = r\n"
+  svntest.main.file_write(authz_path, authz_content)
+
+  # build a groups file
+  (groups_fd, groups_path) = tempfile.mkstemp()
+  groups_content = "[groups]\nmusicians=stafford\ncomedians=groucho\n"
+  svntest.main.file_write(groups_path, groups_content)
+
+  # Anonymous access with no path, and no repository should be no
+  # since it returns the highest level of access granted anywhere.
+  svntest.actions.run_and_verify_svnauthz("Anonymous access", ["no\n"], None,
+                                          0, False, "accessof", authz_path,
+                                          "--groups-file", groups_path)
+
+  # User stafford (@musicians) access with no path, and no repository should
+  # be no since it returns the highest level of access granted anywhere.
+  svntest.actions.run_and_verify_svnauthz("Group 1 access",
+                                          ["rw\n"], None,
+                                          0, False, "accessof", authz_path,
+                                          "--groups-file", groups_path,
+                                          "--username", "stafford")
+
+  # User groucho (@comedians) access with no path, and no repository should
+  # be no since it returns the highest level of access granted anywhere.
+  svntest.actions.run_and_verify_svnauthz("Group 2 access",
+                                          ["no\n"], None,
+                                          0, False, "accessof", authz_path,
+                                          "--groups-file", groups_path,
+                                          "--username", "groucho")  
+
+  # Anonymous access specified on /jokes with the repo comedy will be no.
+  svntest.actions.run_and_verify_svnauthz("Anonymous access on path with repo",
+                                          ["no\n"], None, 0, False,
+                                          "accessof", authz_path,
+                                          "--groups-file", groups_path,
+                                          "--path", "jokes",
+                                          "--repository", "comedy")
+
+  # User stafford (@musicians) specified on /jokes with the repo comedy
+  # will be no.
+  svntest.actions.run_and_verify_svnauthz("Group 1 access on path with repo",
+                                          ["no\n"], None,
+                                          0, False, "accessof", authz_path,
+                                          "--groups-file", groups_path,
+                                          "--path", "jokes",
+                                          "--repository", "comedy",
+                                          "--username", "stafford")
+
+  # User groucho (@comedians) specified on /jokes with the repo
+  # comedy will be r.
+  svntest.actions.run_and_verify_svnauthz("Group 2 access on path with repo",
+                                          ["r\n"], None,
+                                          0, False, "accessof", authz_path,
+                                          "--groups-file", groups_path,
+                                          "--path", "jokes",
+                                          "--repository", "comedy",
+                                          "--username", "groucho")
+
+  os.close(authz_fd)
+  os.remove(authz_path)
+  os.close(groups_fd)
+  os.remove(groups_path)
+
+@SkipUnless(svntest.main.is_ra_type_file)
+def svnauthz_accessof_groups_repo_test(sbox):
+  "test 'svnauthz accessof --groups-file' on urls"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  repo_url = sbox.repo_url
+
+  authz_content = "[/]\n@musicians = rw\n@comedians = \n" + \
+      "[comedy:/jokes]\n@musicians = \n@comedians = r\n"
+
+  groups_content = "[groups]\nmusicians=stafford\ncomedians=groucho\n"
+
+  # build authz and groups files and commit them to the repo
+  authz_path = os.path.join(wc_dir, 'A', 'authz')
+  groups_path = os.path.join(wc_dir, 'A', 'groups')
+  svntest.main.file_write(authz_path, authz_content)
+  svntest.main.file_write(groups_path, groups_content)
+  svntest.main.run_svn(None, 'add', authz_path, groups_path)
+  expected_output = wc.State(wc_dir, {
+    'A/authz'            : Item(verb='Adding'),
+    'A/groups'           : Item(verb='Adding'),
+  })
+  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+  expected_status.add({
+    'A/authz'            :  Item(status='  ', wc_rev=2),
+    'A/groups'           :  Item(status='  ', wc_rev=2),
+  })
+
+  if svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+                                           expected_status, None, wc_dir):
+    raise svntest.Failure
+
+  # Anonymous access with no path, and no repository should be no
+  # since it returns the highest level of access granted anywhere.
+  authz_url = repo_url + "/A/authz"
+  groups_url = repo_url + "/A/groups"
+  svntest.actions.run_and_verify_svnauthz("Anonymous access", ["no\n"], None,
+                                          0, False, "accessof", authz_url,
+                                          "--groups-file", groups_url)
+
+  # User stafford (@musicians) access with no path, and no repository should
+  # be no since it returns the highest level of access granted anywhere.
+  svntest.actions.run_and_verify_svnauthz("Group 1 access",
+                                          ["rw\n"], None,
+                                          0, False, "accessof", authz_url,
+                                          "--groups-file", groups_url,
+                                          "--username", "stafford")
+
+  # User groucho (@comedians) access with no path, and no repository should
+  # be no since it returns the highest level of access granted anywhere.
+  svntest.actions.run_and_verify_svnauthz("Group 2 access",
+                                          ["no\n"], None,
+                                          0, False, "accessof", authz_url,
+                                          "--groups-file", groups_url,
+                                          "--username", "groucho")  
+
+  # Anonymous access specified on /jokes with the repo comedy will be no.
+  svntest.actions.run_and_verify_svnauthz("Anonymous access on path with repo",
+                                          ["no\n"], None, 0, False,
+                                          "accessof", authz_url,
+                                          "--groups-file", groups_url,
+                                          "--path", "jokes",
+                                          "--repository", "comedy")
+
+  # User stafford (@musicians) specified on /jokes with the repo comedy
+  # will be no.
+  svntest.actions.run_and_verify_svnauthz("Group 1 access on path with repo",
+                                          ["no\n"], None,
+                                          0, False, "accessof", authz_url,
+                                          "--groups-file", groups_url,
+                                          "--path", "jokes",
+                                          "--repository", "comedy",
+                                          "--username", "stafford")
+
+  # User groucho (@comedians) specified on /jokes with the repo
+  # comedy will be r.
+  svntest.actions.run_and_verify_svnauthz("Group 2 access on path with repo",
+                                          ["r\n"], None,
+                                          0, False, "accessof", authz_url,
+                                          "--groups-file", groups_url,
+                                          "--path", "jokes",
+                                          "--repository", "comedy",
+                                          "--username", "groucho")
+
 def svnauthz_accessof_is_file_test(sbox):
   "test 'svnauthz accessof --is' on files"
 
@@ -792,9 +944,9 @@ def svnauthz_compat_mode_repo_test(sbox)
                                           authz_path)
 
   # Check a non-existant url.
-  # TODO: Exit code really should be 2 but it's 1 right now.
+  # Exit code really should be 2 since this is an operational error. 
   svntest.actions.run_and_verify_svnauthz(
-      "svnauthz-validate on non-existant file", None, None, 1, True,
+      "svnauthz-validate on non-existant file", None, None, 2, True,
       repo_url + "/zilch"
   )
 
@@ -809,6 +961,8 @@ test_list = [ None,
               svnauthz_validate_txn_test,
               svnauthz_accessof_file_test,
               svnauthz_accessof_repo_test,
+              svnauthz_accessof_groups_file_test,
+              svnauthz_accessof_groups_repo_test,
               svnauthz_accessof_is_file_test,
               svnauthz_accessof_is_repo_test,
               svnauthz_accessof_txn_test,

Modified: subversion/branches/verify-keep-going/subversion/tests/cmdline/svnlook_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/tests/cmdline/svnlook_tests.py?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/tests/cmdline/svnlook_tests.py (original)
+++ subversion/branches/verify-keep-going/subversion/tests/cmdline/svnlook_tests.py Wed Feb 13 10:21:33 2013
@@ -32,6 +32,7 @@ logger = logging.getLogger()
 # Our testing module
 import svntest
 
+from prop_tests import binary_mime_type_on_text_file_warning
 
 # (abbreviation)
 Skip = svntest.testcase.Skip_deco
@@ -578,7 +579,8 @@ def diff_binary(sbox):
   # Set A/mu to a binary mime-type, tweak its text, and commit.
   mu_path = os.path.join(wc_dir, 'A', 'mu')
   svntest.main.file_append(mu_path, 'new appended text for mu')
-  svntest.main.run_svn(None, 'propset', 'svn:mime-type',
+  svntest.main.run_svn(binary_mime_type_on_text_file_warning,
+                       'propset', 'svn:mime-type',
                        'application/octet-stream', mu_path)
   svntest.main.run_svn(None, 'ci', '-m', 'log msg', mu_path)
 

Modified: subversion/branches/verify-keep-going/subversion/tests/cmdline/svnrdump_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/tests/cmdline/svnrdump_tests.py?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/tests/cmdline/svnrdump_tests.py (original)
+++ subversion/branches/verify-keep-going/subversion/tests/cmdline/svnrdump_tests.py Wed Feb 13 10:21:33 2013
@@ -138,12 +138,16 @@ def run_dump_test(sbox, dumpfile_name, e
                                     if not l.startswith('Text-delta-base-md5')]
       svnrdump_dumpfile = [l for l in svnrdump_dumpfile
                                     if not l.startswith('Text-delta-base-md5')]
+    svnadmin_dumpfile = [l for l in svnadmin_dumpfile
+                                  if not mismatched_headers_re.match(l)]
+    svnrdump_dumpfile = [l for l in svnrdump_dumpfile
+                                  if not mismatched_headers_re.match(l)]
 
     svnadmin_dumpfile = svntest.verify.UnorderedOutput(svnadmin_dumpfile)
 
     svntest.verify.compare_and_display_lines(
       "Dump files", "DUMP", svnadmin_dumpfile, svnrdump_dumpfile,
-      None, mismatched_headers_re)
+      None)
 
   else:
     compare_repos_dumps(sbox, svnadmin_dumpfile)

Modified: subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/actions.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/actions.py?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/actions.py (original)
+++ subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/actions.py Wed Feb 13 10:21:33 2013
@@ -40,6 +40,9 @@ from svntest import Failure
 
 logger = logging.getLogger()
 
+# (abbreviation)
+Item = svntest.wc.StateItem
+
 def _log_tree_state(msg, actual, subtree=""):
   if subtree:
     subtree += os.sep
@@ -86,16 +89,10 @@ def setup_pristine_greek_repository():
     # import the greek tree, using l:foo/p:bar
     ### todo: svn should not be prompting for auth info when using
     ### repositories with no auth/auth requirements
-    exit_code, output, errput = main.run_svn(None, 'import', '-m',
-                                             'Log message for revision 1.',
-                                             main.greek_dump_dir,
-                                             main.pristine_greek_repos_url)
-
-    # check for any errors from the import
-    if len(errput):
-      display_lines("Errors during initial 'svn import':",
-                    'STDERR', None, errput)
-      sys.exit(1)
+    _, output, _ = main.run_svn(None, 'import', '-m',
+                                'Log message for revision 1.',
+                                main.greek_dump_dir,
+                                main.pristine_greek_repos_url)
 
     # verify the printed output of 'svn import'.
     lastline = output.pop().strip()
@@ -1132,7 +1129,7 @@ def run_and_verify_merge(dir, rev1, rev2
       for x in out_dry:
         logger.warn(x)
       logger.warn("The full merge output:")
-      for x in out:
+      for x in merge_diff_out:
         logger.warn(x)
       logger.warn("=============================================================")
       raise main.SVNUnmatchedError
@@ -1461,47 +1458,50 @@ def run_and_verify_commit(wc_dir_name, o
 
 # This function always passes '-q' to the status command, which
 # suppresses the printing of any unversioned or nonexistent items.
-def run_and_verify_status(wc_dir_name, output_tree,
+def run_and_verify_status(wc_dir_name, status_tree,
                           singleton_handler_a = None,
                           a_baton = None,
                           singleton_handler_b = None,
                           b_baton = None):
   """Run 'status' on WC_DIR_NAME and compare it with the
-  expected OUTPUT_TREE.  SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will
+  expected STATUS_TREE.  SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will
   be passed to tree.compare_trees - see that function's doc string for
   more details.
   Returns on success, raises on failure."""
 
-  if isinstance(output_tree, wc.State):
-    output_state = output_tree
-    output_tree = output_tree.old_tree()
-  else:
-    output_state = None
-
   exit_code, output, errput = main.run_svn(None, 'status', '-v', '-u', '-q',
                                            wc_dir_name)
 
-  actual = tree.build_tree_from_status(output, wc_dir_name=wc_dir_name)
+  actual_status = svntest.wc.State.from_status(output)
 
   # Verify actual output against expected output.
-  try:
-    tree.compare_trees("status", actual, output_tree,
-                       singleton_handler_a, a_baton,
-                       singleton_handler_b, b_baton)
-  except tree.SVNTreeError:
-    verify.display_trees(None, 'STATUS OUTPUT TREE', output_tree, actual)
-    _log_tree_state("ACTUAL STATUS TREE:", actual, wc_dir_name)
-    raise
+  if isinstance(status_tree, wc.State):
+    try:
+      status_tree.compare_and_display('status', actual_status)
+    except tree.SVNTreeError:
+      _log_tree_state("ACTUAL STATUS TREE:", actual_status.old_tree(),
+                                             wc_dir_name)
+      raise
+  else:
+    actual_status = actual_status.old_tree()
+    try:
+      tree.compare_trees("status", actual_status, status_tree,
+                         singleton_handler_a, a_baton,
+                         singleton_handler_b, b_baton)
+    except tree.SVNTreeError:
+      verify.display_trees(None, 'STATUS OUTPUT TREE', status_tree, actual_status)
+      _log_tree_state("ACTUAL STATUS TREE:", actual_status, wc_dir_name)
+      raise
 
   # if we have an output State, and we can/are-allowed to create an
   # entries-based State, then compare the two.
-  if output_state:
-    entries_state = wc.State.from_entries(wc_dir_name)
-    if entries_state:
-      tweaked = output_state.copy()
+  if isinstance(status_tree, wc.State):
+    actual_entries = wc.State.from_entries(wc_dir_name)
+    if actual_entries:
+      tweaked = status_tree.copy()
       tweaked.tweak_for_entries_compare()
       try:
-        tweaked.compare_and_display('entries', entries_state)
+        tweaked.compare_and_display('entries', actual_entries)
       except tree.SVNTreeUnequal:
         ### do something more
         raise
@@ -1514,20 +1514,27 @@ def run_and_verify_unquiet_status(wc_dir
   expected STATUS_TREE.
   Returns on success, raises on failure."""
 
-  if isinstance(status_tree, wc.State):
-    status_tree = status_tree.old_tree()
-
   exit_code, output, errput = main.run_svn(None, 'status', '-v',
                                            '-u', wc_dir_name)
 
-  actual = tree.build_tree_from_status(output, wc_dir_name=wc_dir_name)
+  actual_status = svntest.wc.State.from_status(output)
 
   # Verify actual output against expected output.
-  try:
-    tree.compare_trees("UNQUIET STATUS", actual, status_tree)
-  except tree.SVNTreeError:
-    _log_tree_state("ACTUAL UNQUIET STATUS TREE:", actual, wc_dir_name)
-    raise
+  if isinstance(status_tree, wc.State):
+    try:
+      status_tree.compare_and_display('unquiet status', actual_status)
+    except tree.SVNTreeError:
+      _log_tree_state("ACTUAL STATUS TREE:",
+                      actual_status.normalize().old_tree(), wc_dir_name)
+      raise
+  else:
+    actual_status = actual_status.old_tree()
+    try:
+      tree.compare_trees("UNQUIET STATUS", actual_status, status_tree)
+    except tree.SVNTreeError:
+      _log_tree_state("ACTUAL UNQUIET STATUS TREE:", actual_status,
+                                                     wc_dir_name)
+      raise
 
 def run_and_verify_status_xml(expected_entries = [],
                               *args):
@@ -2213,980 +2220,3 @@ def build_greek_tree_conflicts(sbox):
   run_and_verify_svn(None, verify.AnyOutput, [], 'update', wc_dir)
 
 
-def make_deep_trees(base):
-  """Helper function for deep trees conflicts. Create a set of trees,
-  each in its own "container" dir. Any conflicts can be tested separately
-  in each container.
-  """
-  j = os.path.join
-  # Create the container dirs.
-  F   = j(base, 'F')
-  D   = j(base, 'D')
-  DF  = j(base, 'DF')
-  DD  = j(base, 'DD')
-  DDF = j(base, 'DDF')
-  DDD = j(base, 'DDD')
-  os.makedirs(F)
-  os.makedirs(j(D, 'D1'))
-  os.makedirs(j(DF, 'D1'))
-  os.makedirs(j(DD, 'D1', 'D2'))
-  os.makedirs(j(DDF, 'D1', 'D2'))
-  os.makedirs(j(DDD, 'D1', 'D2', 'D3'))
-
-  # Create their files.
-  alpha = j(F, 'alpha')
-  beta  = j(DF, 'D1', 'beta')
-  gamma = j(DDF, 'D1', 'D2', 'gamma')
-  main.file_append(alpha, "This is the file 'alpha'.\n")
-  main.file_append(beta, "This is the file 'beta'.\n")
-  main.file_append(gamma, "This is the file 'gamma'.\n")
-
-
-def add_deep_trees(sbox, base_dir_name):
-  """Prepare a "deep_trees" within a given directory.
-
-  The directory <sbox.wc_dir>/<base_dir_name> is created and a deep_tree
-  is created within. The items are only added, a commit has to be
-  called separately, if needed.
-
-  <base_dir_name> will thus be a container for the set of containers
-  mentioned in make_deep_trees().
-  """
-  j = os.path.join
-  base = j(sbox.wc_dir, base_dir_name)
-  make_deep_trees(base)
-  main.run_svn(None, 'add', base)
-
-
-Item = wc.StateItem
-
-# initial deep trees state
-deep_trees_virginal_state = wc.State('', {
-  'F'               : Item(),
-  'F/alpha'         : Item("This is the file 'alpha'.\n"),
-  'D'               : Item(),
-  'D/D1'            : Item(),
-  'DF'              : Item(),
-  'DF/D1'           : Item(),
-  'DF/D1/beta'      : Item("This is the file 'beta'.\n"),
-  'DD'              : Item(),
-  'DD/D1'           : Item(),
-  'DD/D1/D2'        : Item(),
-  'DDF'             : Item(),
-  'DDF/D1'          : Item(),
-  'DDF/D1/D2'       : Item(),
-  'DDF/D1/D2/gamma' : Item("This is the file 'gamma'.\n"),
-  'DDD'             : Item(),
-  'DDD/D1'          : Item(),
-  'DDD/D1/D2'       : Item(),
-  'DDD/D1/D2/D3'    : Item(),
-  })
-
-
-# Many actions on deep trees and their resulting states...
-
-def deep_trees_leaf_edit(base):
-  """Helper function for deep trees test cases. Append text to files,
-  create new files in empty directories, and change leaf node properties."""
-  j = os.path.join
-  F   = j(base, 'F', 'alpha')
-  DF  = j(base, 'DF', 'D1', 'beta')
-  DDF = j(base, 'DDF', 'D1', 'D2', 'gamma')
-  main.file_append(F, "More text for file alpha.\n")
-  main.file_append(DF, "More text for file beta.\n")
-  main.file_append(DDF, "More text for file gamma.\n")
-  run_and_verify_svn(None, verify.AnyOutput, [],
-                     'propset', 'prop1', '1', F, DF, DDF)
-
-  D   = j(base, 'D', 'D1')
-  DD  = j(base, 'DD', 'D1', 'D2')
-  DDD = j(base, 'DDD', 'D1', 'D2', 'D3')
-  run_and_verify_svn(None, verify.AnyOutput, [],
-                     'propset', 'prop1', '1', D, DD, DDD)
-  D   = j(base, 'D', 'D1', 'delta')
-  DD  = j(base, 'DD', 'D1', 'D2', 'epsilon')
-  DDD = j(base, 'DDD', 'D1', 'D2', 'D3', 'zeta')
-  main.file_append(D, "This is the file 'delta'.\n")
-  main.file_append(DD, "This is the file 'epsilon'.\n")
-  main.file_append(DDD, "This is the file 'zeta'.\n")
-  run_and_verify_svn(None, verify.AnyOutput, [],
-                     'add', D, DD, DDD)
-
-# deep trees state after a call to deep_trees_leaf_edit
-deep_trees_after_leaf_edit = wc.State('', {
-  'F'                 : Item(),
-  'F/alpha'           : Item("This is the file 'alpha'.\nMore text for file alpha.\n"),
-  'D'                 : Item(),
-  'D/D1'              : Item(),
-  'D/D1/delta'        : Item("This is the file 'delta'.\n"),
-  'DF'                : Item(),
-  'DF/D1'             : Item(),
-  'DF/D1/beta'        : Item("This is the file 'beta'.\nMore text for file beta.\n"),
-  'DD'                : Item(),
-  'DD/D1'             : Item(),
-  'DD/D1/D2'          : Item(),
-  'DD/D1/D2/epsilon'  : Item("This is the file 'epsilon'.\n"),
-  'DDF'               : Item(),
-  'DDF/D1'            : Item(),
-  'DDF/D1/D2'         : Item(),
-  'DDF/D1/D2/gamma'   : Item("This is the file 'gamma'.\nMore text for file gamma.\n"),
-  'DDD'               : Item(),
-  'DDD/D1'            : Item(),
-  'DDD/D1/D2'         : Item(),
-  'DDD/D1/D2/D3'      : Item(),
-  'DDD/D1/D2/D3/zeta' : Item("This is the file 'zeta'.\n"),
-  })
-
-
-def deep_trees_leaf_del(base):
-  """Helper function for deep trees test cases. Delete files and empty
-  dirs."""
-  j = os.path.join
-  F   = j(base, 'F', 'alpha')
-  D   = j(base, 'D', 'D1')
-  DF  = j(base, 'DF', 'D1', 'beta')
-  DD  = j(base, 'DD', 'D1', 'D2')
-  DDF = j(base, 'DDF', 'D1', 'D2', 'gamma')
-  DDD = j(base, 'DDD', 'D1', 'D2', 'D3')
-  main.run_svn(None, 'rm', F, D, DF, DD, DDF, DDD)
-
-# deep trees state after a call to deep_trees_leaf_del
-deep_trees_after_leaf_del = wc.State('', {
-  'F'               : Item(),
-  'D'               : Item(),
-  'DF'              : Item(),
-  'DF/D1'           : Item(),
-  'DD'              : Item(),
-  'DD/D1'           : Item(),
-  'DDF'             : Item(),
-  'DDF/D1'          : Item(),
-  'DDF/D1/D2'       : Item(),
-  'DDD'             : Item(),
-  'DDD/D1'          : Item(),
-  'DDD/D1/D2'       : Item(),
-  })
-
-# deep trees state after a call to deep_trees_leaf_del with no commit
-def deep_trees_after_leaf_del_no_ci(wc_dir):
-  if svntest.main.wc_is_singledb(wc_dir):
-    return deep_trees_after_leaf_del
-  else:
-    return deep_trees_empty_dirs
-
-
-def deep_trees_tree_del(base):
-  """Helper function for deep trees test cases.  Delete top-level dirs."""
-  j = os.path.join
-  F   = j(base, 'F', 'alpha')
-  D   = j(base, 'D', 'D1')
-  DF  = j(base, 'DF', 'D1')
-  DD  = j(base, 'DD', 'D1')
-  DDF = j(base, 'DDF', 'D1')
-  DDD = j(base, 'DDD', 'D1')
-  main.run_svn(None, 'rm', F, D, DF, DD, DDF, DDD)
-
-def deep_trees_rmtree(base):
-  """Helper function for deep trees test cases.  Delete top-level dirs
-     with rmtree instead of svn del."""
-  j = os.path.join
-  F   = j(base, 'F', 'alpha')
-  D   = j(base, 'D', 'D1')
-  DF  = j(base, 'DF', 'D1')
-  DD  = j(base, 'DD', 'D1')
-  DDF = j(base, 'DDF', 'D1')
-  DDD = j(base, 'DDD', 'D1')
-  os.unlink(F)
-  main.safe_rmtree(D)
-  main.safe_rmtree(DF)
-  main.safe_rmtree(DD)
-  main.safe_rmtree(DDF)
-  main.safe_rmtree(DDD)
-
-# deep trees state after a call to deep_trees_tree_del
-deep_trees_after_tree_del = wc.State('', {
-  'F'                 : Item(),
-  'D'                 : Item(),
-  'DF'                : Item(),
-  'DD'                : Item(),
-  'DDF'               : Item(),
-  'DDD'               : Item(),
-  })
-
-# deep trees state without any files
-deep_trees_empty_dirs = wc.State('', {
-  'F'               : Item(),
-  'D'               : Item(),
-  'D/D1'            : Item(),
-  'DF'              : Item(),
-  'DF/D1'           : Item(),
-  'DD'              : Item(),
-  'DD/D1'           : Item(),
-  'DD/D1/D2'        : Item(),
-  'DDF'             : Item(),
-  'DDF/D1'          : Item(),
-  'DDF/D1/D2'       : Item(),
-  'DDD'             : Item(),
-  'DDD/D1'          : Item(),
-  'DDD/D1/D2'       : Item(),
-  'DDD/D1/D2/D3'    : Item(),
-  })
-
-# deep trees state after a call to deep_trees_tree_del with no commit
-def deep_trees_after_tree_del_no_ci(wc_dir):
-  if svntest.main.wc_is_singledb(wc_dir):
-    return deep_trees_after_tree_del
-  else:
-    return deep_trees_empty_dirs
-
-def deep_trees_tree_del_repos(base):
-  """Helper function for deep trees test cases.  Delete top-level dirs,
-  directly in the repository."""
-  j = '/'.join
-  F   = j([base, 'F', 'alpha'])
-  D   = j([base, 'D', 'D1'])
-  DF  = j([base, 'DF', 'D1'])
-  DD  = j([base, 'DD', 'D1'])
-  DDF = j([base, 'DDF', 'D1'])
-  DDD = j([base, 'DDD', 'D1'])
-  main.run_svn(None, 'mkdir', '-m', '', F, D, DF, DD, DDF, DDD)
-
-# Expected merge/update/switch output.
-
-deep_trees_conflict_output = wc.State('', {
-  'F/alpha'           : Item(status='  ', treeconflict='C'),
-  'D/D1'              : Item(status='  ', treeconflict='C'),
-  'DF/D1'             : Item(status='  ', treeconflict='C'),
-  'DD/D1'             : Item(status='  ', treeconflict='C'),
-  'DDF/D1'            : Item(status='  ', treeconflict='C'),
-  'DDD/D1'            : Item(status='  ', treeconflict='C'),
-  })
-
-deep_trees_conflict_output_skipped = wc.State('', {
-  'D/D1'              : Item(verb='Skipped'),
-  'F/alpha'           : Item(verb='Skipped'),
-  'DD/D1'             : Item(verb='Skipped'),
-  'DF/D1'             : Item(verb='Skipped'),
-  'DDD/D1'            : Item(verb='Skipped'),
-  'DDF/D1'            : Item(verb='Skipped'),
-  })
-
-# Expected status output after merge/update/switch.
-
-deep_trees_status_local_tree_del = wc.State('', {
-  ''                  : Item(status='  ', wc_rev=3),
-  'D'                 : Item(status='  ', wc_rev=3),
-  'D/D1'              : Item(status='D ', wc_rev=2, treeconflict='C'),
-  'DD'                : Item(status='  ', wc_rev=3),
-  'DD/D1'             : Item(status='D ', wc_rev=2, treeconflict='C'),
-  'DD/D1/D2'          : Item(status='D ', wc_rev=2),
-  'DDD'               : Item(status='  ', wc_rev=3),
-  'DDD/D1'            : Item(status='D ', wc_rev=2, treeconflict='C'),
-  'DDD/D1/D2'         : Item(status='D ', wc_rev=2),
-  'DDD/D1/D2/D3'      : Item(status='D ', wc_rev=2),
-  'DDF'               : Item(status='  ', wc_rev=3),
-  'DDF/D1'            : Item(status='D ', wc_rev=2, treeconflict='C'),
-  'DDF/D1/D2'         : Item(status='D ', wc_rev=2),
-  'DDF/D1/D2/gamma'   : Item(status='D ', wc_rev=2),
-  'DF'                : Item(status='  ', wc_rev=3),
-  'DF/D1'             : Item(status='D ', wc_rev=2, treeconflict='C'),
-  'DF/D1/beta'        : Item(status='D ', wc_rev=2),
-  'F'                 : Item(status='  ', wc_rev=3),
-  'F/alpha'           : Item(status='D ', wc_rev=2, treeconflict='C'),
-  })
-
-deep_trees_status_local_leaf_edit = wc.State('', {
-  ''                  : Item(status='  ', wc_rev=3),
-  'D'                 : Item(status='  ', wc_rev=3),
-  'D/D1'              : Item(status=' M', wc_rev=2, treeconflict='C'),
-  'D/D1/delta'        : Item(status='A ', wc_rev=0),
-  'DD'                : Item(status='  ', wc_rev=3),
-  'DD/D1'             : Item(status='  ', wc_rev=2, treeconflict='C'),
-  'DD/D1/D2'          : Item(status=' M', wc_rev=2),
-  'DD/D1/D2/epsilon'  : Item(status='A ', wc_rev=0),
-  'DDD'               : Item(status='  ', wc_rev=3),
-  'DDD/D1'            : Item(status='  ', wc_rev=2, treeconflict='C'),
-  'DDD/D1/D2'         : Item(status='  ', wc_rev=2),
-  'DDD/D1/D2/D3'      : Item(status=' M', wc_rev=2),
-  'DDD/D1/D2/D3/zeta' : Item(status='A ', wc_rev=0),
-  'DDF'               : Item(status='  ', wc_rev=3),
-  'DDF/D1'            : Item(status='  ', wc_rev=2, treeconflict='C'),
-  'DDF/D1/D2'         : Item(status='  ', wc_rev=2),
-  'DDF/D1/D2/gamma'   : Item(status='MM', wc_rev=2),
-  'DF'                : Item(status='  ', wc_rev=3),
-  'DF/D1'             : Item(status='  ', wc_rev=2, treeconflict='C'),
-  'DF/D1/beta'        : Item(status='MM', wc_rev=2),
-  'F'                 : Item(status='  ', wc_rev=3),
-  'F/alpha'           : Item(status='MM', wc_rev=2, treeconflict='C'),
-  })
-
-
-class DeepTreesTestCase:
-  """Describes one tree-conflicts test case.
-  See deep_trees_run_tests_scheme_for_update(), ..._switch(), ..._merge().
-
-  The name field is the subdirectory name in which the test should be run.
-
-  The local_action and incoming_action are the functions to run
-  to construct the local changes and incoming changes, respectively.
-  See deep_trees_leaf_edit, deep_trees_tree_del, etc.
-
-  The expected_* and error_re_string arguments are described in functions
-  run_and_verify_[update|switch|merge]
-  except expected_info, which is a dict that has path keys with values
-  that are dicts as passed to run_and_verify_info():
-    expected_info = {
-      'F/alpha' : {
-        'Revision' : '3',
-        'Tree conflict' :
-          '^local delete, incoming edit upon update'
-          + ' Source  left: .file.*/F/alpha@2'
-          + ' Source right: .file.*/F/alpha@3$',
-      },
-      'DF/D1' : {
-        'Tree conflict' :
-          '^local delete, incoming edit upon update'
-          + ' Source  left: .dir.*/DF/D1@2'
-          + ' Source right: .dir.*/DF/D1@3$',
-      },
-      ...
-    }
-
-  Note: expected_skip is only used in merge, i.e. using
-  deep_trees_run_tests_scheme_for_merge.
-  """
-
-  def __init__(self, name, local_action, incoming_action,
-                expected_output = None, expected_disk = None,
-                expected_status = None, expected_skip = None,
-                error_re_string = None,
-                commit_block_string = ".*remains in conflict.*",
-                expected_info = None):
-    self.name = name
-    self.local_action = local_action
-    self.incoming_action = incoming_action
-    self.expected_output = expected_output
-    self.expected_disk = expected_disk
-    self.expected_status = expected_status
-    self.expected_skip = expected_skip
-    self.error_re_string = error_re_string
-    self.commit_block_string = commit_block_string
-    self.expected_info = expected_info
-
-
-
-def deep_trees_run_tests_scheme_for_update(sbox, greater_scheme):
-  """
-  Runs a given list of tests for conflicts occuring at an update operation.
-
-  This function wants to save time and perform a number of different
-  test cases using just a single repository and performing just one commit
-  for all test cases instead of one for each test case.
-
-   1) Each test case is initialized in a separate subdir. Each subdir
-      again contains one set of "deep_trees", being separate container
-      dirs for different depths of trees (F, D, DF, DD, DDF, DDD).
-
-   2) A commit is performed across all test cases and depths.
-      (our initial state, -r2)
-
-   3) In each test case subdir (e.g. "local_tree_del_incoming_leaf_edit"),
-      its *incoming* action is performed (e.g. "deep_trees_leaf_edit"), in
-      each of the different depth trees (F, D, DF, ... DDD).
-
-   4) A commit is performed across all test cases and depths:
-      our "incoming" state is "stored away in the repository for now",
-      -r3.
-
-   5) All test case dirs and contained deep_trees are time-warped
-      (updated) back to -r2, the initial state containing deep_trees.
-
-   6) In each test case subdir (e.g. "local_tree_del_incoming_leaf_edit"),
-      its *local* action is performed (e.g. "deep_trees_leaf_del"), in
-      each of the different depth trees (F, D, DF, ... DDD).
-
-   7) An update to -r3 is performed across all test cases and depths.
-      This causes tree-conflicts between the "local" state in the working
-      copy and the "incoming" state from the repository, -r3.
-
-   8) A commit is performed in each separate container, to verify
-      that each tree-conflict indeed blocks a commit.
-
-  The sbox parameter is just the sbox passed to a test function. No need
-  to call sbox.build(), since it is called (once) within this function.
-
-  The "table" greater_scheme models all of the different test cases
-  that should be run using a single repository.
-
-  greater_scheme is a list of DeepTreesTestCase items, which define complete
-  test setups, so that they can be performed as described above.
-  """
-
-  j = os.path.join
-
-  if not sbox.is_built():
-    sbox.build()
-  wc_dir = sbox.wc_dir
-
-
-  # 1) create directories
-
-  for test_case in greater_scheme:
-    try:
-      add_deep_trees(sbox, test_case.name)
-    except:
-      logger.warn("ERROR IN: Tests scheme for update: "
-          + "while setting up deep trees in '%s'", test_case.name)
-      raise
-
-
-  # 2) commit initial state
-
-  main.run_svn(None, 'commit', '-m', 'initial state', wc_dir)
-
-
-  # 3) apply incoming changes
-
-  for test_case in greater_scheme:
-    try:
-      test_case.incoming_action(j(sbox.wc_dir, test_case.name))
-    except:
-      logger.warn("ERROR IN: Tests scheme for update: "
-          + "while performing incoming action in '%s'", test_case.name)
-      raise
-
-
-  # 4) commit incoming changes
-
-  main.run_svn(None, 'commit', '-m', 'incoming changes', wc_dir)
-
-
-  # 5) time-warp back to -r2
-
-  main.run_svn(None, 'update', '-r2', wc_dir)
-
-
-  # 6) apply local changes
-
-  for test_case in greater_scheme:
-    try:
-      test_case.local_action(j(wc_dir, test_case.name))
-    except:
-      logger.warn("ERROR IN: Tests scheme for update: "
-          + "while performing local action in '%s'", test_case.name)
-      raise
-
-
-  # 7) update to -r3, conflicting with incoming changes.
-  #    A lot of different things are expected.
-  #    Do separate update operations for each test case.
-
-  for test_case in greater_scheme:
-    try:
-      base = j(wc_dir, test_case.name)
-
-      x_out = test_case.expected_output
-      if x_out != None:
-        x_out = x_out.copy()
-        x_out.wc_dir = base
-
-      x_disk = test_case.expected_disk
-
-      x_status = test_case.expected_status
-      if x_status != None:
-        x_status.copy()
-        x_status.wc_dir = base
-
-      run_and_verify_update(base, x_out, x_disk, None,
-                            error_re_string = test_case.error_re_string)
-      if x_status:
-        run_and_verify_unquiet_status(base, x_status)
-
-      x_info = test_case.expected_info or {}
-      for path in x_info:
-        run_and_verify_info([x_info[path]], j(base, path))
-
-    except:
-      logger.warn("ERROR IN: Tests scheme for update: "
-          + "while verifying in '%s'", test_case.name)
-      raise
-
-
-  # 8) Verify that commit fails.
-
-  for test_case in greater_scheme:
-    try:
-      base = j(wc_dir, test_case.name)
-
-      x_status = test_case.expected_status
-      if x_status != None:
-        x_status.copy()
-        x_status.wc_dir = base
-
-      run_and_verify_commit(base, None, x_status,
-                            test_case.commit_block_string,
-                            base)
-    except:
-      logger.warn("ERROR IN: Tests scheme for update: "
-          + "while checking commit-blocking in '%s'", test_case.name)
-      raise
-
-
-
-def deep_trees_skipping_on_update(sbox, test_case, skip_paths,
-                                  chdir_skip_paths):
-  """
-  Create tree conflicts, then update again, expecting the existing tree
-  conflicts to be skipped.
-  SKIP_PATHS is a list of paths, relative to the "base dir", for which
-  "update" on the "base dir" should report as skipped.
-  CHDIR_SKIP_PATHS is a list of (target-path, skipped-path) pairs for which
-  an update of "target-path" (relative to the "base dir") should result in
-  "skipped-path" (relative to "target-path") being reported as skipped.
-  """
-
-  """FURTHER_ACTION is a function that will make a further modification to
-  each target, this being the modification that we expect to be skipped. The
-  function takes the "base dir" (the WC path to the test case directory) as
-  its only argument."""
-  further_action = deep_trees_tree_del_repos
-
-  j = os.path.join
-  wc_dir = sbox.wc_dir
-  base = j(wc_dir, test_case.name)
-
-  # Initialize: generate conflicts. (We do not check anything here.)
-  setup_case = DeepTreesTestCase(test_case.name,
-                                 test_case.local_action,
-                                 test_case.incoming_action,
-                                 None,
-                                 None,
-                                 None)
-  deep_trees_run_tests_scheme_for_update(sbox, [setup_case])
-
-  # Make a further change to each target in the repository so there is a new
-  # revision to update to. (This is r4.)
-  further_action(sbox.repo_url + '/' + test_case.name)
-
-  # Update whole working copy, expecting the nodes still in conflict to be
-  # skipped.
-
-  x_out = test_case.expected_output
-  if x_out != None:
-    x_out = x_out.copy()
-    x_out.wc_dir = base
-
-  x_disk = test_case.expected_disk
-
-  x_status = test_case.expected_status
-  if x_status != None:
-    x_status = x_status.copy()
-    x_status.wc_dir = base
-    # Account for nodes that were updated by further_action
-    x_status.tweak('', 'D', 'F', 'DD', 'DF', 'DDD', 'DDF', wc_rev=4)
-
-  run_and_verify_update(base, x_out, x_disk, None,
-                        error_re_string = test_case.error_re_string)
-
-  run_and_verify_unquiet_status(base, x_status)
-
-  # Try to update each in-conflict subtree. Expect a 'Skipped' output for
-  # each, and the WC status to be unchanged.
-  for path in skip_paths:
-    run_and_verify_update(j(base, path),
-                          wc.State(base, {path : Item(verb='Skipped')}),
-                          None, None)
-
-  run_and_verify_unquiet_status(base, x_status)
-
-  # Try to update each in-conflict subtree. Expect a 'Skipped' output for
-  # each, and the WC status to be unchanged.
-  # This time, cd to the subdir before updating it.
-  was_cwd = os.getcwd()
-  for path, skipped in chdir_skip_paths:
-    if isinstance(skipped, list):
-      expected_skip = {}
-      for p in skipped:
-        expected_skip[p] = Item(verb='Skipped')
-    else:
-      expected_skip = {skipped : Item(verb='Skipped')}
-    p = j(base, path)
-    run_and_verify_update(p,
-                          wc.State(p, expected_skip),
-                          None, None)
-  os.chdir(was_cwd)
-
-  run_and_verify_unquiet_status(base, x_status)
-
-  # Verify that commit still fails.
-  for path, skipped in chdir_skip_paths:
-
-    run_and_verify_commit(j(base, path), None, None,
-                          test_case.commit_block_string,
-                          base)
-
-  run_and_verify_unquiet_status(base, x_status)
-
-
-def deep_trees_run_tests_scheme_for_switch(sbox, greater_scheme):
-  """
-  Runs a given list of tests for conflicts occuring at a switch operation.
-
-  This function wants to save time and perform a number of different
-  test cases using just a single repository and performing just one commit
-  for all test cases instead of one for each test case.
-
-   1) Each test case is initialized in a separate subdir. Each subdir
-      again contains two subdirs: one "local" and one "incoming" for
-      the switch operation. These contain a set of deep_trees each.
-
-   2) A commit is performed across all test cases and depths.
-      (our initial state, -r2)
-
-   3) In each test case subdir's incoming subdir, the
-      incoming actions are performed.
-
-   4) A commit is performed across all test cases and depths. (-r3)
-
-   5) In each test case subdir's local subdir, the local actions are
-      performed. They remain uncommitted in the working copy.
-
-   6) In each test case subdir's local dir, a switch is performed to its
-      corresponding incoming dir.
-      This causes conflicts between the "local" state in the working
-      copy and the "incoming" state from the incoming subdir (still -r3).
-
-   7) A commit is performed in each separate container, to verify
-      that each tree-conflict indeed blocks a commit.
-
-  The sbox parameter is just the sbox passed to a test function. No need
-  to call sbox.build(), since it is called (once) within this function.
-
-  The "table" greater_scheme models all of the different test cases
-  that should be run using a single repository.
-
-  greater_scheme is a list of DeepTreesTestCase items, which define complete
-  test setups, so that they can be performed as described above.
-  """
-
-  j = os.path.join
-
-  if not sbox.is_built():
-    sbox.build()
-  wc_dir = sbox.wc_dir
-
-
-  # 1) Create directories.
-
-  for test_case in greater_scheme:
-    try:
-      base = j(sbox.wc_dir, test_case.name)
-      os.makedirs(base)
-      make_deep_trees(j(base, "local"))
-      make_deep_trees(j(base, "incoming"))
-      main.run_svn(None, 'add', base)
-    except:
-      logger.warn("ERROR IN: Tests scheme for switch: "
-          + "while setting up deep trees in '%s'", test_case.name)
-      raise
-
-
-  # 2) Commit initial state (-r2).
-
-  main.run_svn(None, 'commit', '-m', 'initial state', wc_dir)
-
-
-  # 3) Apply incoming changes
-
-  for test_case in greater_scheme:
-    try:
-      test_case.incoming_action(j(sbox.wc_dir, test_case.name, "incoming"))
-    except:
-      logger.warn("ERROR IN: Tests scheme for switch: "
-          + "while performing incoming action in '%s'", test_case.name)
-      raise
-
-
-  # 4) Commit all changes (-r3).
-
-  main.run_svn(None, 'commit', '-m', 'incoming changes', wc_dir)
-
-
-  # 5) Apply local changes in their according subdirs.
-
-  for test_case in greater_scheme:
-    try:
-      test_case.local_action(j(sbox.wc_dir, test_case.name, "local"))
-    except:
-      logger.warn("ERROR IN: Tests scheme for switch: "
-          + "while performing local action in '%s'", test_case.name)
-      raise
-
-
-  # 6) switch the local dir to the incoming url, conflicting with incoming
-  #    changes. A lot of different things are expected.
-  #    Do separate switch operations for each test case.
-
-  for test_case in greater_scheme:
-    try:
-      local = j(wc_dir, test_case.name, "local")
-      incoming = sbox.repo_url + "/" + test_case.name + "/incoming"
-
-      x_out = test_case.expected_output
-      if x_out != None:
-        x_out = x_out.copy()
-        x_out.wc_dir = local
-
-      x_disk = test_case.expected_disk
-
-      x_status = test_case.expected_status
-      if x_status != None:
-        x_status.copy()
-        x_status.wc_dir = local
-
-      run_and_verify_switch(local, local, incoming, x_out, x_disk, None,
-                            test_case.error_re_string, None, None, None,
-                            None, False, '--ignore-ancestry')
-      run_and_verify_unquiet_status(local, x_status)
-
-      x_info = test_case.expected_info or {}
-      for path in x_info:
-        run_and_verify_info([x_info[path]], j(local, path))
-    except:
-      logger.warn("ERROR IN: Tests scheme for switch: "
-          + "while verifying in '%s'", test_case.name)
-      raise
-
-
-  # 7) Verify that commit fails.
-
-  for test_case in greater_scheme:
-    try:
-      local = j(wc_dir, test_case.name, 'local')
-
-      x_status = test_case.expected_status
-      if x_status != None:
-        x_status.copy()
-        x_status.wc_dir = local
-
-      run_and_verify_commit(local, None, x_status,
-                            test_case.commit_block_string,
-                            local)
-    except:
-      logger.warn("ERROR IN: Tests scheme for switch: "
-          + "while checking commit-blocking in '%s'", test_case.name)
-      raise
-
-
-def deep_trees_run_tests_scheme_for_merge(sbox, greater_scheme,
-                                          do_commit_local_changes,
-                                          do_commit_conflicts=True,
-                                          ignore_ancestry=False):
-  """
-  Runs a given list of tests for conflicts occuring at a merge operation.
-
-  This function wants to save time and perform a number of different
-  test cases using just a single repository and performing just one commit
-  for all test cases instead of one for each test case.
-
-   1) Each test case is initialized in a separate subdir. Each subdir
-      initially contains another subdir, called "incoming", which
-      contains a set of deep_trees.
-
-   2) A commit is performed across all test cases and depths.
-      (a pre-initial state)
-
-   3) In each test case subdir, the "incoming" subdir is copied to "local",
-      via the `svn copy' command. Each test case's subdir now has two sub-
-      dirs: "local" and "incoming", initial states for the merge operation.
-
-   4) An update is performed across all test cases and depths, so that the
-      copies made in 3) are pulled into the wc.
-
-   5) In each test case's "incoming" subdir, the incoming action is
-      performed.
-
-   6) A commit is performed across all test cases and depths, to commit
-      the incoming changes.
-      If do_commit_local_changes is True, this becomes step 7 (swap steps).
-
-   7) In each test case's "local" subdir, the local_action is performed.
-      If do_commit_local_changes is True, this becomes step 6 (swap steps).
-      Then, in effect, the local changes are committed as well.
-
-   8) In each test case subdir, the "incoming" subdir is merged into the
-      "local" subdir.  If ignore_ancestry is True, then the merge is done
-      with the --ignore-ancestry option, so mergeinfo is neither considered
-      nor recorded.  This causes conflicts between the "local" state in the
-      working copy and the "incoming" state from the incoming subdir.
-
-   9) If do_commit_conflicts is True, then a commit is performed in each
-      separate container, to verify that each tree-conflict indeed blocks
-      a commit.
-
-  The sbox parameter is just the sbox passed to a test function. No need
-  to call sbox.build(), since it is called (once) within this function.
-
-  The "table" greater_scheme models all of the different test cases
-  that should be run using a single repository.
-
-  greater_scheme is a list of DeepTreesTestCase items, which define complete
-  test setups, so that they can be performed as described above.
-  """
-
-  j = os.path.join
-
-  if not sbox.is_built():
-    sbox.build()
-  wc_dir = sbox.wc_dir
-
-  # 1) Create directories.
-  for test_case in greater_scheme:
-    try:
-      base = j(sbox.wc_dir, test_case.name)
-      os.makedirs(base)
-      make_deep_trees(j(base, "incoming"))
-      main.run_svn(None, 'add', base)
-    except:
-      logger.warn("ERROR IN: Tests scheme for merge: "
-          + "while setting up deep trees in '%s'", test_case.name)
-      raise
-
-
-  # 2) Commit pre-initial state (-r2).
-
-  main.run_svn(None, 'commit', '-m', 'pre-initial state', wc_dir)
-
-
-  # 3) Copy "incoming" to "local".
-
-  for test_case in greater_scheme:
-    try:
-      base_url = sbox.repo_url + "/" + test_case.name
-      incoming_url = base_url + "/incoming"
-      local_url = base_url + "/local"
-      main.run_svn(None, 'cp', incoming_url, local_url, '-m',
-                   'copy incoming to local')
-    except:
-      logger.warn("ERROR IN: Tests scheme for merge: "
-          + "while copying deep trees in '%s'", test_case.name)
-      raise
-
-  # 4) Update to load all of the "/local" subdirs into the working copies.
-
-  try:
-    main.run_svn(None, 'up', sbox.wc_dir)
-  except:
-    logger.warn("ERROR IN: Tests scheme for merge: "
-          + "while updating local subdirs")
-    raise
-
-
-  # 5) Perform incoming actions
-
-  for test_case in greater_scheme:
-    try:
-      test_case.incoming_action(j(sbox.wc_dir, test_case.name, "incoming"))
-    except:
-      logger.warn("ERROR IN: Tests scheme for merge: "
-          + "while performing incoming action in '%s'", test_case.name)
-      raise
-
-
-  # 6) or 7) Commit all incoming actions
-
-  if not do_commit_local_changes:
-    try:
-      main.run_svn(None, 'ci', '-m', 'Committing incoming actions',
-                   sbox.wc_dir)
-    except:
-      logger.warn("ERROR IN: Tests scheme for merge: "
-          + "while committing incoming actions")
-      raise
-
-
-  # 7) or 6) Perform all local actions.
-
-  for test_case in greater_scheme:
-    try:
-      test_case.local_action(j(sbox.wc_dir, test_case.name, "local"))
-    except:
-      logger.warn("ERROR IN: Tests scheme for merge: "
-          + "while performing local action in '%s'", test_case.name)
-      raise
-
-
-  # 6) or 7) Commit all incoming actions
-
-  if do_commit_local_changes:
-    try:
-      main.run_svn(None, 'ci', '-m', 'Committing incoming and local actions',
-                   sbox.wc_dir)
-    except:
-      logger.warn("ERROR IN: Tests scheme for merge: "
-          + "while committing incoming and local actions")
-      raise
-
-
-  # 8) Merge all "incoming" subdirs to their respective "local" subdirs.
-  #    This creates conflicts between the local changes in the "local" wc
-  #    subdirs and the incoming states committed in the "incoming" subdirs.
-
-  for test_case in greater_scheme:
-    try:
-      local = j(sbox.wc_dir, test_case.name, "local")
-      incoming = sbox.repo_url + "/" + test_case.name + "/incoming"
-
-      x_out = test_case.expected_output
-      if x_out != None:
-        x_out = x_out.copy()
-        x_out.wc_dir = local
-
-      x_disk = test_case.expected_disk
-
-      x_status = test_case.expected_status
-      if x_status != None:
-        x_status.copy()
-        x_status.wc_dir = local
-
-      x_skip = test_case.expected_skip
-      if x_skip != None:
-        x_skip.copy()
-        x_skip.wc_dir = local
-
-      varargs = (local,)
-      if ignore_ancestry:
-        varargs = varargs + ('--ignore-ancestry',)
-
-      run_and_verify_merge(local, None, None, incoming, None,
-                           x_out, None, None, x_disk, None, x_skip,
-                           test_case.error_re_string,
-                           None, None, None, None,
-                           False, False, *varargs)
-      run_and_verify_unquiet_status(local, x_status)
-    except:
-      logger.warn("ERROR IN: Tests scheme for merge: "
-          + "while verifying in '%s'", test_case.name)
-      raise
-
-
-  # 9) Verify that commit fails.
-
-  if do_commit_conflicts:
-    for test_case in greater_scheme:
-      try:
-        local = j(wc_dir, test_case.name, 'local')
-
-        x_status = test_case.expected_status
-        if x_status != None:
-          x_status.copy()
-          x_status.wc_dir = local
-
-        run_and_verify_commit(local, None, x_status,
-                              test_case.commit_block_string,
-                              local)
-      except:
-        logger.warn("ERROR IN: Tests scheme for merge: "
-            + "while checking commit-blocking in '%s'", test_case.name)
-        raise
-
-

Modified: subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/main.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/main.py?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/main.py (original)
+++ subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/main.py Wed Feb 13 10:21:33 2013
@@ -1186,6 +1186,26 @@ def merge_notify_line(revstart=None, rev
       return "--- Merging %sr%ld through r%ld into '%s':\n" \
              % (from_foreign_phrase, revstart, revend, target_re)
 
+def summary_of_conflicts(text_conflicts=0, prop_conflicts=0,
+                         tree_conflicts=0, skipped_paths=0):
+  """Return a list of lines corresponding to the summary of conflicts and
+     skipped paths that is printed by merge and update and switch.  If all
+     parameters are zero, return an empty list.
+  """
+  lines = []
+  if text_conflicts or prop_conflicts or tree_conflicts or skipped_paths:
+    lines.append("Summary of conflicts:\n")
+    if text_conflicts:
+      lines.append("  Text conflicts: %d\n" % text_conflicts)
+    if prop_conflicts:
+      lines.append("  Property conflicts: %d\n" % prop_conflicts)
+    if tree_conflicts:
+      lines.append("  Tree conflicts: %d\n" % tree_conflicts)
+    if skipped_paths:
+      lines.append("  Skipped paths: %d\n" % skipped_paths)
+
+  return lines
+
 
 def make_log_msg():
   "Conjure up a log message based on the calling test."

Modified: subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/sandbox.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/sandbox.py?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/sandbox.py (original)
+++ subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/sandbox.py Wed Feb 13 10:21:33 2013
@@ -191,6 +191,12 @@ class Sandbox:
        path WC_DIR if supplied."""
     return [self.ospath(rp, wc_dir) for rp in relpaths]
 
+  def path(self, relpath, wc_dir=None):
+    """Return RELPATH converted to an path relative to the WC dir
+       of this sbox, or relative to WC_DIR if supplied, but always
+       using '/' as directory separator."""
+    return self.ospath(relpath, wc_dir=wc_dir).replace(os.path.sep, '/')
+
   def redirected_root_url(self, temporary=False):
     """If TEMPORARY is set, return the URL which should be configured
        to temporarily redirect to the root of this repository;

Modified: subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/tree.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/tree.py?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/tree.py (original)
+++ subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/tree.py Wed Feb 13 10:21:33 2013
@@ -838,11 +838,10 @@ def build_tree_from_commit(lines):
 #             IFF columns non-empty.
 #
 
-def build_tree_from_status(lines, wc_dir_name=None):
+def build_tree_from_status(lines):
   "Return a tree derived by parsing the output LINES from 'st -vuq'."
 
-  return svntest.wc.State.from_status(lines,
-                                       wc_dir_name=wc_dir_name).old_tree()
+  return svntest.wc.State.from_status(lines).old_tree()
 
 
 # Parse merge "skipped" output

Modified: subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/verify.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/verify.py?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/verify.py (original)
+++ subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/verify.py Wed Feb 13 10:21:33 2013
@@ -96,130 +96,72 @@ def createExpectedOutput(expected, outpu
     raise SVNIncorrectDatatype("Unexpected type for '%s' data" % output_type)
   return expected
 
-class ExpectedOutput:
-  """Contains expected output, and performs comparisons."""
+class ExpectedOutput(object):
+  """Matches an ordered list of lines.
 
-  is_regex = False
-  is_unordered = False
+     If MATCH_ALL is True, the expected lines must match all the actual
+     lines, one-to-one, in the same order.  If MATCH_ALL is False, the
+     expected lines must match a subset of the actual lines, one-to-one,
+     in the same order, ignoring any other actual lines among the
+     matching ones.
+  """
 
-  def __init__(self, output, match_all=True):
-    """Initialize the expected output to OUTPUT which is a string, or a list
-    of strings, or None meaning an empty list. If MATCH_ALL is True, the
-    expected strings will be matched with the actual strings, one-to-one, in
-    the same order. If False, they will be matched with a subset of the
-    actual strings, one-to-one, in the same order, ignoring any other actual
-    strings among the matching ones."""
-    self.output = output
+  def __init__(self, expected, match_all=True):
+    """Initialize the expected output to EXPECTED which is a string, or
+       a list of strings.
+    """
+    assert expected is not None
+    self.expected = expected
     self.match_all = match_all
 
   def __str__(self):
-    return str(self.output)
+    return str(self.expected)
 
   def __cmp__(self, other):
-    raise Exception('badness')
-
-  def matches(self, other, except_re=None):
-    """Return whether SELF.output matches OTHER (which may be a list
-    of newline-terminated lines, or a single string).  Either value
-    may be None."""
-    if self.output is None:
-      expected = []
-    else:
-      expected = self.output
-    if other is None:
-      actual = []
-    else:
-      actual = other
+    raise TypeError("ExpectedOutput does not implement direct comparison; "
+                    "see the 'matches()' method")
 
-    if not isinstance(actual, list):
-      actual = [actual]
+  def matches(self, actual):
+    """Return whether SELF matches ACTUAL (which may be a list
+       of newline-terminated lines, or a single string).
+    """
+    assert actual is not None
+    expected = self.expected
     if not isinstance(expected, list):
       expected = [expected]
+    if not isinstance(actual, list):
+      actual = [actual]
 
-    if except_re:
-      return self.matches_except(expected, actual, except_re)
-    else:
-      return self.is_equivalent_list(expected, actual)
-
-  def matches_except(self, expected, actual, except_re):
-    "Return whether EXPECTED and ACTUAL match except for except_re."
-    if not self.is_regex:
-      i_expected = 0
-      i_actual = 0
-      while i_expected < len(expected) and i_actual < len(actual):
-        if re.match(except_re, actual[i_actual]):
-          i_actual += 1
-        elif re.match(except_re, expected[i_expected]):
-          i_expected += 1
-        elif expected[i_expected] == actual[i_actual]:
-          i_expected += 1
-          i_actual += 1
-        else:
-          return False
-      if i_expected == len(expected) and i_actual == len(actual):
-            return True
-      return False
-    else:
-      raise Exception("is_regex and except_re are mutually exclusive")
-
-  def is_equivalent_list(self, expected, actual):
-    "Return whether EXPECTED and ACTUAL are equivalent."
-    if not self.is_regex:
-      if self.match_all:
-        # The EXPECTED lines must match the ACTUAL lines, one-to-one, in
-        # the same order.
-        return expected == actual
-
-      # The EXPECTED lines must match a subset of the ACTUAL lines,
-      # one-to-one, in the same order, with zero or more other ACTUAL
-      # lines interspersed among the matching ACTUAL lines.
-      i_expected = 0
-      for actual_line in actual:
-        if expected[i_expected] == actual_line:
-          i_expected += 1
-          if i_expected == len(expected):
-            return True
-      return False
-
-    expected_re = expected[0]
-    # If we want to check that every line matches the regexp
-    # assume they all match and look for any that don't.  If
-    # only one line matching the regexp is enough, assume none
-    # match and look for even one that does.
     if self.match_all:
-      all_lines_match_re = True
-    else:
-      all_lines_match_re = False
-
-    # If a regex was provided assume that we actually require
-    # some output. Fail if we don't have any.
-    if len(actual) == 0:
-      return False
+      return expected == actual
 
+    i_expected = 0
     for actual_line in actual:
-      if self.match_all:
-        if not re.match(expected_re, actual_line):
-          return False
-      else:
-        # As soon an actual_line matches something, then we're good.
-        if re.match(expected_re, actual_line):
+      if expected[i_expected] == actual_line:
+        i_expected += 1
+        if i_expected == len(expected):
           return True
-
-    return all_lines_match_re
+    return False
 
   def display_differences(self, message, label, actual):
-    """Delegate to the display_lines() routine with the appropriate
-    args.  MESSAGE is ignored if None."""
-    display_lines(message, label, self.output, actual,
-                  self.is_regex, self.is_unordered)
+    """Show the differences between the expected and ACTUAL lines. Print
+       MESSAGE unless it is None, the expected lines, the ACTUAL lines,
+       and a diff, all labeled with LABEL.
+    """
+    display_lines(message, self.expected, actual, label, label)
+    display_lines_diff(self.expected, actual, label, label)
 
 
 class AnyOutput(ExpectedOutput):
-  """Matches any non-empty output."""
+  """Matches any non-empty output.
+  """
+
   def __init__(self):
-    ExpectedOutput.__init__(self, None, False)
+    ExpectedOutput.__init__(self, [], False)
+
+  def matches(self, actual):
+    assert actual is not None
 
-  def is_equivalent_list(self, ignored, actual):
     if len(actual) == 0:
       # No actual output. No match.
       return False
@@ -238,60 +180,139 @@ class AnyOutput(ExpectedOutput):
 
 
 class RegexOutput(ExpectedOutput):
-  is_regex = True
+  """Matches a single regular expression.
 
+     If MATCH_ALL is true, every actual line must match the RE.  If
+     MATCH_ALL is false, at least one actual line must match the RE.  In
+     any case, there must be at least one line of actual output.
+  """
 
-class UnorderedOutput(ExpectedOutput):
-  """Marks unordered output, and performs comparisons."""
+  def __init__(self, expected, match_all=True):
+    "EXPECTED is a regular expression string."
+    assert isinstance(expected, str)
+    ExpectedOutput.__init__(self, expected, match_all)
+    self.expected_re = re.compile(expected)
 
-  is_unordered = True
+  def matches(self, actual):
+    assert actual is not None
 
-  def __cmp__(self, other):
-    raise Exception('badness')
+    if not isinstance(actual, list):
+      actual = [actual]
+
+    # If a regex was provided assume that we require some actual output.
+    # Fail if we don't have any.
+    if len(actual) == 0:
+      return False
 
-  def matches_except(self, expected, actual, except_re):
-    assert type(actual) == type([]) # ### if this trips: fix it!
-    return self.is_equivalent_list([l for l in expected if not except_re.match(l)],
-                                   [l for l in actual if not except_re.match(l)])
+    if self.match_all:
+      return all(self.expected_re.match(line) for line in actual)
+    else:
+      return any(self.expected_re.match(line) for line in actual)
+
+  def display_differences(self, message, label, actual):
+    display_lines(message, self.expected, actual, label + ' (regexp)', label)
+
+
+class RegexListOutput(ExpectedOutput):
+  """Matches an ordered list of regular expressions.
 
-  def is_equivalent_list(self, expected, actual):
-    "Disregard the order of ACTUAL lines during comparison."
+     If MATCH_ALL is True, the expressions must match all the actual
+     lines, one-to-one, in the same order.  If MATCH_ALL is False, the
+     expressions must match a subset of the actual lines, one-to-one, in
+     the same order, ignoring any other actual lines among the matching
+     ones.
 
-    e_set = set(expected)
-    a_set = set(actual)
+     In any case, there must be at least one line of actual output.
+  """
+
+  def __init__(self, expected, match_all=True):
+    "EXPECTED is a list of regular expression strings."
+    assert isinstance(expected, list) and expected != []
+    ExpectedOutput.__init__(self, expected, match_all)
+    self.expected_res = [re.compile(e) for e in expected]
+
+  def matches(self, actual):
+    assert actual is not None
+    if not isinstance(actual, list):
+      actual = [actual]
 
     if self.match_all:
-      if len(e_set) != len(a_set):
-        return False
-      if self.is_regex:
-        for expect_re in e_set:
-          for actual_line in a_set:
-            if re.match(expect_re, actual_line):
-              a_set.remove(actual_line)
-              break
-          else:
-            # One of the regexes was not found
-            return False
-        return True
+      return (len(self.expected_res) == len(actual) and
+              all(e.match(a) for e, a in zip(self.expected_res, actual)))
 
-      # All expected lines must be in the output.
-      return e_set == a_set
+    i_expected = 0
+    for actual_line in actual:
+      if self.expected_res[i_expected].match(actual_line):
+        i_expected += 1
+        if i_expected == len(self.expected_res):
+          return True
+    return False
 
-    if self.is_regex:
-      # If any of the expected regexes are in the output, then we match.
-      for expect_re in e_set:
-        for actual_line in a_set:
-          if re.match(expect_re, actual_line):
-            return True
-      return False
+  def display_differences(self, message, label, actual):
+    display_lines(message, self.expected, actual, label + ' (regexp)', label)
+
+
+class UnorderedOutput(ExpectedOutput):
+  """Matches an unordered list of lines.
+
+     The expected lines must match all the actual lines, one-to-one, in
+     any order.
+  """
+
+  def __init__(self, expected):
+    assert isinstance(expected, list)
+    ExpectedOutput.__init__(self, expected)
+
+  def matches(self, actual):
+    if not isinstance(actual, list):
+      actual = [actual]
+
+    return sorted(self.expected) == sorted(actual)
+
+  def display_differences(self, message, label, actual):
+    display_lines(message, self.expected, actual, label + ' (unordered)', label)
+    display_lines_diff(self.expected, actual, label + ' (unordered)', label)
+
+
+class UnorderedRegexListOutput(ExpectedOutput):
+  """Matches an unordered list of regular expressions.
+
+     The expressions must match all the actual lines, one-to-one, in any
+     order.
+
+     Note: This can give a false negative result (no match) when there is
+     an actual line that matches multiple expressions and a different
+     actual line that matches some but not all of those same
+     expressions.  The implementation matches each expression in turn to
+     the first unmatched actual line that it can match, and does not try
+     all the permutations when there are multiple possible matches.
+  """
 
-    # If any of the expected lines are in the output, then we match.
-    return len(e_set.intersection(a_set)) > 0
+  def __init__(self, expected):
+    assert isinstance(expected, list)
+    ExpectedOutput.__init__(self, expected)
 
+  def matches(self, actual):
+    assert actual is not None
+    if not isinstance(actual, list):
+      actual = [actual]
 
-class UnorderedRegexOutput(UnorderedOutput, RegexOutput):
-  is_regex = True
-  is_unordered = True
+    if len(self.expected) != len(actual):
+      return False
+    for e in self.expected:
+      expect_re = re.compile(e)
+      for actual_line in actual:
+        if expect_re.match(actual_line):
+          actual.remove(actual_line)
+          break
+      else:
+        # One of the regexes was not found
+        return False
+    return True
+
+  def display_differences(self, message, label, actual):
+    display_lines(message, self.expected, actual,
+                  label + ' (regexp) (unordered)', label)
 
 
 ######################################################################
@@ -309,55 +330,55 @@ def display_trees(message, label, expect
     svntest.tree.dump_tree(actual)
 
 
-def display_lines(message, label, expected, actual, expected_is_regexp=None,
-                  expected_is_unordered=None):
+def display_lines_diff(expected, actual, expected_label, actual_label):
+  """Print a unified diff between EXPECTED (labeled with EXPECTED_LABEL)
+     and ACTUAL (labeled with ACTUAL_LABEL).
+     Each of EXPECTED and ACTUAL is a string or a list of strings.
+  """
+  logger.warn('DIFF ' + expected_label + ':')
+  for x in unified_diff(expected, actual,
+                        fromfile='EXPECTED ' + expected_label,
+                        tofile='ACTUAL ' + actual_label):
+    logger.warn('| ' + x.rstrip())
+
+def display_lines(message, expected, actual,
+                  expected_label, actual_label=None):
   """Print MESSAGE, unless it is None, then print EXPECTED (labeled
-  with LABEL) followed by ACTUAL (also labeled with LABEL).
-  Both EXPECTED and ACTUAL may be strings or lists of strings."""
+     with EXPECTED_LABEL) followed by ACTUAL (labeled with ACTUAL_LABEL).
+     Each of EXPECTED and ACTUAL is a string or a list of strings.
+  """
   if message is not None:
     logger.warn(message)
+
+  if type(expected) is str:
+    expected = [expected]
+  if type(actual) is str:
+    actual = [actual]
+  if actual_label is None:
+    actual_label = expected_label
   if expected is not None:
-    output = 'EXPECTED %s' % label
-    if expected_is_regexp:
-      output += ' (regexp)'
-      expected = [expected + '\n']
-    if expected_is_unordered:
-      output += ' (unordered)'
-    output += ':'
-    logger.warn(output)
+    logger.warn('EXPECTED %s:', expected_label)
     for x in expected:
-      sys.stdout.write(x)
+      logger.warn('| ' + x.rstrip())
   if actual is not None:
-    logger.warn('ACTUAL %s:', label)
+    logger.warn('ACTUAL %s:', actual_label)
     for x in actual:
-      sys.stdout.write(x)
-
-  # Additionally print unified diff
-  if not expected_is_regexp:
-    logger.warn('DIFF ' + ' '.join(output.split(' ')[1:]))
-
-    if type(expected) is str:
-      expected = [expected]
-
-    if type(actual) is str:
-      actual = [actual]
-
-    for x in unified_diff(expected, actual,
-                          fromfile="EXPECTED %s" % label,
-                          tofile="ACTUAL %s" % label):
-      sys.stdout.write(x)
+      logger.warn('| ' + x.rstrip())
 
 def compare_and_display_lines(message, label, expected, actual,
-                              raisable=None, except_re=None):
+                              raisable=None):
   """Compare two sets of output lines, and print them if they differ,
   preceded by MESSAGE iff not None.  EXPECTED may be an instance of
-  ExpectedOutput (and if not, it is wrapped as such).  RAISABLE is an
+  ExpectedOutput (and if not, it is wrapped as such).  ACTUAL may be a
+  list of newline-terminated lines, or a single string.  RAISABLE is an
   exception class, an instance of which is thrown if ACTUAL doesn't
   match EXPECTED."""
   if raisable is None:
     raisable = svntest.main.SVNLineUnequal
   ### It'd be nicer to use createExpectedOutput() here, but its
   ### semantics don't match all current consumers of this function.
+  assert expected is not None
+  assert actual is not None
   if not isinstance(expected, ExpectedOutput):
     expected = ExpectedOutput(expected)
 
@@ -365,7 +386,7 @@ def compare_and_display_lines(message, l
     actual = [actual]
   actual = svntest.main.filter_dbg(actual)
 
-  if not expected.matches(actual, except_re):
+  if not expected.matches(actual):
     expected.display_differences(message, label, actual)
     raise raisable
 
@@ -403,8 +424,7 @@ def verify_exit_code(message, actual, ex
   not None) and raise an exception."""
 
   if expected != actual:
-    display_lines(message, "Exit Code",
-                  str(expected) + '\n', str(actual) + '\n')
+    display_lines(message, str(expected), str(actual), "Exit Code")
     raise raisable
 
 # A simple dump file parser.  While sufficient for the current

Modified: subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/wc.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/wc.py?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/wc.py (original)
+++ subversion/branches/verify-keep-going/subversion/tests/cmdline/svntest/wc.py Wed Feb 13 10:21:33 2013
@@ -261,11 +261,22 @@ class State:
 
     base = to_relpath(os.path.normpath(self.wc_dir))
 
-    # TODO: We should probably normalize the paths in moved_from and moved_to.
-
     desc = dict([(repos_join(base, path), item)
                  for path, item in self.desc.items()])
 
+    for path, item in desc.copy().items():
+      if item.moved_from or item.moved_to:
+        i = item.copy()
+
+        if i.moved_from:
+          i.moved_from = to_relpath(os.path.normpath(
+                                        repos_join(base, i.moved_from)))
+        if i.moved_to:
+          i.moved_to = to_relpath(os.path.normpath(
+                                        repos_join(base, i.moved_to)))
+
+        desc[path] = i
+
     return State('', desc)
 
   def compare(self, other):
@@ -423,7 +434,7 @@ class State:
     return not self.__eq__(other)
 
   @classmethod
-  def from_status(cls, lines, wc_dir_name=None):
+  def from_status(cls, lines):
     """Create a State object from 'svn status' output."""
 
     def not_space(value):
@@ -445,21 +456,12 @@ class State:
         if ex_match:
           if ex_match.group('moved_from'):
             path = ex_match.group('moved_from')
-            if wc_dir_name and path.startswith(wc_dir_name + os.path.sep):
-              path = path[len(wc_dir_name) + 1:]
-            
             last.tweak(moved_from = to_relpath(path))
           elif ex_match.group('moved_to'):
             path = ex_match.group('moved_to')
-            if wc_dir_name and path.startswith(wc_dir_name + os.path.sep):
-              path = path[len(wc_dir_name) + 1:]
-            
             last.tweak(moved_to = to_relpath(path))
           elif ex_match.group('swapped_with'):
             path = ex_match.group('swapped_with')
-            if wc_dir_name and path.startswith(wc_dir_name + os.path.sep):
-              path = path[len(wc_dir_name) + 1:]
-            
             last.tweak(moved_to = to_relpath(path))
             last.tweak(moved_from = to_relpath(path))
 
@@ -472,6 +474,8 @@ class State:
       prev_treeconflict = None
 
       path = to_relpath(match.group('path'))
+      if path == '.':
+        path = ''
       if path in desc:
         prev_status = desc[path].status
         prev_treeconflict = desc[path].treeconflict
@@ -790,15 +794,14 @@ class StateItem:
     if not isinstance(other, StateItem):
       return False
     v_self = dict([(k, v) for k, v in vars(self).items()
-                   if not k.startswith('_')])
+                   if not k.startswith('_') and not k.startswith('entry_')])
     v_other = dict([(k, v) for k, v in vars(other).items()
-                    if not k.startswith('_')])
-    if self.treeconflict is None:
-      v_other = v_other.copy()
-      v_other['treeconflict'] = None
-    if other.treeconflict is None:
-      v_self = v_self.copy()
-      v_self['treeconflict'] = None
+                    if not k.startswith('_') and not k.startswith('entry_')])
+
+    if self.wc_rev == '0' and self.status == 'A ':
+      v_self['wc_rev'] = '-'
+    if other.wc_rev == '0' and other.status == 'A ':
+      v_other['wc_rev'] = '-'
     return v_self == v_other
 
   def __ne__(self, other):

Modified: subversion/branches/verify-keep-going/subversion/tests/cmdline/switch_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/tests/cmdline/switch_tests.py?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/tests/cmdline/switch_tests.py (original)
+++ subversion/branches/verify-keep-going/subversion/tests/cmdline/switch_tests.py Wed Feb 13 10:21:33 2013
@@ -29,7 +29,7 @@ import shutil, re, os
 
 # Our testing module
 import svntest
-from svntest import verify, actions, main
+from svntest import verify, actions, main, deeptrees
 
 # (abbreviation)
 Skip = svntest.testcase.Skip_deco
@@ -2283,25 +2283,25 @@ def tolerate_local_mods(sbox):
 # parent directory.
 
 # convenience definitions
-leaf_edit = svntest.actions.deep_trees_leaf_edit
-tree_del = svntest.actions.deep_trees_tree_del
-leaf_del = svntest.actions.deep_trees_leaf_del
+leaf_edit = svntest.deeptrees.deep_trees_leaf_edit
+tree_del = svntest.deeptrees.deep_trees_tree_del
+leaf_del = svntest.deeptrees.deep_trees_leaf_del
 
-disk_after_leaf_edit = svntest.actions.deep_trees_after_leaf_edit
-disk_after_leaf_del = svntest.actions.deep_trees_after_leaf_del
-disk_after_tree_del = svntest.actions.deep_trees_after_tree_del
+disk_after_leaf_edit = svntest.deeptrees.deep_trees_after_leaf_edit
+disk_after_leaf_del = svntest.deeptrees.deep_trees_after_leaf_del
+disk_after_tree_del = svntest.deeptrees.deep_trees_after_tree_del
 
-disk_empty_dirs = svntest.actions.deep_trees_empty_dirs
+disk_empty_dirs = svntest.deeptrees.deep_trees_empty_dirs
 
-deep_trees_conflict_output = svntest.actions.deep_trees_conflict_output
+deep_trees_conflict_output = svntest.deeptrees.deep_trees_conflict_output
 deep_trees_conflict_output_skipped = \
-    svntest.actions.deep_trees_conflict_output_skipped
+    svntest.deeptrees.deep_trees_conflict_output_skipped
 deep_trees_status_local_tree_del = \
-    svntest.actions.deep_trees_status_local_tree_del
+    svntest.deeptrees.deep_trees_status_local_tree_del
 deep_trees_status_local_leaf_edit = \
-    svntest.actions.deep_trees_status_local_leaf_edit
+    svntest.deeptrees.deep_trees_status_local_leaf_edit
 
-DeepTreesTestCase = svntest.actions.DeepTreesTestCase
+DeepTreesTestCase = svntest.deeptrees.DeepTreesTestCase
 
 j = os.path.join
 
@@ -2385,7 +2385,7 @@ def tree_conflicts_on_switch_1_1(sbox):
     },
   }
 
-  svntest.actions.deep_trees_run_tests_scheme_for_switch(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_switch(sbox,
     [ DeepTreesTestCase("local_tree_del_incoming_leaf_edit",
                         tree_del,
                         leaf_edit,
@@ -2483,7 +2483,7 @@ def tree_conflicts_on_switch_1_2(sbox):
     },
   }
 
-  svntest.actions.deep_trees_run_tests_scheme_for_switch(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_switch(sbox,
     [ DeepTreesTestCase("local_tree_del_incoming_leaf_del",
                         tree_del,
                         leaf_del,
@@ -2571,7 +2571,7 @@ def tree_conflicts_on_switch_2_1(sbox):
   ### local-copy from its original revision. however, right now, we cannot
   ### denote that delta is a local-add rather than a child of that D/D1 copy.
   ### thus, it appears in the status output as a (M)odified child.
-  svntest.actions.deep_trees_run_tests_scheme_for_switch(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_switch(sbox,
     [ DeepTreesTestCase("local_leaf_edit_incoming_tree_del",
                         leaf_edit,
                         tree_del,
@@ -2593,7 +2593,7 @@ def tree_conflicts_on_switch_2_2(sbox):
 
   expected_disk = disk_empty_dirs.copy()
 
-  expected_status = svntest.actions.deep_trees_virginal_state.copy()
+  expected_status = svntest.deeptrees.deep_trees_virginal_state.copy()
   expected_status.add({'' : Item(),
                        'F/alpha' : Item()})
   expected_status.tweak(contents=None, status='  ', wc_rev=3)
@@ -2665,7 +2665,7 @@ def tree_conflicts_on_switch_2_2(sbox):
     },
   }
 
-  svntest.actions.deep_trees_run_tests_scheme_for_switch(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_switch(sbox,
     [ DeepTreesTestCase("local_leaf_del_incoming_tree_del",
                         leaf_del,
                         tree_del,
@@ -2754,7 +2754,7 @@ def tree_conflicts_on_switch_3(sbox):
     },
   }
 
-  svntest.actions.deep_trees_run_tests_scheme_for_switch(sbox,
+  svntest.deeptrees.deep_trees_run_tests_scheme_for_switch(sbox,
     [ DeepTreesTestCase("local_tree_del_incoming_tree_del",
                         tree_del,
                         tree_del,

Modified: subversion/branches/verify-keep-going/subversion/tests/cmdline/tree_conflict_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/tests/cmdline/tree_conflict_tests.py?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/tests/cmdline/tree_conflict_tests.py (original)
+++ subversion/branches/verify-keep-going/subversion/tests/cmdline/tree_conflict_tests.py Wed Feb 13 10:21:33 2013
@@ -689,7 +689,6 @@ def merge_dir_mod_onto_not_dir(sbox):
   test_tc_merge(sbox2, d_mods, wc_scen = d_dels + d_moves)
 
 # Test for issue #3150 'tree conflicts with directories as victims'.
-@XFail()
 @Issue(3150)
 def merge_dir_del_onto_not_same(sbox):
   "merge dir: del/rpl/mv onto not-same"
@@ -1376,9 +1375,7 @@ def actual_only_node_behaviour(sbox):
   # update (up)
   expected_stdout = [
    "Skipped '%s' -- Node remains in conflict\n" % sbox.ospath('A/foo'),
-   "Summary of conflicts:\n",
-   "  Skipped paths: 1\n",
-  ]
+  ] + svntest.main.summary_of_conflicts(skipped_paths=1)
   expected_stderr = []
   run_and_verify_svn(None, expected_stdout, expected_stderr,
                      "update", foo_path)