You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/08/10 20:06:33 UTC

svn commit: r984153 [36/39] - in /subversion/branches/ignore-mergeinfo: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/hudson/ build/hudson/jobs/subversion-1.6.x-solaris/ build/hudson/jobs/subversion-1.6.x-ubuntu/ build/hu...

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/special_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/special_tests.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/special_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/special_tests.py Tue Aug 10 18:06:17 2010
@@ -3,7 +3,7 @@
 #  special_tests.py:  testing special and reserved file handling
 #
 #  Subversion is a tool for revision control.
-#  See http://subversion.tigris.org for more information.
+#  See http://subversion.apache.org for more information.
 #
 # ====================================================================
 #    Licensed to the Apache Software Foundation (ASF) under one

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/stat_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/stat_tests.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/stat_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/stat_tests.py Tue Aug 10 18:06:17 2010
@@ -3,7 +3,7 @@
 #  stat_tests.py:  testing the svn stat command
 #
 #  Subversion is a tool for revision control.
-#  See http://subversion.tigris.org for more information.
+#  See http://subversion.apache.org for more information.
 #
 # ====================================================================
 #    Licensed to the Apache Software Foundation (ASF) under one
@@ -981,6 +981,8 @@ def status_add_plus_conflict(sbox):
     "?       " + os.path.join(wc_dir, "trunk", "file.merge-right.r5") + "\n",
     "?       " + os.path.join(wc_dir, "trunk", "file.working") + "\n",
     "C  +    " + os.path.join(wc_dir, "trunk", "file") + "\n",
+    "Summary of conflicts:\n",
+    "  Text conflicts: 1\n",
   ]
   if svntest.main.server_has_mergeinfo():
     lines.append(" M      " + os.path.join(wc_dir, "trunk") + "\n")
@@ -1625,6 +1627,8 @@ def status_with_tree_conflicts(sbox):
           "      >   local edit, incoming delete upon update\n",
           "!     C %s\n" % tau,
           "      >   local delete, incoming delete upon update\n",
+          "Summary of conflicts:\n",
+          "  Tree conflicts: 3\n",
           ])
 
   svntest.actions.run_and_verify_svn(None,
@@ -1641,6 +1645,8 @@ def status_with_tree_conflicts(sbox):
           "      >   local edit, incoming delete upon update\n",
           "!     C                                  %s\n" % tau,
           "      >   local delete, incoming delete upon update\n",
+          "Summary of conflicts:\n",
+          "  Tree conflicts: 3\n",
           ])
 
 

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnadmin_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnadmin_tests.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnadmin_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnadmin_tests.py Tue Aug 10 18:06:17 2010
@@ -3,7 +3,7 @@
 #  svnadmin_tests.py:  testing the 'svnadmin' tool.
 #
 #  Subversion is a tool for revision control.
-#  See http://subversion.tigris.org for more information.
+#  See http://subversion.apache.org for more information.
 #
 # ====================================================================
 #    Licensed to the Apache Software Foundation (ASF) under one
@@ -33,6 +33,7 @@ import sys
 import svntest
 from svntest.verify import SVNExpectedStdout, SVNExpectedStderr
 from svntest.verify import SVNUnexpectedStderr
+from svntest.main import SVN_PROP_MERGEINFO
 
 # (abbreviation)
 Skip = svntest.testcase.Skip
@@ -812,22 +813,22 @@ def reflect_dropped_renumbered_revs(sbox
                              '/toplevel')
 
   # Verify the svn:mergeinfo properties
-  svntest.actions.run_and_verify_svn(None, ["/trunk:1-4\n"],
+  svntest.actions.run_and_verify_svn(None, ["/trunk:2-4\n"],
                                      [], 'propget', 'svn:mergeinfo',
                                      sbox.repo_url + '/branch2')
   svntest.actions.run_and_verify_svn(None, ["/branch1:5-9\n"],
                                      [], 'propget', 'svn:mergeinfo',
                                      sbox.repo_url + '/trunk')
-  svntest.actions.run_and_verify_svn(None, ["/toplevel/trunk:1-13\n"],
+  svntest.actions.run_and_verify_svn(None, ["/toplevel/trunk:11-13\n"],
                                      [], 'propget', 'svn:mergeinfo',
                                      sbox.repo_url + '/toplevel/branch2')
   svntest.actions.run_and_verify_svn(None, ["/toplevel/branch1:14-18\n"],
                                      [], 'propget', 'svn:mergeinfo',
                                      sbox.repo_url + '/toplevel/trunk')
-  svntest.actions.run_and_verify_svn(None, ["/toplevel/trunk:1-12\n"],
+  svntest.actions.run_and_verify_svn(None, ["/toplevel/trunk:11-12\n"],
                                      [], 'propget', 'svn:mergeinfo',
                                      sbox.repo_url + '/toplevel/branch1')
-  svntest.actions.run_and_verify_svn(None, ["/trunk:1-3\n"],
+  svntest.actions.run_and_verify_svn(None, ["/trunk:2-3\n"],
                                      [], 'propget', 'svn:mergeinfo',
                                      sbox.repo_url + '/branch1')
 
@@ -925,6 +926,274 @@ def create_in_repo_subdir(sbox):
   # No SVNRepositoryCreateFailure raised?
   raise svntest.Failure
 
+def verify_with_invalid_revprops(sbox):
+  "svnadmin verify detects invalid revprops file"
+
+  repo_dir = sbox.repo_dir
+
+  svntest.main.safe_rmtree(repo_dir, 1)
+
+  # This should succeed
+  svntest.main.create_repos(repo_dir)
+
+  # Run a test verify
+  exit_code, output, errput = svntest.main.run_svnadmin("verify",
+                                                        sbox.repo_dir)
+
+  if svntest.verify.verify_outputs(
+    "Output of 'svnadmin verify' is unexpected.", None, errput, None,
+    ".*Verified revision 0*"):
+    raise svntest.Failure
+
+  # Empty the revprops file
+  rp_file = open(os.path.join(repo_dir, 'db', 'revprops', '0', '0'), 'w')
+
+  rp_file.write('')
+  rp_file.close()
+
+  exit_code, output, errput = svntest.main.run_svnadmin("verify",
+                                                        sbox.repo_dir)
+
+  if svntest.verify.verify_outputs(
+    "Output of 'svnadmin verify' is unexpected.", None, errput, None,
+    ".*Malformed file"):
+    raise svntest.Failure
+
+#----------------------------------------------------------------------
+# More testing for issue #3020 'Reflect dropped/renumbered revisions in
+# svn:mergeinfo data during svnadmin load'
+#
+# Specifically, test that loading a partial dump file filters out
+# mergeinfo that refers to revisions that are older than the oldest
+# loaded revisions -- See
+# http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc10.
+def drop_mergeinfo_outside_of_dump_stream(sbox):
+  "filter mergeinfo revs outside of dump stream"
+
+  test_create(sbox)
+
+  # Load a partial dump into an existing repository.
+  #
+  # Picture == 1k words:
+  #
+  # The existing repos loaded from skeleton_repos.dump looks like this:
+  #
+  # Projects/       (Added r1)
+  #   README        (Added r2)
+  #   Project-X     (Added r3)
+  #   Project-Y     (Added r4)
+  #   Project-Z     (Added r5)
+  #   docs/         (Added r6)
+  #     README      (Added r6)
+  #
+  # The dump file 'mergeinfo_included_partial.dump' is a dump of r6:HEAD of
+  # the following repos:
+  #                       __________________________________________
+  #                      |                                         |
+  #                      |             ____________________________|_____
+  #                      |            |                            |     |
+  # trunk---r2---r3-----r5---r6-------r8---r9--------------->      |     |
+  #   r1             |        |     |       |                      |     |
+  # intial           |        |     |       |______                |     |
+  # import         copy       |   copy             |            merge   merge
+  #                  |        |     |            merge           (r5)   (r8)
+  #                  |        |     |            (r9)              |     |
+  #                  |        |     |              |               |     |
+  #                  |        |     V              V               |     |
+  #                  |        | branches/B2-------r11---r12---->   |     |
+  #                  |        |     r7              |____|         |     |
+  #                  |        |                        |           |     |
+  #                  |      merge                      |___        |     |
+  #                  |      (r6)                           |       |     |
+  #                  |        |_________________           |       |     |
+  #                  |                          |        merge     |     |
+  #                  |                          |      (r11-12)    |     |
+  #                  |                          |          |       |     |
+  #                  V                          V          V       |     |
+  #              branches/B1-------------------r10--------r13-->   |     |
+  #                  r4                                            |     |
+  #                   |                                            V     V
+  #                  branches/B1/B/E------------------------------r14---r15->
+  #                  
+  #
+  # The mergeinfo on the complete repos in the preceeding repos looks like:
+  #
+  #   Properties on 'branches/B1':
+  #     svn:mergeinfo
+  #       /branches/B2:11-12
+  #       /trunk:6,9
+  #   Properties on 'branches/B1/B/E':
+  #     svn:mergeinfo
+  #       /branches/B2/B/E:11-12
+  #       /trunk/B/E:5-6,8-9
+  #   Properties on 'branches/B2':
+  #     svn:mergeinfo
+  #       /trunk:9
+  #
+  # If we were to load the dump of r6:HEAD into an empty repository, we'd
+  # expect any references to revisions <r6 to be removed entirely (since
+  # that history no longer exists) and the the remaining mergeinfo should
+  # have its revisions offset by -5.  The resulting mergeinfo should look
+  # like this:
+  #
+  #   Properties on 'branches/B1':
+  #     svn:mergeinfo
+  #       /branches/B2:6-7
+  #       /trunk:1,4
+  #   Properties on 'branches/B1/B/E':
+  #     svn:mergeinfo
+  #       /branches/B2/B/E:6-7
+  #       /trunk/B/E:1,3-4
+  #   Properties on 'branches/B2':
+  #     svn:mergeinfo
+  #       /trunk:4
+  #
+  # But here we will load it into the existing skeleton repository in the
+  # Projects/Project-X directory.  Since we are loading the dump into a
+  # subtree, all the merge sources should be prefixed with the path to
+  # that subtree, i.e. 'Projects/Project-X', compared to the mergeinfo above.
+  # In addition, since the skeleton repos already has 6 revisions, we expect
+  # all the remaining revisions to be offset +6 from the above.  That should
+  # result in this mergeinfo:
+  #
+  #   Properties on 'Projects/Project-X/branches/B1':
+  #     svn:mergeinfo
+  #       /Projects/Project-X/branches/B2:12-13
+  #       /Projects/Project-X/trunk:7,10
+  #   Properties on 'Projects/Project-X/branches/B1/B/E':
+  #     svn:mergeinfo
+  #       /Projects/Project-X/branches/B2/B/E:12-13
+  #       /Projects/Project-X/trunk/B/E:7,9-10
+  #   Properties on 'Projects/Project-X/branches/B2':
+  #     svn:mergeinfo
+  #       /Projects/Project-X/trunk:10
+
+  # Load the skeleton dump:
+  dumpfile1 = svntest.main.file_read(
+    os.path.join(os.path.dirname(sys.argv[0]),
+                 'svnadmin_tests_data',
+                 'skeleton_repos.dump'))
+  load_and_verify_dumpstream(sbox, [], [], None, dumpfile1, '--ignore-uuid')
+
+  # Load the partial repository with mergeinfo dump:
+  dumpfile2 = svntest.main.file_read(
+    os.path.join(os.path.dirname(sys.argv[0]),
+                 'svnadmin_tests_data',
+                 'mergeinfo_included_partial.dump'))
+  load_and_verify_dumpstream(sbox, [], [], None, dumpfile2, '--ignore-uuid',
+                             '--parent-dir', '/Projects/Project-X')
+
+  # Check the resulting mergeinfo.
+  #
+  # TODO: Use pg -vR, which would make the expected output easier on the eyes.
+  #       Not using it because pg -vR on windows is outputting <CR><CR><LF>
+  #       after the first line of multiline mergeinfo, which breaks the
+  #       comparison, e.g.:
+  #
+  #   Properties on 'Projects/Project-X/branches/B1/B/E':<CR><LF>
+  #     svn:mergeinfo<CR><LF>
+  #       /Projects/Project-X/branches/B2:12-13<CR><CR><LF>
+  #                                            ^^^
+  #       /Projects/Project-X/trunk:7,10<CR><LF>
+  url = sbox.repo_url + '/Projects/Project-X/branches/'
+  expected_output = svntest.verify.UnorderedOutput([
+    url + "B1 - /Projects/Project-X/branches/B2:12-13\n",
+    "/Projects/Project-X/trunk:7,10\n",
+    url + "B2 - /Projects/Project-X/trunk:10\n",
+    url + "B1/B/E - /Projects/Project-X/branches/B2/B/E:12-13\n",
+    "/Projects/Project-X/trunk/B/E:7,9-10\n"])
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'propget', 'svn:mergeinfo', '-R',
+                                     sbox.repo_url)
+
+#----------------------------------------------------------------------
+# Even *more* testing for issue #3020 'Reflect dropped/renumbered
+# revisions in svn:mergeinfo data during svnadmin load'
+#
+# Filtering revsions from mergeinfo in a load stream that refers to
+# history outside of the stream is all well and good if the load
+# is a partial dump loaded in one shot...but if a repository's full
+# history is dumped incrementally and each incremental dump is loaded,
+# well then, *any* filtering done then is removing valid mergeinfo...
+#
+# ...and currently we do exactly that, as this test demonstrates.
+#
+# Note: If a repository is *partially* dumped in a sequence of incremental
+# dumps then possibly some mergeinfo should be filtered on the load, but
+# not *all* mergeinfo, which is what we are doing in that case too.
+def dont_drop_valid_mergeinfo_during_incremental_loads(sbox):
+  "don't filter mergeinfo revs from incremental dump"
+
+  # Create an empty repos.
+  test_create(sbox)
+
+  # Load the test repository to the first repos in a single load.
+  #
+  # Note: The test repository 'mergeinfo_included_full.dump' is the full
+  # repos diagramed in the test drop_mergeinfo_outside_of_dump_stream.
+  dumpfile1 = svntest.main.file_read(
+    os.path.join(os.path.dirname(sys.argv[0]),
+                 'svnadmin_tests_data',
+                 'mergeinfo_included_full.dump'))
+  load_and_verify_dumpstream(sbox, [], [], None, dumpfile1, '--ignore-uuid')
+
+  # Check that the mergeinfo is as expected.
+  url = sbox.repo_url + '/branches/'
+  expected_output = svntest.verify.UnorderedOutput([
+    url + "B1 - /branches/B2:11-12\n",
+    "/trunk:6,9\n",
+    url + "B2 - /trunk:9\n",
+    url + "B1/B/E - /branches/B2/B/E:11-12\n",
+    "/trunk/B/E:5-6,8-9\n"])
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'propget', 'svn:mergeinfo', '-R',
+                                     sbox.repo_url)  
+
+  # Now incrementally dump that repository into three dump files:
+  dump_file_r1_10 = svntest.main.temp_dir + "-r1-10.dump"
+  exit_code, output, errput = svntest.main.run_svnadmin(
+    'dump', sbox.repo_dir, '-r1:10')
+  dump_fp = open(dump_file_r1_10, 'wb')
+  dump_fp.writelines(output)
+  dump_fp.close()
+
+  dump_file_r11_13 = svntest.main.temp_dir + "-r11-13.dump"
+  exit_code, output, errput = svntest.main.run_svnadmin(
+    'dump', sbox.repo_dir, '--incremental', '-r11:13')
+  dump_fp = open(dump_file_r11_13, 'wb')
+  dump_fp.writelines(output)
+  dump_fp.close()
+
+  dump_file_r14_15 = svntest.main.temp_dir + "-r14-15.dump"
+  exit_code, output, errput = svntest.main.run_svnadmin(
+    'dump', sbox.repo_dir, '--incremental', '-r14:15')
+  dump_fp = open(dump_file_r14_15, 'wb')
+  dump_fp.writelines(output)
+  dump_fp.close()
+
+  # Blow away the current repos and create an empty one in its place.
+  test_create(sbox)
+
+  # Load the three incremental dump files in sequence.
+  load_and_verify_dumpstream(sbox, [], [], None,
+                             svntest.main.file_read(dump_file_r1_10),
+                             '--ignore-uuid')
+  load_and_verify_dumpstream(sbox, [], [], None,
+                             svntest.main.file_read(dump_file_r11_13),
+                             '--ignore-uuid')
+  load_and_verify_dumpstream(sbox, [], [], None,
+                             svntest.main.file_read(dump_file_r14_15),
+                             '--ignore-uuid')
+
+  # Check the mergeinfo, we use the same expected output as before,
+  # as it (duh!) should be exactly the same as when we loaded the
+  # repos in one shot.
+  #
+  # Currently this test is set as XFail, because the mergeinfo filtering
+  # logic in load is removing valid mergeinfo.
+  svntest.actions.run_and_verify_svn(None, expected_output, [],
+                                     'propget', 'svn:mergeinfo', '-R',
+                                     sbox.repo_url)
 
 ########################################################################
 # Run the tests
@@ -951,6 +1220,10 @@ test_list = [ None,
               SkipUnless(fsfs_recover_handle_missing_revs_or_revprops_file,
                          svntest.main.is_fs_type_fsfs),
               create_in_repo_subdir,
+              SkipUnless(verify_with_invalid_revprops,
+                         svntest.main.is_fs_type_fsfs),
+              drop_mergeinfo_outside_of_dump_stream,
+              XFail(dont_drop_valid_mergeinfo_during_incremental_loads),
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svndumpfilter_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svndumpfilter_tests.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svndumpfilter_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svndumpfilter_tests.py Tue Aug 10 18:06:17 2010
@@ -3,7 +3,7 @@
 #  svndumpfilter_tests.py:  testing the 'svndumpfilter' tool.
 #
 #  Subversion is a tool for revision control.
-#  See http://subversion.tigris.org for more information.
+#  See http://subversion.apache.org for more information.
 #
 # ====================================================================
 #    Licensed to the Apache Software Foundation (ASF) under one

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svneditor.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svneditor.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svneditor.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svneditor.py Tue Aug 10 18:06:17 2010
@@ -3,7 +3,7 @@
 #  svneditor.py: a mock $SVN_EDITOR for the Subversion test suite
 #
 #  Subversion is a tool for revision control.
-#  See http://subversion.tigris.org for more information.
+#  See http://subversion.apache.org for more information.
 #
 # ====================================================================
 #    Licensed to the Apache Software Foundation (ASF) under one

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnlook_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnlook_tests.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnlook_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnlook_tests.py Tue Aug 10 18:06:17 2010
@@ -3,7 +3,7 @@
 #  svnlook_tests.py:  testing the 'svnlook' tool.
 #
 #  Subversion is a tool for revision control.
-#  See http://subversion.tigris.org for more information.
+#  See http://subversion.apache.org for more information.
 #
 # ====================================================================
 #    Licensed to the Apache Software Foundation (ASF) under one
@@ -577,6 +577,96 @@ def test_filesize(sbox):
                             "'svnlook cat's output differ for the path "
                             "'%s'." % (line))
 
+#----------------------------------------------------------------------
+def verify_logfile(logfilename, expected_data):
+  if os.path.exists(logfilename):
+    fp = open(logfilename)
+  else:
+    raise svntest.verify.SVNUnexpectedOutput("hook logfile %s not found"\
+                                             % logfilename)
+
+  actual_data = fp.readlines()
+  fp.close()
+  os.unlink(logfilename)
+  svntest.verify.compare_and_display_lines('wrong hook logfile content',
+                                           'STDOUT',
+                                           expected_data, actual_data)
+
+def test_txn_flag(sbox):
+  "test 'svnlook * -t'"
+
+  sbox.build()
+  repo_dir = sbox.repo_dir
+  wc_dir = sbox.wc_dir
+  logfilepath = os.path.join(repo_dir, 'hooks.log')
+
+  # List changed dirs and files in this transaction
+  hook_template = """import sys,os,subprocess
+svnlook_bin=%s
+
+fp = open(os.path.join(sys.argv[1], 'hooks.log'), 'wb')
+def output_command(fp, cmd, opt):
+  command = [svnlook_bin, cmd, '-t', sys.argv[2], sys.argv[1]] + opt
+  process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False)
+  (output, errors) = process.communicate()
+  status = process.returncode
+  fp.write(output)
+  fp.write(errors)
+  return status
+
+for (svnlook_cmd, svnlook_opt) in %s:
+  output_command(fp, svnlook_cmd, svnlook_opt.split(' '))
+fp.close()"""
+  pre_commit_hook = svntest.main.get_pre_commit_hook_path(repo_dir)
+
+  # 1. svnlook 'changed' -t and 'dirs-changed' -t
+  hook_instance = hook_template % (repr(svntest.main.svnlook_binary),
+                                   repr([('changed', ''),
+                                         ('dirs-changed', '')]))
+  svntest.main.create_python_hook_script(pre_commit_hook,
+                                         hook_instance)
+
+  # Change files mu and rho
+  A_path = os.path.join(wc_dir, 'A')
+  mu_path = os.path.join(wc_dir, 'A', 'mu')
+  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
+  svntest.main.file_append(mu_path, 'appended mu text')
+  svntest.main.file_append(rho_path, 'new appended text for rho')
+
+  # commit, and check the hook's logfile
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'ci', '-m', 'log msg', wc_dir)
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'up', wc_dir)
+
+  expected_data = [ 'U   A/D/G/rho\n', 'U   A/mu\n', 'A/\n', 'A/D/G/\n' ]
+  verify_logfile(logfilepath, expected_data)
+
+  # 2. svnlook 'propget' -t, 'proplist' -t
+  # 2. Change a dir and revision property
+  hook_instance = hook_template % (repr(svntest.main.svnlook_binary),
+                                   repr([('propget', 'bogus_prop /A'),
+                                         ('propget', '--revprop bogus_rev_prop'),
+                                         ('proplist', '/A'),
+                                         ('proplist', '--revprop')]))
+  svntest.main.create_python_hook_script(pre_commit_hook,
+                                         hook_instance)
+
+  svntest.actions.run_and_verify_svn(None, None, [], 'propset',
+                                     'bogus_prop', 'bogus_val\n', A_path)
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'ci', '-m', 'log msg', wc_dir,
+                                     '--with-revprop', 'bogus_rev_prop=bogus_rev_val\n')
+  # Now check the logfile
+  expected_data = [ 'bogus_val\n',
+                    'bogus_rev_val\n',
+                    '  bogus_prop\n',
+                    '  svn:log\n', '  svn:author\n',
+                    #  internal property, not really expected
+                    '  svn:check-locks\n',
+                    '  bogus_rev_prop\n', '  svn:date\n']
+  verify_logfile(logfilepath, expected_data)
+
 ########################################################################
 # Run the tests
 
@@ -596,6 +686,7 @@ test_list = [ None,
               diff_ignore_eolstyle,
               diff_binary,
               test_filesize,
+              test_txn_flag,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnsync_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnsync_tests.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnsync_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnsync_tests.py Tue Aug 10 18:06:17 2010
@@ -3,7 +3,7 @@
 #  svnsync_tests.py:  Tests SVNSync's repository mirroring capabilities.
 #
 #  Subversion is a tool for revision control.
-#  See http://subversion.tigris.org for more information.
+#  See http://subversion.apache.org for more information.
 #
 # ====================================================================
 #    Licensed to the Apache Software Foundation (ASF) under one

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/actions.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/actions.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/actions.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/actions.py Tue Aug 10 18:06:17 2010
@@ -213,20 +213,26 @@ def run_and_verify_svn(message, expected
   be 0 if no output is expected on stderr, and 1 otherwise."""
 
   expected_exit = 0
-  if expected_stderr is not None and expected_stderr != []:
-    expected_exit = 1
+  if expected_stderr is not None:
+    if isinstance(expected_stderr, verify.ExpectedOutput):
+      if not expected_stderr.matches([]):
+        expected_exit = 1
+    elif expected_stderr != []:
+      expected_exit = 1
   return run_and_verify_svn2(message, expected_stdout, expected_stderr,
                              expected_exit, *varargs)
 
 def run_and_verify_svn2(message, expected_stdout, expected_stderr,
                         expected_exit, *varargs):
-  """Invokes main.run_svn() with *VARARGS, returns exit code as int, stdout
-  and stderr as lists of lines.  For both EXPECTED_STDOUT and EXPECTED_STDERR,
-  create an appropriate instance of verify.ExpectedOutput (if necessary):
+  """Invoke main.run_svn() with *VARARGS. Return exit code as int; stdout,
+  stderr as lists of lines (including line terminators).  For both
+  EXPECTED_STDOUT and EXPECTED_STDERR, create an appropriate instance of
+  verify.ExpectedOutput (if necessary):
 
      - If it is an array of strings, create a vanilla ExpectedOutput.
 
-     - If it is a single string, create a RegexOutput.
+     - If it is a single string, create a RegexOutput that must match every
+       line (for stdout) or any line (for stderr) of the expected output.
 
      - If it is already an instance of ExpectedOutput
        (e.g. UnorderedOutput), leave it alone.
@@ -246,7 +252,10 @@ def run_and_verify_svn2(message, expecte
     raise verify.SVNIncorrectDatatype("expected_stderr must not be None")
 
   want_err = None
-  if expected_stderr != []:
+  if isinstance(expected_stderr, verify.ExpectedOutput):
+    if not expected_stderr.matches([]):
+      want_err = True
+  elif expected_stderr != []:
     want_err = True
 
   exit_code, out, err = main.run_svn(want_err, *varargs)
@@ -254,38 +263,6 @@ def run_and_verify_svn2(message, expecte
   verify.verify_exit_code(message, exit_code, expected_exit)
   return exit_code, out, err
 
-def run_and_verify_svn_match_any(message, expected_stdout, expected_stderr,
-                                 *varargs):
-  """Like run_and_verify_svn_match_any2, but the expected exit code is
-  assumed to be 0 if no output is expected on stderr, and 1 otherwise."""
-
-  expected_exit = 0
-  if expected_stderr is not None and expected_stderr != []:
-    expected_exit = 1
-  return run_and_verify_svn_match_any2(message, expected_stdout,
-                                       expected_stderr, expected_exit,
-                                       *varargs)
-
-
-def run_and_verify_svn_match_any2(message, expected_stdout, expected_stderr,
-                                 expected_exit, *varargs):
-  """Like run_and_verify_svn2, except that only one stdout line must match
-  EXPECTED_STDOUT."""
-
-  if expected_stderr is None:
-    raise verify.SVNIncorrectDatatype("expected_stderr must not be None")
-
-  want_err = None
-  if expected_stderr is not None and expected_stderr != []:
-    want_err = True
-
-  exit_code, out, err = main.run_svn(want_err, *varargs)
-  verify.verify_outputs(message, out, err, expected_stdout, expected_stderr,
-                        False)
-  verify.verify_exit_code(message, exit_code, expected_exit)
-  return exit_code, out, err
-
-
 def run_and_verify_load(repo_dir, dump_file_content):
   "Runs 'svnadmin load' and reports any errors."
   if not isinstance(dump_file_content, list):
@@ -584,19 +561,30 @@ def run_and_verify_log_xml(message=None,
       entry.assert_changed_paths(expected_paths[index])
 
 
-def verify_update(actual_output, wc_dir_name,
-                  output_tree, disk_tree, status_tree,
-                  singleton_handler_a=None, a_baton=None,
-                  singleton_handler_b=None, b_baton=None,
+def verify_update(actual_output,
+                  actual_mergeinfo_output,
+                  actual_elision_output,
+                  wc_dir_name,
+                  output_tree,
+                  mergeinfo_output_tree,
+                  elision_output_tree,
+                  disk_tree,
+                  status_tree,
+                  singleton_handler_a=None,
+                  a_baton=None,
+                  singleton_handler_b=None,
+                  b_baton=None,
                   check_props=False):
   """Verify update of WC_DIR_NAME.
 
-  The subcommand output (found in ACTUAL_OUTPUT) will be verified
-  against OUTPUT_TREE (if provided), the working copy itself will be
-  verified against DISK_TREE (if provided), and the working copy's
-  'svn status' output will be verified against STATUS_TREE (if
-  provided).  (This is a good way to check that revision numbers were
-  bumped.)
+  The subcommand output (found in ACTUAL_OUTPUT, ACTUAL_MERGEINFO_OUTPUT,
+  and ACTUAL_ELISION_OUTPUT) will be verified against OUTPUT_TREE,
+  MERGEINFO_OUTPUT_TREE, and ELISION_OUTPUT_TREE respectively (if any of
+  these is provided, they may be None in which case a comparison is not
+  done).  The working copy itself will be verified against DISK_TREE (if
+  provided), and the working copy's 'svn status' output will be verified
+  against STATUS_TREE (if provided).  (This is a good way to check that
+  revision numbers were bumped.)
 
   Return if successful, raise on failure.
 
@@ -607,8 +595,16 @@ def verify_update(actual_output, wc_dir_
 
   if isinstance(actual_output, wc.State):
     actual_output = actual_output.old_tree()
+  if isinstance(actual_mergeinfo_output, wc.State):
+    actual_mergeinfo_output = actual_mergeinfo_output.old_tree()
+  if isinstance(actual_elision_output, wc.State):
+    actual_elision_output = actual_elision_output.old_tree()
   if isinstance(output_tree, wc.State):
     output_tree = output_tree.old_tree()
+  if isinstance(mergeinfo_output_tree, wc.State):
+    mergeinfo_output_tree = mergeinfo_output_tree.old_tree()
+  if isinstance(elision_output_tree, wc.State):
+    elision_output_tree = elision_output_tree.old_tree()
   if isinstance(disk_tree, wc.State):
     disk_tree = disk_tree.old_tree()
   if isinstance(status_tree, wc.State):
@@ -623,6 +619,28 @@ def verify_update(actual_output, wc_dir_
       tree.dump_tree_script(actual_output, wc_dir_name + os.sep)
       raise
 
+  # Verify actual mergeinfo recording output against expected output.
+  if mergeinfo_output_tree:
+    try:
+      tree.compare_trees("mergeinfo_output", actual_mergeinfo_output,
+                         mergeinfo_output_tree)
+    except tree.SVNTreeUnequal:
+      print("ACTUAL MERGEINFO OUTPUT TREE:")
+      tree.dump_tree_script(actual_mergeinfo_output,
+                            wc_dir_name + os.sep)
+      raise
+
+  # Verify actual mergeinfo elision output against expected output.
+  if elision_output_tree:
+    try:
+      tree.compare_trees("elision_output", actual_elision_output,
+                         elision_output_tree)
+    except tree.SVNTreeUnequal:
+      print("ACTUAL ELISION OUTPUT TREE:")
+      tree.dump_tree_script(actual_elision_output,
+                            wc_dir_name + os.sep)
+      raise
+
   # Create a tree by scanning the working copy, and verify it
   if disk_tree:
     actual_disk = tree.build_tree_from_wc(wc_dir_name, check_props)
@@ -644,8 +662,8 @@ def verify_disk(wc_dir_name, disk_tree, 
   """Verify WC_DIR_NAME against DISK_TREE.  If CHECK_PROPS is set,
   the comparison will examin props.  Returns if successful, raises on
   failure."""
-  verify_update(None, wc_dir_name, None, disk_tree, None,
-                check_props=check_props)
+  verify_update(None, None, None, wc_dir_name, None, None, None, disk_tree,
+                None, check_props=check_props)
 
 
 
@@ -702,8 +720,8 @@ def run_and_verify_update(wc_dir_name,
     raise main.SVNUnmatchedError
 
   actual = wc.State.from_checkout(output)
-  verify_update(actual, wc_dir_name,
-                output_tree, disk_tree, status_tree,
+  verify_update(actual, None, None, wc_dir_name,
+                output_tree, None, None, disk_tree, status_tree,
                 singleton_handler_a, a_baton,
                 singleton_handler_b, b_baton,
                 check_props)
@@ -808,8 +826,11 @@ def run_and_verify_info(expected_infos, 
                      % (actual_infos, expected_infos))
     raise
 
-def run_and_verify_merge(dir, rev1, rev2, url,
-                         output_tree, disk_tree, status_tree, skip_tree,
+def run_and_verify_merge(dir, rev1, rev2, url1, url2,
+                         output_tree,
+                         mergeinfo_output_tree,
+                         elision_output_tree,
+                         disk_tree, status_tree, skip_tree,
                          error_re_string = None,
                          singleton_handler_a = None,
                          a_baton = None,
@@ -818,30 +839,6 @@ def run_and_verify_merge(dir, rev1, rev2
                          check_props = False,
                          dry_run = True,
                          *args):
-  """Run 'svn merge -rREV1:REV2 URL DIR', leaving off the '-r'
-  argument if both REV1 and REV2 are None."""
-  if args:
-    run_and_verify_merge2(dir, rev1, rev2, url, None, output_tree, disk_tree,
-                          status_tree, skip_tree, error_re_string,
-                          singleton_handler_a, a_baton, singleton_handler_b,
-                          b_baton, check_props, dry_run, *args)
-  else:
-    run_and_verify_merge2(dir, rev1, rev2, url, None, output_tree, disk_tree,
-                          status_tree, skip_tree, error_re_string,
-                          singleton_handler_a, a_baton, singleton_handler_b,
-                          b_baton, check_props, dry_run)
-
-
-def run_and_verify_merge2(dir, rev1, rev2, url1, url2,
-                          output_tree, disk_tree, status_tree, skip_tree,
-                          error_re_string = None,
-                          singleton_handler_a = None,
-                          a_baton = None,
-                          singleton_handler_b = None,
-                          b_baton = None,
-                          check_props = False,
-                          dry_run = True,
-                          *args):
   """Run 'svn merge URL1@REV1 URL2@REV2 DIR' if URL2 is not None
   (for a three-way merge between URLs and WC).
 
@@ -853,10 +850,13 @@ def run_and_verify_merge2(dir, rev1, rev
 
   Else if ERROR_RE_STRING is None, then:
 
-  The subcommand output will be verified against OUTPUT_TREE, and the
-  working copy itself will be verified against DISK_TREE.  If optional
-  STATUS_TREE is given, then 'svn status' output will be compared.
-  The 'skipped' merge output will be compared to SKIP_TREE.
+  The subcommand output will be verified against OUTPUT_TREE.  Output
+  related to mergeinfo notifications will be verified against
+  MERGEINFO_OUTPUT_TREE if that is not None.  Output related to mergeinfo
+  elision will be verified against ELISION_OUTPUT_TREE if that is not None.
+  The working copy itself will be verified against DISK_TREE.  If optional
+  STATUS_TREE is given, then 'svn status' output will be compared.  The
+  'skipped' merge output will be compared to SKIP_TREE.
 
   For the DISK_TREE verification, SINGLETON_HANDLER_A and
   SINGLETON_HANDLER_B will be passed to tree.compare_trees -- see that
@@ -908,13 +908,41 @@ def run_and_verify_merge2(dir, rev1, rev
   elif err:
     raise verify.SVNUnexpectedStderr(err)
 
-  if dry_run and out != out_dry:
+  # Split the output into that related to application of the actual diff
+  # and that related to the recording of mergeinfo describing the merge.
+  merge_diff_out = []
+  mergeinfo_notification_out = []
+  mergeinfo_elision_out = []
+  mergeinfo_notifications = False
+  elision_notifications = False
+  for line in out:
+    if line.startswith('--- Recording'):
+      mergeinfo_notifications = True
+      elision_notifications = False
+    elif line.startswith('--- Eliding'):
+      mergeinfo_notifications = False
+      elision_notifications = True
+    elif line.startswith('--- Merging')          or \
+         line.startswith('--- Reverse-merging')  or \
+         line.startswith('Summary of conflicts') or \
+         line.startswith('Skipped missing target'):
+      mergeinfo_notifications = False
+      elision_notifications = False
+
+    if mergeinfo_notifications:
+      mergeinfo_notification_out.append(line)
+    elif elision_notifications:
+      mergeinfo_elision_out.append(line)
+    else:
+      merge_diff_out.append(line)
+
+  if dry_run and merge_diff_out != out_dry:
     # Due to the way ra_serf works, it's possible that the dry-run and
     # real merge operations did the same thing, but the output came in
     # a different order.  Let's see if maybe that's the case.
     #
     # NOTE:  Would be nice to limit this dance to serf tests only, but...
-    out_copy = out[:]
+    out_copy = merge_diff_out[:]
     out_dry_copy = out_dry[:]
     out_copy.sort()
     out_dry_copy.sort()
@@ -952,9 +980,14 @@ def run_and_verify_merge2(dir, rev1, rev
     tree.dump_tree_script(myskiptree, dir + os.sep)
     raise
 
-  actual = svntest.wc.State.from_checkout(out, False)
-  verify_update(actual, dir,
-                output_tree, disk_tree, status_tree,
+  actual_diff = svntest.wc.State.from_checkout(merge_diff_out, False)
+  actual_mergeinfo = svntest.wc.State.from_checkout(mergeinfo_notification_out,
+                                                    False)
+  actual_elision = svntest.wc.State.from_checkout(mergeinfo_elision_out,
+                                                  False)
+  verify_update(actual_diff, actual_mergeinfo, actual_elision, dir,
+                output_tree, mergeinfo_output_tree, elision_output_tree,
+                disk_tree, status_tree,
                 singleton_handler_a, a_baton,
                 singleton_handler_b, b_baton,
                 check_props)
@@ -1061,8 +1094,8 @@ def run_and_verify_patch(dir, patch_path
     verify.verify_outputs(None, out, err, output_tree, error_re_string)
     output_tree = None
 
-  verify_update(mytree, dir,
-                output_tree, disk_tree, status_tree,
+  verify_update(mytree, None, None, dir,
+                output_tree, None, None, disk_tree, status_tree,
                 check_props=check_props)
 
 
@@ -1151,8 +1184,8 @@ def run_and_verify_switch(wc_dir_name,
 
   actual = wc.State.from_checkout(output)
 
-  verify_update(actual, wc_dir_name,
-                output_tree, disk_tree, status_tree,
+  verify_update(actual, None, None, wc_dir_name,
+                output_tree, None, None, disk_tree, status_tree,
                 singleton_handler_a, a_baton,
                 singleton_handler_b, b_baton,
                 check_props)
@@ -2141,6 +2174,24 @@ class DeepTreesTestCase:
 
   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.
@@ -2150,7 +2201,8 @@ class DeepTreesTestCase:
                 expected_output = None, expected_disk = None,
                 expected_status = None, expected_skip = None,
                 error_re_string = None,
-                commit_block_string = ".*remains in conflict.*"):
+                commit_block_string = ".*remains in conflict.*",
+                expected_info = None):
     self.name = name
     self.local_action = local_action
     self.incoming_action = incoming_action
@@ -2160,6 +2212,7 @@ class DeepTreesTestCase:
     self.expected_skip = expected_skip
     self.error_re_string = error_re_string
     self.commit_block_string = commit_block_string
+    self.expected_info = expected_info
 
 
 
@@ -2288,6 +2341,11 @@ def deep_trees_run_tests_scheme_for_upda
                             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:
       print("ERROR IN: Tests scheme for update: "
           + "while verifying in '%s'" % test_case.name)
@@ -2523,6 +2581,10 @@ def deep_trees_run_tests_scheme_for_swit
       run_and_verify_switch(local, local, incoming, x_out, x_disk, None,
                             error_re_string = test_case.error_re_string)
       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:
       print("ERROR IN: Tests scheme for switch: "
           + "while verifying in '%s'" % test_case.name)
@@ -2720,8 +2782,8 @@ def deep_trees_run_tests_scheme_for_merg
         x_skip.copy()
         x_skip.wc_dir = local
 
-      run_and_verify_merge(local, None, None, incoming,
-                           x_out, x_disk, None, x_skip,
+      run_and_verify_merge(local, None, None, incoming, None,
+                           x_out, None, None, x_disk, None, x_skip,
                            error_re_string = test_case.error_re_string,
                            dry_run = False)
       run_and_verify_unquiet_status(local, x_status)

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/main.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/main.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/main.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/main.py Tue Aug 10 18:06:17 2010
@@ -299,7 +299,6 @@ def setup_development_mode():
         'run_and_verify_export',
         'run_and_verify_update',
         'run_and_verify_merge',
-        'run_and_verify_merge2',
         'run_and_verify_switch',
         'run_and_verify_commit',
         'run_and_verify_unquiet_status',
@@ -359,9 +358,9 @@ def get_fsfs_format_file_path(repo_dir):
 
 # Run any binary, logging the command line and return code
 def run_command(command, error_expected, binary_mode=0, *varargs):
-  """Run COMMAND with VARARGS; return exit code as int; stdout, stderr
-  as lists of lines.  See run_command_stdin() for details.
-  If ERROR_EXPECTED is None, any stderr also will be printed."""
+  """Run COMMAND with VARARGS. Return exit code as int; stdout, stderr
+  as lists of lines (including line terminators).  See run_command_stdin()
+  for details.  If ERROR_EXPECTED is None, any stderr also will be printed."""
 
   return run_command_stdin(command, error_expected, binary_mode,
                            None, *varargs)
@@ -434,8 +433,8 @@ def wait_on_pipe(waiter, binary_mode, st
   to finish, dying if it does.  If KID fails, create an error message
   containing any stdout and stderr from the kid.  Show COMMAND_STRING in
   diagnostic messages.  Normalize Windows line endings of stdout and stderr
-  if not BINARY_MODE.  Return KID's exit code, stdout and stderr (the latter
-  two as lists)."""
+  if not BINARY_MODE.  Return KID's exit code as int; stdout, stderr as
+  lists of lines (including line terminators)."""
   if waiter is None:
     return
 
@@ -478,7 +477,9 @@ def wait_on_pipe(waiter, binary_mode, st
 
 def spawn_process(command, binary_mode=0, stdin_lines=None, *varargs):
   """Run any binary, supplying input text, logging the command line.
-  Normalize Windows line endings of stdout and stderr if not BINARY_MODE."""
+  Normalize Windows line endings of stdout and stderr if not BINARY_MODE.
+  Return exit code as int; stdout, stderr as lists of lines (including
+  line terminators)."""
   if stdin_lines and not isinstance(stdin_lines, list):
     raise TypeError("stdin_lines should have list type")
 
@@ -510,7 +511,8 @@ def run_command_stdin(command, error_exp
   is willing to buffer, this will deadlock, with both Python and
   COMMAND waiting to write to each other for ever.
   Normalize Windows line endings of stdout and stderr if not BINARY_MODE.
-  Return exit code as int; stdout, stderr as lists of lines.
+  Return exit code as int; stdout, stderr as lists of lines (including
+  line terminators).
   If ERROR_EXPECTED is None, any stderr also will be printed."""
 
   if verbose_mode:
@@ -595,7 +597,7 @@ def _with_auth(args):
 # For running subversion and returning the output
 def run_svn(error_expected, *varargs):
   """Run svn with VARARGS; return exit code as int; stdout, stderr as
-  lists of lines.
+  lists of lines (including line terminators).
   If ERROR_EXPECTED is None, any stderr also will be printed.  If
   you're just checking that something does/doesn't come out of
   stdout/stderr, you might want to use actions.run_and_verify_svn()."""
@@ -605,23 +607,23 @@ def run_svn(error_expected, *varargs):
 # For running svnadmin.  Ignores the output.
 def run_svnadmin(*varargs):
   """Run svnadmin with VARARGS, returns exit code as int; stdout, stderr as
-  list of lines."""
+  list of lines (including line terminators)."""
   return run_command(svnadmin_binary, 1, 0, *varargs)
 
 # For running svnlook.  Ignores the output.
 def run_svnlook(*varargs):
   """Run svnlook with VARARGS, returns exit code as int; stdout, stderr as
-  list of lines."""
+  list of lines (including line terminators)."""
   return run_command(svnlook_binary, 1, 0, *varargs)
 
 def run_svnsync(*varargs):
   """Run svnsync with VARARGS, returns exit code as int; stdout, stderr as
-  list of lines."""
+  list of lines (including line terminators)."""
   return run_command(svnsync_binary, 1, 0, *(_with_config_dir(varargs)))
 
 def run_svnversion(*varargs):
   """Run svnversion with VARARGS, returns exit code as int; stdout, stderr
-  as list of lines."""
+  as list of lines (including line terminators)."""
   return run_command(svnversion_binary, 1, 0, *varargs)
 
 def run_entriesdump(path):
@@ -944,6 +946,22 @@ def use_editor(func):
   os.environ['SVNTEST_EDITOR_FUNC'] = func
   os.environ['SVN_TEST_PYTHON'] = sys.executable
 
+def mergeinfo_notify_line(revstart, revend):
+  """Return an expected output line that describes the beginning of a
+  mergeinfo recording notification on revisions REVSTART through REVEND."""
+  if (revend is None):
+    if (revstart < 0):
+      revstart = abs(revstart)
+      return "--- Recording mergeinfo for reverse merge of r%ld .*:\n" \
+             % (revstart)
+    else:
+      return "--- Recording mergeinfo for merge of r%ld .*:\n" % (revstart)
+  elif (revstart < revend):
+    return "--- Recording mergeinfo for merge of r%ld through r%ld .*:\n" \
+           % (revstart, revend)
+  else:
+    return "--- Recording mergeinfo for reverse merge of r%ld through " \
+           "r%ld .*:\n" % (revstart, revend)
 
 def merge_notify_line(revstart=None, revend=None, same_URL=True,
                       foreign=False):
@@ -1100,7 +1118,7 @@ class TestSpawningThread(threading.Threa
     if server_minor_version:
       args.append('--server-minor-version=' + str(server_minor_version))
 
-    result, stdout_lines, stderr_lines = spawn_process(command, 1, None, *args)
+    result, stdout_lines, stderr_lines = spawn_process(command, 0, None, *args)
     self.results.append((index, result, stdout_lines, stderr_lines))
 
     if result != 1:

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/objects.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/objects.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/objects.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/objects.py Tue Aug 10 18:06:17 2010
@@ -62,50 +62,37 @@ def db_dump(db_dump_name, repo_path, tab
     elif copying:
       yield line
 
-def crude_bdb_parse(line):
-  """Replace BDB ([klen] kval) syntax with (kval) syntax. This is a very
-  crude parser, only just good enough to make a more human-friendly output
-  in common cases. The result is not intended to be unambiguous or machine-
-  parsable."""
-  lparts = line.split('(')
-  new_lparts = []
-  for lpart in lparts:
-    rparts = lpart.split(')')
-    new_rparts = []
-    for rpart in rparts:
-      words = rpart.split(' ')
-
-      # Set new_words to all the words that we want to keep
-      new_words = []
-      last_len = None
-      last_word = None
-      for word in words:
-        if last_len is None:
-          try:
-            last_len = int(word)
-            last_word = word
-          except ValueError:
-            if last_word is not None:
-              new_words += [last_word]
-            new_words += [word]
-            last_word = None
-            last_len = None
-        else:  # print a word that was previously prefixed by its len
-          if not len(word) == last_len + word.count('\\') * 2:
-            print "## parsed wrongly: %d %d '%s'" % (last_len, len(word), word)
-          if word == "":
-            new_words += ["''"]
-          else:
-            new_words += [word]
-          last_word = None
-          last_len = None
-
-      if last_word is not None:
-        new_words += [last_word]
-
-      new_rparts += [' '.join(new_words)]
-    new_lparts += [')'.join(new_rparts)]
-  new_line = '('.join(new_lparts)
+def pretty_print_skel(line):
+  """Return LINE modified so as to look prettier for human reading, but no
+  longer unambiguous or machine-parsable. LINE is assumed to be in the
+  "Skel" format in which some values are preceded by a decimal length. This
+  function removes the length indicator, and also replaces a zero-length
+  value with a pair of single quotes."""
+  new_line = ''
+  while line:
+    # an explicit-length atom
+    explicit_atom = re.match(r'\d+ ', line)
+    if explicit_atom:
+      n = int(explicit_atom.group())
+      line = line[explicit_atom.end():]
+      new_line += line[:n]
+      line = line[n:]
+      if n == 0:
+        new_line += "''"
+      continue
+
+    # an implicit-length atom
+    implicit_atom = re.match(r'[A-Za-z][^\s()]*', line)
+    if implicit_atom:
+      n = implicit_atom.end()
+      new_line += line[:n]
+      line = line[n:]
+      continue
+
+    # parentheses, white space or any other non-atom
+    new_line += line[:1]
+    line = line[1:]
+
   return new_line
 
 def dump_bdb(db_dump_name, repo_path, dump_dir):
@@ -117,16 +104,21 @@ def dump_bdb(db_dump_name, repo_path, du
                 'node-origins', 'representations', 'checksum-reps', 'strings',
                 'locks', 'lock-tokens', 'miscellaneous', 'uuids']:
     file.write(table + ":\n")
+    next_key_line = False
     for line in db_dump(db_dump_name, repo_path, table):
-      file.write(crude_bdb_parse(line))
+      # Omit any 'next-key' line and the following line.
+      if next_key_line:
+        next_key_line = False
+        continue
+      if line == ' next-key\n':
+        next_key_line = True
+        continue
+      # The line isn't necessarily a skel, but pretty_print_skel() shouldn't
+      # do too much harm if it isn't.
+      file.write(pretty_print_skel(line))
     file.write("\n")
   file.close()
 
-  # Dump the svn view of the repository
-  #svnadmin dump    repo_path > dump_dir + "/dump.svn"
-  #svnadmin lslocks repo_path > dump_dir + "/locks.svn"
-  #svnadmin lstxns  repo_path > dump_dir + "/txns.svn"
-
 def locate_db_dump():
   """Locate a db_dump executable"""
   # Assume that using the newest version is OK.
@@ -150,6 +142,8 @@ class SvnRepository:
     self.repo_url = repo_url
     self.repo_absdir = os.path.abspath(repo_dir)
     self.db_dump_name = locate_db_dump()
+    # This repository object's idea of its own head revision.
+    self.head_rev = 0
 
   def dump(self, output_dir):
     """Dump the repository into the directory OUTPUT_DIR"""
@@ -170,11 +164,23 @@ class SvnRepository:
     main.file_append(ldumpfile, ''.join(stdout))
 
 
-  def obliterate_node_rev(self, path, rev):
-    """Obliterate the single node-rev PATH in revision REV."""
+  def obliterate_node_rev(self, path, rev,
+                          exp_out=None, exp_err=[], exp_exit=0):
+    """Obliterate the single node-rev PATH in revision REV. Check the
+    expected stdout, stderr and exit code (EXP_OUT, EXP_ERR, EXP_EXIT)."""
+    arg = self.repo_url + '/' + path + '@' + str(rev)
+    actions.run_and_verify_svn2(None, exp_out, exp_err, exp_exit,
+                                'obliterate', arg)
+
+  def svn_mkdirs(self, *dirs):
+    """Run 'svn mkdir' on the repository. DIRS is a list of directories to
+    make, and each directory is a path relative to the repository root,
+    neither starting nor ending with a slash."""
+    urls = [self.repo_url + '/' + dir for dir in dirs]
     actions.run_and_verify_svn(None, None, [],
-                               'obliterate',
-                               self.repo_url + '/' + path + '@' + str(rev))
+                               'mkdir', '-m', 'svn_mkdirs()', '--parents',
+                               *urls)
+    self.head_rev += 1
 
 
 ######################################################################
@@ -190,9 +196,9 @@ class SvnWC:
      in Subversion canonical form ('/' separators).
      """
 
-  def __init__(self, wc_dir):
-    """Initialize the object to use the existing WC at path WC_DIR.
-    """
+  def __init__(self, wc_dir, repo):
+    """Initialize the object to use the existing WC at path WC_DIR and
+    the existing repository object REPO."""
     self.wc_absdir = os.path.abspath(wc_dir)
     # 'state' is, at all times, the 'wc.State' representation of the state
     # of the WC, with paths relative to 'wc_absdir'.
@@ -202,15 +208,10 @@ class SvnWC:
     self.state.add({
       '': wc.StateItem()
     })
-    # This WC's idea of the repository's head revision.
-    # ### Shouldn't be in this class: should ask the repository instead.
-    self.head_rev = 0
-
-    print "## new SvnWC:"
-    print self
+    self.repo = repo
 
   def __str__(self):
-    return "SvnWC(head_rev=" + str(self.head_rev) + ", state={" + \
+    return "SvnWC(head_rev=" + str(self.repo.head_rev) + ", state={" + \
       str(self.state.desc) + \
       "})"
 
@@ -306,10 +307,13 @@ class SvnWC:
       rpath2: self.state.desc[rpath1]
     })
 
-  def svn_delete(self, rpath):
+  def svn_delete(self, rpath, even_if_modified=False):
     "Delete a WC path locally."
     lpath = local_path(rpath)
-    actions.run_and_verify_svn(None, None, [], 'delete', lpath)
+    args = []
+    if even_if_modified:
+      args += ['--force']
+    actions.run_and_verify_svn(None, None, [], 'delete', lpath, *args)
 
   def svn_commit(self, rpath='', log=''):
     "Commit a WC path (recursively). Return the new revision number."
@@ -317,9 +321,14 @@ class SvnWC:
     actions.run_and_verify_svn(None, verify.AnyOutput, [],
                                'commit', '-m', log, lpath)
     actions.run_and_verify_update(lpath, None, None, None)
-    self.head_rev += 1
-    print "## head-rev == " + str(self.head_rev)
-    return self.head_rev
+    self.repo.head_rev += 1
+    print "## head-rev == " + str(self.repo.head_rev)
+    return self.repo.head_rev
+
+  def svn_update(self, rpath='', rev='HEAD'):
+    "Update the WC to the specified revision"
+    lpath = local_path(rpath)
+    actions.run_and_verify_update(lpath, None, None, None)
 
 #  def svn_merge(self, rev_spec, source, target, exp_out=None):
 #    """Merge a single change from path 'source' to path 'target'.

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/testcase.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/testcase.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/testcase.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/testcase.py Tue Aug 10 18:06:17 2010
@@ -63,6 +63,20 @@ class TestCase:
     }
 
   def __init__(self, delegate=None, cond_func=lambda: True, doc=None, wip=None):
+    """Create a test case instance based on DELEGATE.
+
+    COND_FUNC is a callable that is evaluated at test run time and should
+    return a boolean value that determines how a pass or failure is
+    interpreted: see the specialized kinds of test case such as XFail and
+    Skip for details.  The evaluation of COND_FUNC is deferred so that it
+    can base its decision on useful bits of information that are not
+    available at __init__ time (like the fact that we're running over a
+    particular RA layer).
+
+    DOC is ...
+
+    WIP is ...
+    """
     assert hasattr(cond_func, '__call__')
 
     self._delegate = delegate
@@ -163,7 +177,9 @@ class XFail(TestCase):
     TEST_CASE is run normally.  The evaluation of COND_FUNC is
     deferred so that it can base its decision on useful bits of
     information that are not available at __init__ time (like the fact
-    that we're running over a particular RA layer)."""
+    that we're running over a particular RA layer).
+
+    WIP is ..."""
 
     TestCase.__init__(self, create_test_case(test_case), cond_func, wip=wip)
 
@@ -190,7 +206,7 @@ class Skip(TestCase):
   """A test that will be skipped if its conditional is true."""
 
   def __init__(self, test_case, cond_func=lambda: True):
-    """Create an Skip instance based on TEST_CASE.  COND_FUNC is a
+    """Create a Skip instance based on TEST_CASE.  COND_FUNC is a
     callable that is evaluated at test run time and should return a
     boolean value.  If COND_FUNC returns true, then TEST_CASE is
     skipped; otherwise, TEST_CASE is run normally.

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/verify.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/verify.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/verify.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/verify.py Tue Aug 10 18:06:17 2010
@@ -79,7 +79,7 @@ def createExpectedOutput(expected, outpu
     expected = ExpectedOutput(expected)
   elif isinstance(expected, str):
     expected = RegexOutput(expected, match_all)
-  elif expected == AnyOutput:
+  elif expected is AnyOutput:
     expected = AnyOutput()
   elif expected is not None and not isinstance(expected, ExpectedOutput):
     raise SVNIncorrectDatatype("Unexpected type for '%s' data" % output_type)
@@ -87,6 +87,10 @@ def createExpectedOutput(expected, outpu
 
 class ExpectedOutput:
   """Contains expected output, and performs comparisons."""
+
+  is_regex = False
+  is_unordered = False
+
   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
@@ -96,12 +100,14 @@ class ExpectedOutput:
     strings among the matching ones."""
     self.output = output
     self.match_all = match_all
-    self.is_reg_exp = False
 
   def __str__(self):
     return str(self.output)
 
   def __cmp__(self, other):
+    raise 'badness'
+
+  def matches(self, other):
     """Return whether SELF.output matches OTHER (which may be a list
     of newline-terminated lines, or a single string).  Either value
     may be None."""
@@ -114,77 +120,64 @@ class ExpectedOutput:
     else:
       actual = other
 
-    if isinstance(actual, list):
-      if isinstance(expected, str):
-        expected = [expected]
-      is_match = self.is_equivalent_list(expected, actual)
-    elif isinstance(actual, str):
-      is_match = self.is_equivalent_line(expected, actual)
-    else: # unhandled type
-      is_match = False
+    if not isinstance(actual, list):
+      actual = [actual]
+    if not isinstance(expected, list):
+      expected = [expected]
 
-    if is_match:
-      return 0
-    else:
-      return 1
+    return self.is_equivalent_list(expected, actual)
 
   def is_equivalent_list(self, expected, actual):
     "Return whether EXPECTED and ACTUAL are equivalent."
-    if not self.is_reg_exp:
+    if not self.is_regex:
       if self.match_all:
         # The EXPECTED lines must match the ACTUAL lines, one-to-one, in
         # the same order.
-        if len(expected) != len(actual):
-          return False
-        for i in range(0, len(actual)):
-          if not self.is_equivalent_line(expected[i], actual[i]):
-            return False
-        return True
-      else:
-        # 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 self.is_equivalent_line(expected[i_expected], actual_line):
-            i_expected += 1
-            if i_expected == len(expected):
-              return True
-        return False
+        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:
-      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
+      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
+    # 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
 
-      for i in range(0, len(actual)):
-        if self.match_all:
-          if not self.is_equivalent_line(expected_re, actual[i]):
-            all_lines_match_re = False
-            break
-        else:
-          if self.is_equivalent_line(expected_re, actual[i]):
-            return True
-      return all_lines_match_re
+    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):
+          return True
 
-  def is_equivalent_line(self, expected, actual):
-    "Return whether EXPECTED and ACTUAL are equal."
-    return expected == actual
+    return all_lines_match_re
 
   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, False, False)
+    display_lines(message, label, self.output, actual,
+                  self.is_regex, self.is_unordered)
+
 
 class AnyOutput(ExpectedOutput):
   def __init__(self):
@@ -192,86 +185,72 @@ class AnyOutput(ExpectedOutput):
 
   def is_equivalent_list(self, ignored, actual):
     if len(actual) == 0:
-      # Empty text or empty list -- either way, no output!
-      return False
-    elif isinstance(actual, list):
-      for line in actual:
-        if self.is_equivalent_line(None, line):
-          return True
+      # No actual output. No match.
       return False
-    else:
-      return True
 
-  def is_equivalent_line(self, ignored, actual):
-    return len(actual) > 0
+    for line in actual:
+      # If any line has some text, then there is output, so we match.
+      if line:
+        return True
+
+    # We did not find a line with text. No match.
+    return False
 
   def display_differences(self, message, label, actual):
     if message:
       print(message)
 
-class RegexOutput(ExpectedOutput):
-  def __init__(self, output, match_all=True, is_reg_exp=True):
-    self.output = output
-    self.match_all = match_all
-    self.is_reg_exp = is_reg_exp
 
-  def is_equivalent_line(self, expected, actual):
-    "Return whether the regex EXPECTED matches the ACTUAL text."
-    return re.match(expected, actual) is not None
+class RegexOutput(ExpectedOutput):
+  is_regex = True
 
-  def display_differences(self, message, label, actual):
-    display_lines(message, label, self.output, actual, True, False)
 
 class UnorderedOutput(ExpectedOutput):
   """Marks unordered output, and performs comparisons."""
 
+  is_unordered = True
+
   def __cmp__(self, other):
-    "Handle ValueError."
-    try:
-      return ExpectedOutput.__cmp__(self, other)
-    except ValueError:
-      return 1
+    raise 'badness'
 
   def is_equivalent_list(self, expected, actual):
     "Disregard the order of ACTUAL lines during comparison."
+
+    e_set = set(expected)
+    a_set = set(actual)
+
     if self.match_all:
-      if len(expected) != len(actual):
+      if len(e_set) != len(a_set):
         return False
-      expected = list(expected)
-      for actual_line in actual:
-        try:
-          i = self.is_equivalent_line(expected, actual_line)
-          expected.pop(i)
-        except ValueError:
-          return False
-      return True
-    else:
-      for actual_line in actual:
-        try:
-          self.is_equivalent_line(expected, actual_line)
-          return True
-        except ValueError:
-          pass
+      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
+
+      # All expected lines must be in the output.
+      return e_set == a_set
+
+    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 is_equivalent_line(self, expected, actual):
-    """Return the index into the EXPECTED lines of the line ACTUAL.
-    Raise ValueError if not found."""
-    return expected.index(actual)
+    # If any of the expected lines are in the output, then we match.
+    return len(e_set.intersection(a_set)) > 0
 
-  def display_differences(self, message, label, actual):
-    display_lines(message, label, self.output, actual, False, True)
 
 class UnorderedRegexOutput(UnorderedOutput, RegexOutput):
-  def is_equivalent_line(self, expected, actual):
-    for i in range(0, len(expected)):
-      if RegexOutput.is_equivalent_line(self, expected[i], actual):
-        return i
-      else:
-        raise ValueError("'%s' not found" % actual)
-
-  def display_differences(self, message, label, actual):
-    display_lines(message, label, self.output, actual, True, True)
+  is_regex = True
+  is_unordered = True
 
 
 ######################################################################
@@ -331,7 +310,7 @@ def compare_and_display_lines(message, l
     actual = [actual]
   actual = [line for line in actual if not line.startswith('DBG:')]
 
-  if expected != actual:
+  if not expected.matches(actual):
     expected.display_differences(message, label, actual)
     raise raisable
 

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnversion_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnversion_tests.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnversion_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svnversion_tests.py Tue Aug 10 18:06:17 2010
@@ -3,7 +3,7 @@
 #  svnversion_tests.py:  testing the 'svnversion' tool.
 #
 #  Subversion is a tool for revision control.
-#  See http://subversion.tigris.org for more information.
+#  See http://subversion.apache.org for more information.
 #
 # ====================================================================
 #    Licensed to the Apache Software Foundation (ASF) under one

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/switch_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/switch_tests.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/switch_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/switch_tests.py Tue Aug 10 18:06:17 2010
@@ -3,7 +3,7 @@
 #  switch_tests.py:  testing `svn switch'.
 #
 #  Subversion is a tool for revision control.
-#  See http://subversion.tigris.org for more information.
+#  See http://subversion.apache.org for more information.
 #
 # ====================================================================
 #    Licensed to the Apache Software Foundation (ASF) under one
@@ -1612,6 +1612,11 @@ def mergeinfo_switch_elision(sbox):
     'E/alpha' : Item(status='U '),
     'E/beta'  : Item(status='U '),
     })
+  expected_mergeinfo_output = svntest.wc.State(B_COPY_1_path, {
+    '' : Item(status=' U'),
+    })
+  expected_elision_output = svntest.wc.State(B_COPY_1_path, {
+    })
   expected_merge_status = svntest.wc.State(B_COPY_1_path, {
     ''        : Item(status=' M', wc_rev=2),
     'lambda'  : Item(status='  ', wc_rev=2),
@@ -1632,9 +1637,10 @@ def mergeinfo_switch_elision(sbox):
   saved_cwd = os.getcwd()
 
   svntest.actions.run_and_verify_merge(B_COPY_1_path, '2', '4',
-                                       sbox.repo_url + \
-                                       '/A/B',
+                                       sbox.repo_url + '/A/B', None,
                                        expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
                                        expected_merge_disk,
                                        expected_merge_status,
                                        expected_skip,
@@ -1660,6 +1666,11 @@ def mergeinfo_switch_elision(sbox):
     'alpha' : Item(status='U '),
     'beta'  : Item(status='U '),
     })
+  expected_mergeinfo_output = svntest.wc.State(E_COPY_2_path, {
+    '' : Item(status=' U'),
+    })
+  expected_elision_output = svntest.wc.State(E_COPY_2_path, {
+    })
   expected_merge_status = svntest.wc.State(E_COPY_2_path, {
     ''      : Item(status=' M', wc_rev=2),
     'alpha' : Item(status='M ', wc_rev=2),
@@ -1674,9 +1685,10 @@ def mergeinfo_switch_elision(sbox):
   saved_cwd = os.getcwd()
 
   svntest.actions.run_and_verify_merge(E_COPY_2_path, '2', '4',
-                                       sbox.repo_url + \
-                                       '/A/B/E',
+                                       sbox.repo_url + '/A/B/E', None,
                                        expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
                                        expected_merge_disk,
                                        expected_merge_status,
                                        expected_skip,
@@ -2320,13 +2332,53 @@ def tree_conflicts_on_switch_1_1(sbox):
   # Update to the target rev.
   expected_status.tweak(wc_rev=3)
 
+  expected_info = {
+    'F/alpha' : {
+      'Tree conflict' :
+        '^local delete, incoming edit upon switch'
+        + ' Source  left: .file.*/F/alpha@2'
+        + ' Source right: .file.*/F/alpha@3$',
+    },
+    'DF/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming edit upon switch'
+        + ' Source  left: .dir.*/DF/D1@2'
+        + ' Source right: .dir.*/DF/D1@3$',
+    },
+    'DDF/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming edit upon switch'
+        + ' Source  left: .dir.*/DDF/D1@2'
+        + ' Source right: .dir.*/DDF/D1@3$',
+    },
+    'D/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming edit upon switch'
+        + ' Source  left: .dir.*/D/D1@2'
+        + ' Source right: .dir.*/D/D1@3$',
+    },
+    'DD/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming edit upon switch'
+        + ' Source  left: .dir.*/DD/D1@2'
+        + ' Source right: .dir.*/DD/D1@3$',
+    },
+    'DDD/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming edit upon switch'
+        + ' Source  left: .dir.*/DDD/D1@2'
+        + ' Source right: .dir.*/DDD/D1@3$',
+    },
+  }
+
   svntest.actions.deep_trees_run_tests_scheme_for_switch(sbox,
     [ DeepTreesTestCase("local_tree_del_incoming_leaf_edit",
                         tree_del,
                         leaf_edit,
                         expected_output,
                         expected_disk,
-                        expected_status) ] )
+                        expected_status,
+                        expected_info = expected_info) ] )
 
 
 def tree_conflicts_on_switch_1_2(sbox):
@@ -2372,13 +2424,53 @@ def tree_conflicts_on_switch_1_2(sbox):
                        'DD/D1/D2',
                        'DDD/D1/D2/D3')
 
+  expected_info = {
+    'F/alpha' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .file.*/F/alpha@2'
+        + ' Source right: .none.*/F/alpha@3$',
+    },
+    'DF/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming edit upon switch'
+        + ' Source  left: .dir.*/DF/D1@2'
+        + ' Source right: .dir.*/DF/D1@3$',
+    },
+    'DDF/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming edit upon switch'
+        + ' Source  left: .dir.*/DDF/D1@2'
+        + ' Source right: .dir.*/DDF/D1@3$',
+    },
+    'D/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .dir.*/D/D1@2'
+        + ' Source right: .none.*/D/D1@3$',
+    },
+    'DD/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming edit upon switch'
+        + ' Source  left: .dir.*/DD/D1@2'
+        + ' Source right: .dir.*/DD/D1@3$',
+    },
+    'DDD/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming edit upon switch'
+        + ' Source  left: .dir.*/DDD/D1@2'
+        + ' Source right: .dir.*/DDD/D1@3$',
+    },
+  }
+
   svntest.actions.deep_trees_run_tests_scheme_for_switch(sbox,
     [ DeepTreesTestCase("local_tree_del_incoming_leaf_del",
                         tree_del,
                         leaf_del,
                         expected_output,
                         expected_disk,
-                        expected_status) ] )
+                        expected_status,
+                        expected_info = expected_info) ] )
 
 
 def tree_conflicts_on_switch_2_1(sbox):
@@ -2413,13 +2505,54 @@ def tree_conflicts_on_switch_2_1(sbox):
     'DDF/D1/D2/gamma',
     copied='+', wc_rev='-')
   expected_status.tweak('', switched='S')
+
+  expected_info = {
+    'F/alpha' : {
+      'Tree conflict' :
+        '^local edit, incoming delete upon switch'
+        + ' Source  left: .file.*/F/alpha@2'
+        + ' Source right: .none.*/F/alpha@3$',
+    },
+    'DF/D1' : {
+      'Tree conflict' :
+        '^local edit, incoming delete upon switch'
+        + ' Source  left: .dir.*/DF/D1@2'
+        + ' Source right: .none.*/DF/D1@3$',
+    },
+    'DDF/D1' : {
+      'Tree conflict' :
+        '^local edit, incoming delete upon switch'
+        + ' Source  left: .dir.*/DDF/D1@2'
+        + ' Source right: .none.*/DDF/D1@3$',
+    },
+    'D/D1' : {
+      'Tree conflict' :
+        '^local edit, incoming delete upon switch'
+        + ' Source  left: .dir.*/D/D1@2'
+        + ' Source right: .none.*/D/D1@3$',
+    },
+    'DD/D1' : {
+      'Tree conflict' :
+        '^local edit, incoming delete upon switch'
+        + ' Source  left: .dir.*/DD/D1@2'
+        + ' Source right: .none.*/DD/D1@3$',
+    },
+    'DDD/D1' : {
+      'Tree conflict' :
+        '^local edit, incoming delete upon switch'
+        + ' Source  left: .dir.*/DDD/D1@2'
+        + ' Source right: .none.*/DDD/D1@3$',
+    },
+  }
+
   svntest.actions.deep_trees_run_tests_scheme_for_switch(sbox,
     [ DeepTreesTestCase("local_leaf_edit_incoming_tree_del",
                         leaf_edit,
                         tree_del,
                         expected_output,
                         expected_disk,
-                        expected_status) ] )
+                        expected_status,
+                        expected_info = expected_info) ] )
 
 
 def tree_conflicts_on_switch_2_2(sbox):
@@ -2467,13 +2600,53 @@ def tree_conflicts_on_switch_2_2(sbox):
                        'DDF/D1',
                        'DDF/D1/D2',)
 
+  expected_info = {
+    'F/alpha' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .file.*/F/alpha@2'
+        + ' Source right: .none.*/F/alpha@3$',
+    },
+    'DF/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .dir.*/DF/D1@2'
+        + ' Source right: .none.*/DF/D1@3$',
+    },
+    'DDF/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .dir.*/DDF/D1@2'
+        + ' Source right: .none.*/DDF/D1@3$',
+    },
+    'D/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .dir.*/D/D1@2'
+        + ' Source right: .none.*/D/D1@3$',
+    },
+    'DD/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .dir.*/DD/D1@2'
+        + ' Source right: .none.*/DD/D1@3$',
+    },
+    'DDD/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .dir.*/DDD/D1@2'
+        + ' Source right: .none.*/DDD/D1@3$',
+    },
+  }
+
   svntest.actions.deep_trees_run_tests_scheme_for_switch(sbox,
     [ DeepTreesTestCase("local_leaf_del_incoming_tree_del",
                         leaf_del,
                         tree_del,
                         expected_output,
                         expected_disk,
-                        expected_status) ] )
+                        expected_status,
+                        expected_info = expected_info) ] )
 
 
 def tree_conflicts_on_switch_3(sbox):
@@ -2516,13 +2689,53 @@ def tree_conflicts_on_switch_3(sbox):
                        'DDF/D1',
                        'DDF/D1/D2',)
 
+  expected_info = {
+    'F/alpha' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .file.*/F/alpha@2'
+        + ' Source right: .none.*/F/alpha@3$',
+    },
+    'DF/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .dir.*/DF/D1@2'
+        + ' Source right: .none.*/DF/D1@3$',
+    },
+    'DDF/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .dir.*/DDF/D1@2'
+        + ' Source right: .none.*/DDF/D1@3$',
+    },
+    'D/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .dir.*/D/D1@2'
+        + ' Source right: .none.*/D/D1@3$',
+    },
+    'DD/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .dir.*/DD/D1@2'
+        + ' Source right: .none.*/DD/D1@3$',
+    },
+    'DDD/D1' : {
+      'Tree conflict' :
+        '^local delete, incoming delete upon switch'
+        + ' Source  left: .dir.*/DDD/D1@2'
+        + ' Source right: .none.*/DDD/D1@3$',
+    },
+  }
+
   svntest.actions.deep_trees_run_tests_scheme_for_switch(sbox,
     [ DeepTreesTestCase("local_tree_del_incoming_tree_del",
                         tree_del,
                         tree_del,
                         expected_output,
                         expected_disk,
-                        expected_status) ] )
+                        expected_status,
+                        expected_info = expected_info) ] )
 
 
 def single_file_relocate(sbox):

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/trans_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/trans_tests.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/trans_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/trans_tests.py Tue Aug 10 18:06:17 2010
@@ -3,7 +3,7 @@
 #  trans_tests.py:  testing eol conversion and keyword substitution
 #
 #  Subversion is a tool for revision control.
-#  See http://subversion.tigris.org for more information.
+#  See http://subversion.apache.org for more information.
 #
 # ====================================================================
 #    Licensed to the Apache Software Foundation (ASF) under one

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/tree_conflict_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/tree_conflict_tests.py?rev=984153&r1=984152&r2=984153&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/tree_conflict_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/tree_conflict_tests.py Tue Aug 10 18:06:17 2010
@@ -3,7 +3,7 @@
 #  tree_conflict_tests.py:  testing tree-conflict cases.
 #
 #  Subversion is a tool for revision control.
-#  See http://subversion.tigris.org for more information.
+#  See http://subversion.apache.org for more information.
 #
 # ====================================================================
 #    Licensed to the Apache Software Foundation (ASF) under one
@@ -1130,6 +1130,67 @@ def query_absent_tree_conflicted_dir(sbo
   # using info:
   run_and_verify_svn(None, None, [], 'info', C_C_path)
 
+#----------------------------------------------------------------------
+
+def up_add_onto_add_revert(sbox):
+  "issue #3608: reverting an add onto add conflict"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  wc2_dir = sbox.add_wc_path('wc2')
+  svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
+                                     sbox.repo_url, wc2_dir)
+
+  file1 = os.path.join(wc_dir, 'newfile')
+  file2 = os.path.join(wc2_dir, 'newfile')
+
+  dir1 = os.path.join(wc_dir, 'NewDir')
+  dir2 = os.path.join(wc2_dir, 'NewDir')
+
+  main.run_svn(None, 'cp', os.path.join(wc_dir, 'iota'), file1)
+  main.run_svn(None, 'cp', os.path.join(wc2_dir, 'iota'), file2)
+
+  main.run_svn(None, 'cp', os.path.join(wc_dir, 'A/C'), dir1)
+  main.run_svn(None, 'cp', os.path.join(wc2_dir, 'A/C'), dir2)
+
+  main.run_svn(None, 'ci', wc_dir, '-m', 'Added file')
+
+  expected_disk = main.greek_state.copy()
+  expected_disk.add({
+    'newfile'           : Item(contents="This is the file 'iota'.\n"),
+    'NewDir'            : Item(),
+    })
+
+  expected_status = get_virginal_state(wc2_dir, 2)
+  expected_status.add({
+    'newfile' : Item(status='A ', copied='+', treeconflict='C', wc_rev='-'),
+    'NewDir'  : Item(status='A ', copied='+', treeconflict='C', wc_rev='-'),
+    })
+
+  run_and_verify_update(wc2_dir,
+                        None, expected_disk, expected_status,
+                        None, None, None, None, None, 1,
+                        wc2_dir)
+
+  # Currently (r927086), this removes dir2 and file2 in a way that
+  # they don't reappear after update.
+  main.run_svn(None, 'revert', file2)
+  main.run_svn(None, 'revert', dir2)
+
+  expected_status = get_virginal_state(wc2_dir, 2)
+  expected_status.add({
+    'newfile' : Item(status='  ', wc_rev='2'),
+    'NewDir'  : Item(status='  ', wc_rev='2'),
+    })
+
+  # Expected behavior is that after revert + update the tree matches
+  # the repository
+  run_and_verify_update(wc2_dir,
+                        None, expected_disk, expected_status,
+                        None, None, None, None, None, 1,
+                        wc2_dir)
+
+
 
 #######################################################################
 # Run the tests
@@ -1159,6 +1220,7 @@ test_list = [ None,
               XFail(keep_local_del_tc_is_target),
               XFail(force_del_tc_is_target),
               XFail(query_absent_tree_conflicted_dir),
+              XFail(up_add_onto_add_revert),
              ]
 
 if __name__ == '__main__':