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/11 00:07:31 UTC

svn commit: r984234 [18/20] - in /subversion/branches/ignore-mergeinfo: ./ build/ build/ac-macros/ build/generator/ notes/ notes/api-errata/ notes/obliterate/ notes/obliterate/fspec-cc1/ notes/rename-tracking/ notes/svnpatch/ notes/tree-conflicts/ note...

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/merge_reintegrate_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/merge_reintegrate_tests.py?rev=984234&r1=984233&r2=984234&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/merge_reintegrate_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/merge_reintegrate_tests.py Tue Aug 10 22:07:24 2010
@@ -1865,6 +1865,380 @@ def reintegrate_with_subtree_merges(sbox
                                        None, None, None, None,
                                        None, 1, 1, "--reintegrate")
 
+#----------------------------------------------------------------------
+# Test for issue #3654 'added subtrees with mergeinfo break reintegrate'.
+def added_subtrees_with_mergeinfo_break_reintegrate(sbox):
+  "added subtrees with mergeinfo break reintegrate"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # Some paths we'll care about
+  A_path           = os.path.join(wc_dir, "A")
+  nu_path          = os.path.join(wc_dir, "A", "C", "nu")
+  mu_path          = os.path.join(wc_dir, "A", "mu")
+  A_COPY_path      = os.path.join(wc_dir, "A_COPY")
+  lambda_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "lambda")
+  A_COPY_2_path    = os.path.join(wc_dir, "A_COPY_2")
+  nu_COPY_2_path   = os.path.join(wc_dir, "A_COPY_2", "C", "nu")
+  
+  # Branch A@1 to A_COPY and A_COPY_2 in r2 and r3 respectively.
+  # Make some changes under 'A' in r4-7.
+  wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2)
+
+  # r8 - Add a new file A_COPY_2/C/nu.
+  svntest.main.file_write(nu_COPY_2_path, "This is the file 'nu'.\n")
+  svntest.actions.run_and_verify_svn(None, None, [], 'add', nu_COPY_2_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Add new file in A_COPY_2 branch',
+                                     wc_dir)
+
+  
+  # r9 - Cyclic cherry pick merge r8 from A_COPY_2 back to A.
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
+                                     'merge', '-c', '8',
+                                     sbox.repo_url + '/A_COPY_2',
+                                     A_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Merge r8 from A_COPY_2 to A.',
+                                     wc_dir)
+
+  # r10 - Make an edit to A_COPY_2/C/nu.
+  svntest.main.file_write(nu_COPY_2_path, "A_COPY_2 edit to file 'nu'.\n")
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Edit new file on A_COPY_2 branch',
+                                     wc_dir)
+
+  # r11 - Cyclic subtree cherry pick merge r10 from A_COPY_2/C/nu
+  # back to A/C/nu.
+  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
+                                     'merge', '-c', '10',
+                                     sbox.repo_url + '/A_COPY_2/C/nu',
+                                     nu_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m',
+                                     'Merge r8 from A_COPY_2/C/nu to A/C/nu.',
+                                     wc_dir)
+
+  # r12 - Edit under A_COPY.
+  svntest.main.file_write(mu_path, "mu edits on A_COPY.\n")
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Work on A_COPY branch.',
+                                     wc_dir)
+
+  # r13 - Sync merge A to A_COPY in preparation for reintegrate.
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
+                                     'merge', sbox.repo_url + '/A', A_COPY_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m',
+                                     'Prep for reintegrate: Sync A to A_COPY.',
+                                     wc_dir)
+
+  # r14 - Reintegrate A_COPY to A.
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
+                                     'merge', '--reintegrate',
+                                     sbox.repo_url + '/A_COPY', A_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m',
+                                     'Reintegrate A_COPY to A.',
+                                     wc_dir)
+
+  # r15 - Delete A_COPY.
+  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
+                                     'delete', A_COPY_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m',
+                                     'Delete A_COPY branch', wc_dir)
+
+  # r16 - Create new A_COPY from A@HEAD=15.
+  #
+  # Update so we copy HEAD:
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
+                                     'copy', A_path, A_COPY_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m',
+                                     'Create new A_COPY branch from A', wc_dir)
+
+  # r17 - Unrelated edits under both A and A_COPY.  
+  svntest.main.file_write(nu_path, "Trunk work on nu.\n")
+  svntest.main.file_write(lambda_COPY_path, "lambda edit on A_COPY.\n")
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m',
+                                     'Unrelated edits on A and A_COPY branch.',
+                                     wc_dir)
+
+  # r18 - Sync A to A_COPY in preparation for another reintegrate.
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
+                                     'merge', sbox.repo_url + '/A', A_COPY_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci', '-m',
+                                     'Prep for reintegrate: Sync A to A_COPY.',
+                                     wc_dir)
+
+  # Reintegrate A_COPY back to A.  We just synced A_COPY with A, so this
+  # should work.  The only text change should be the change made to
+  # A_COPY/B/lambda in r17 after the new A_COPY was created.
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+  expected_output = wc.State(A_path, {
+    ''         : Item(status=' U'),
+    'B/lambda' : Item(status='U '),
+    'C/nu'     : Item(status=' U'),
+    })
+  expected_mergeinfo_output = wc.State(A_path, {
+    ''     : Item(status=' G'),
+    'C/nu' : Item(status=' G'),
+    })
+  expected_elision_output = wc.State(A_path, {
+    })
+  expected_status = wc.State(A_path, {
+    ''          : Item(status=' M'),
+    'B'         : Item(status='  '),
+    'mu'        : Item(status='  '),
+    'B/E'       : Item(status='  '),
+    'B/E/alpha' : Item(status='  '),
+    'B/E/beta'  : Item(status='  '),
+    'B/lambda'  : Item(status='M '),
+    'B/F'       : Item(status='  '),
+    'C'         : Item(status='  '),
+    'C/nu'      : Item(status=' M'),
+    'D'         : Item(status='  '),
+    'D/G'       : Item(status='  '),
+    'D/G/pi'    : Item(status='  '),
+    'D/G/rho'   : Item(status='  '),
+    'D/G/tau'   : Item(status='  '),
+    'D/gamma'   : Item(status='  '),
+    'D/H'       : Item(status='  '),
+    'D/H/chi'   : Item(status='  '),
+    'D/H/psi'   : Item(status='  '),
+    'D/H/omega' : Item(status='  '),
+    })
+  expected_status.tweak(wc_rev=18)
+  expected_disk = wc.State('', {
+    ''          : Item(props={SVN_PROP_MERGEINFO :
+                              '/A_COPY:2-13,16-18\n'
+                              #         ^     ^
+                              #         |     |
+                              #   from _|     |
+                              #    1st        |
+                              # reintegrate   |
+                              #               |
+                              #        from this reintegrate
+                              #
+                              '/A_COPY_2:8'}), # <-- From cyclic merge in r9
+    'B'         : Item(),
+    'mu'        : Item("mu edits on A_COPY.\n"), # From earlier reintegrate.
+    'B/E'       : Item(),
+    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
+    'B/E/beta'  : Item("New content"),
+    'B/lambda'  : Item("lambda edit on A_COPY.\n"), # From this reintegrate.
+    'B/F'       : Item(),
+    'C'         : Item(),
+    'C/nu'      : Item("Trunk work on nu.\n",
+                       props={SVN_PROP_MERGEINFO :
+                              '/A_COPY/C/nu:16-18\n'
+                              '/A_COPY_2/C/nu:10'}), # <-- From cyclic
+                                                     # merge in r11
+    'D'         : Item(),
+    'D/G'       : Item(),
+    'D/G/pi'    : Item("This is the file 'pi'.\n"),
+    'D/G/rho'   : Item("New content"),
+    'D/G/tau'   : Item("This is the file 'tau'.\n"),
+    'D/gamma'   : Item("This is the file 'gamma'.\n"),
+    'D/H'       : Item(),
+    'D/H/chi'   : Item("This is the file 'chi'.\n"),
+    'D/H/psi'   : Item("New content"),
+    'D/H/omega' : Item("New content"),
+    })
+  expected_skip = wc.State(A_COPY_path, {})
+  svntest.actions.run_and_verify_merge(A_path, None, None,
+                                       sbox.repo_url + '/A_COPY', None,
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       None, None, None, None,
+                                       None, 1, 1, "--reintegrate")
+
+#----------------------------------------------------------------------
+# Test for issue #3648 '2-URL merges incorrectly reverse-merge mergeinfo
+# for merge target'.
+def two_URL_merge_removes_valid_mergefino_from_target(sbox):
+  "2-URL merge removes valid mergefino from target"
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+
+  # Some paths we'll care about
+  lambda_COPY_path = os.path.join(wc_dir, "A_COPY", "B", "lambda")
+  mu_path          = os.path.join(wc_dir, "A", "mu")
+  A_COPY_path      = os.path.join(wc_dir, "A_COPY")
+  A_COPY_2_path    = os.path.join(wc_dir, "A_COPY_2")
+  
+  # Branch A@1 to A_COPY r2
+  # Branch A@1 to A_COPY_2 in r3.
+  # Make some changes under 'A' in r4-7.
+  wc_disk, wc_status = set_up_branch(sbox, nbr_of_branches=2)
+
+  # r8 - A simple text edit on the A_COPY branch.
+  svntest.main.file_write(lambda_COPY_path, "Edit on 'branch 1'.\n")
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', "Work on 'branch 1'.",
+                                     wc_dir)
+
+  # r9 - Sync the A_COPY branch with A up the HEAD (r8).  Now A_COPY
+  # differs from A only by the change made in r8 and by the mergeinfo
+  # '/A:2-8' on A_COPY which was set to describe the merge.
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
+                                     'merge', sbox.repo_url + '/A', A_COPY_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Sync A to A_COPY.',
+                                     wc_dir)
+
+  # r10 - A simple text edit on our "trunk" A.
+  svntest.main.file_write(mu_path, "Edit on 'trunk'.\n")
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', "Work on 'trunk'",
+                                     wc_dir)
+
+  # r11 - Sync the A_COPY_2 branch with A up to HEAD (r10).
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
+                                     'merge', sbox.repo_url + '/A',
+                                     A_COPY_2_path)
+  svntest.actions.run_and_verify_svn(None, None, [], 'ci',
+                                     '-m', 'Sync A to A_COPY_2.',
+                                     wc_dir)
+
+  # Confirm that the mergeinfo on each branch is what we expect.
+  svntest.actions.run_and_verify_svn(None,
+                                     [A_COPY_path + ' - /A:2-8\n'],
+                                     [], 'pg', SVN_PROP_MERGEINFO,
+                                     '-R', A_COPY_path)
+  svntest.actions.run_and_verify_svn(None,
+                                     [A_COPY_2_path + ' - /A:3-10\n'],
+                                     [], 'pg', SVN_PROP_MERGEINFO,
+                                     '-R', A_COPY_2_path)
+
+  # Now say we want to apply the changes made on the first branch (A_COPY)
+  # to the second branch (A_COPY_2).  One way to do this is a 2-URL merge
+  # between A at the revision last synced to A_COPY and A_COPY_2 at HEAD (r11),
+  # i.e.:
+  #
+  #   svn merge ^/A@8 ^/A_COPY@11 A_COPY_2_WC
+  #
+  # Recall from the note on r9 that this diff is simply the one text change
+  # made on branch 1 and some mergeinfo:
+  # 
+  #   >svn diff ^/A@8 ^/A_COPY@11
+  #   Index: B/lambda
+  #   ===================================================================
+  #   --- B/lambda    (.../A) (revision 8)
+  #   +++ B/lambda    (.../A_COPY)    (revision 11)
+  #   @@ -1 +1 @@
+  #   -This is the file 'lambda'.
+  #   +Edit on 'branch 1'.
+  #
+  #   Property changes on: .
+  #   ___________________________________________________________________
+  #   Added: svn:mergeinfo
+  #      Merged /A:r2-8
+  #
+  # The mergeinfo diff is already represented in A_COPY_2's mergeinfo, so the
+  # result of the merge should be the text change to lambda and the addition
+  # of mergeinfo showing that the history of A_COPY is now part of A_COPY_2,
+  # i.e. '/A_COPY:2-11'
+  #
+  # This test is currently marked as XFail because this is not what happens.
+  # Well, actually, all the above *does* happen, but as discussed in
+  # http://svn.haxx.se/dev/archive-2010-05/0292.shtml, the merge removes some
+  # of the valid mergeinfo on A_COPY_2 that describes the sync merge made in
+  # r9:
+  #
+  #   >svn pl -vR A_COPY_2
+  #   Properties on 'A_COPY_2':
+  #     svn:mergeinfo
+  #       /A:9-10
+  #       /A_COPY:2-11
+  #
+  #   >svn diff --depth empty A_COPY_2
+  #
+  #   Property changes on: A_COPY_2
+  #   ___________________________________________________________________
+  #   Modified: svn:mergeinfo
+  #      Reverse-merged /A:r3-8
+  #      Merged /A_COPY:r2-11
+  #
+  # '/A:r3-8' represents valid, operative changes merged from A to A_COPY_2!
+  # If this merge was committed, subsequent merges would try to reapply the
+  # diff, possibly leading to spurious conflicts.
+  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
+  expected_output = wc.State(A_COPY_2_path, {
+    ''         : Item(status=' G'),
+    'B/lambda' : Item(status='U '),
+    })
+  expected_mergeinfo_output = wc.State(A_COPY_2_path, {
+    '' : Item(status=' G'),
+    })
+  expected_elision_output = wc.State(A_COPY_2_path, {
+    })
+  expected_status = wc.State(A_COPY_2_path, {
+    ''          : Item(status=' M'),
+    'B'         : Item(status='  '),
+    'mu'        : Item(status='  '),
+    'B/E'       : Item(status='  '),
+    'B/E/alpha' : Item(status='  '),
+    'B/E/beta'  : Item(status='  '),
+    'B/lambda'  : Item(status='M '),
+    'B/F'       : Item(status='  '),
+    'C'         : Item(status='  '),
+    'D'         : Item(status='  '),
+    'D/G'       : Item(status='  '),
+    'D/G/pi'    : Item(status='  '),
+    'D/G/rho'   : Item(status='  '),
+    'D/G/tau'   : Item(status='  '),
+    'D/gamma'   : Item(status='  '),
+    'D/H'       : Item(status='  '),
+    'D/H/chi'   : Item(status='  '),
+    'D/H/psi'   : Item(status='  '),
+    'D/H/omega' : Item(status='  '),
+    })
+  expected_status.tweak(wc_rev=11)
+  expected_disk = wc.State('', {
+    ''          : Item(props={SVN_PROP_MERGEINFO :
+                              '/A:3-10\n/A_COPY:2-11'}),
+    'B'         : Item(),
+    'mu'        : Item("Edit on 'trunk'.\n"),
+    'B/E'       : Item(),
+    'B/E/alpha' : Item("This is the file 'alpha'.\n"),
+    'B/E/beta'  : Item("New content"),
+    'B/lambda'  : Item("Edit on 'branch 1'.\n"),
+    'B/F'       : Item(),
+    'C'         : Item(),
+    'D'         : Item(),
+    'D/G'       : Item(),
+    'D/G/pi'    : Item("This is the file 'pi'.\n"),
+    'D/G/rho'   : Item("New content"),
+    'D/G/tau'   : Item("This is the file 'tau'.\n"),
+    'D/gamma'   : Item("This is the file 'gamma'.\n"),
+    'D/H'       : Item(),
+    'D/H/chi'   : Item("This is the file 'chi'.\n"),
+    'D/H/psi'   : Item("New content"),
+    'D/H/omega' : Item("New content"),
+    })
+  expected_skip = wc.State(A_COPY_path, {})
+  svntest.actions.run_and_verify_merge(A_COPY_2_path, 8, 11,
+                                       sbox.repo_url + '/A',
+                                       sbox.repo_url + '/A_COPY',
+                                       expected_output,
+                                       expected_mergeinfo_output,
+                                       expected_elision_output,
+                                       expected_disk,
+                                       expected_status,
+                                       expected_skip,
+                                       None, None, None, None,
+                                       None, 1, 1)
+
 ########################################################################
 # Run the tests
 
@@ -1887,7 +2261,8 @@ test_list = [ None,
               SkipUnless(multiple_reintegrates_from_the_same_branch,
                          server_has_mergeinfo),
               reintegrate_with_self_referential_mergeinfo,
-              reintegrate_with_subtree_merges,
+              added_subtrees_with_mergeinfo_break_reintegrate,
+              XFail(two_URL_merge_removes_valid_mergefino_from_target),
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/merge_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/merge_tests.py?rev=984234&r1=984233&r2=984234&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/merge_tests.py Tue Aug 10 22:07:24 2010
@@ -1710,7 +1710,10 @@ def merge_into_missing(sbox):
   expected_status = wc.State(F_path, {
     ''      : Item(status='  ', wc_rev=1),
     'foo'   : Item(status='! ', wc_rev=2),
-    'Q'     : Item(status='! ', wc_rev='2', entry_rev='?'),
+    'Q'     : Item(status='! ', wc_rev='?'),
+# In some intermediate WC-NG state (since r937468) this was:
+#   'Q'     : Item(status='! ', wc_rev='2', entry_rev='?'),
+# but the expected value is now back what it was.
     })
   expected_skip = wc.State(F_path, {
     'Q'   : Item(),
@@ -1732,7 +1735,10 @@ def merge_into_missing(sbox):
   expected_status = wc.State(F_path, {
     ''      : Item(status=' M', wc_rev=1),
     'foo'   : Item(status='!M', wc_rev=2),
-    'Q'     : Item(status='! ', wc_rev='2', entry_rev='?'),
+    'Q'     : Item(status='! ', wc_rev='?'),
+# In some intermediate WC-NG state (since r937468) this was:
+#   'Q'     : Item(status='! ', wc_rev='2', entry_rev='?'),
+# but the expected value is now back what it was.
     })
   expected_mergeinfo_output = wc.State(F_path, {
     ''    : Item(status=' U'),
@@ -1763,7 +1769,10 @@ def merge_into_missing(sbox):
   expected_status.add({
     'A/B/F'     : Item(status=' M', wc_rev=1),
     'A/B/F/foo' : Item(status='!M', wc_rev=2),
-    'A/B/F/Q'   : Item(status='! ', wc_rev='2', entry_rev='?'),
+    'A/B/F/Q'   : Item(status='! ', wc_rev='?'),
+# In some intermediate WC-NG state (since r937468) this was:
+#  'A/B/F/Q'   : Item(status='! ', wc_rev='2', entry_rev='?'),
+# but the expected value is now back what it was.
     })
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
@@ -10355,17 +10364,17 @@ def foreign_repos(sbox):
   zeta_path = os.path.join(wc_dir, 'A', 'D', 'G', 'Z', 'zeta')
   fred_path = os.path.join(wc_dir, 'A', 'C', 'fred')
 
-  # Add new directories, with properties
+  # Add new directories, with and without properties.
   svntest.main.run_svn(None, 'mkdir', Q_path, Z_path)
-  svntest.main.run_svn(None, 'pset', 'foo', 'bar', Q_path, Z_path)
+  svntest.main.run_svn(None, 'pset', 'foo', 'bar', Z_path)
 
-  # Add new files, with contents and properties.
+  # Add new files, with contents, with and without properties.
   zeta_contents = "This is the file 'zeta'.\n"
   fred_contents = "This is the file 'fred'.\n"
   svntest.main.file_append(zeta_path, zeta_contents)
   svntest.main.file_append(fred_path, fred_contents)
   svntest.main.run_svn(None, 'add', zeta_path, fred_path)
-  svntest.main.run_svn(None, 'pset', 'foo', 'bar', zeta_path, fred_path)
+  svntest.main.run_svn(None, 'pset', 'foo', 'bar', fred_path)
   
   # Modify existing files and directories.
   added_contents = "This is another line of text.\n"
@@ -10400,9 +10409,9 @@ def foreign_repos(sbox):
                          'A/D/H/psi', 'A/D/H/omega')
   expected_disk = svntest.main.greek_state.copy()
   expected_disk.add({
-    'Q'            : Item(props={'foo':'bar'}),
+    'Q'            : Item(),
     'A/D/G/Z'      : Item(props={'foo':'bar'}),
-    'A/D/G/Z/zeta' : Item(contents=zeta_contents,props={'foo':'bar'}),
+    'A/D/G/Z/zeta' : Item(contents=zeta_contents),
     'A/C/fred'     : Item(contents=fred_contents,props={'foo':'bar'}),
     })
   expected_disk.remove('A/B/E/alpha', 'A/D/H', 'A/D/H/chi',
@@ -10429,10 +10438,13 @@ def foreign_repos(sbox):
   ### TODO: Use run_and_verify_merge() ###
   svntest.main.run_svn(None, 'merge', '-c2', sbox.repo_url, wc_dir2)
   svntest.main.run_svn(None, 'ci', '-m', 'Merge from foreign repos', wc_dir2)
+  svntest.actions.verify_disk(wc_dir2, expected_disk, True)
 
   # Now, let's make a third checkout -- our second from the original
   # repository -- and make sure that all the data there is correct.
   # It should look just like the original EXPECTED_DISK.
+  # This is a regression test for issue #3623 in which wc_dir2 had the
+  # correct state but the committed state was wrong.
   wc_dir3 = sbox.add_wc_path('wc3')
   svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
                                      sbox2.repo_url, wc_dir3)

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/prop_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/prop_tests.py?rev=984234&r1=984233&r2=984234&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/prop_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/prop_tests.py Tue Aug 10 22:07:24 2010
@@ -1787,6 +1787,7 @@ def prop_reject_grind(sbox):
 
   iota_path = sbox.ospath('iota')
   mu_path = sbox.ospath('A/mu')
+  mu_prej_path = sbox.ospath('A/mu.prej')
 
   # Create r2 with all the properties we intend to use as incoming-change,
   # and as incoming-delete. Also set up our local-edit and local-delete
@@ -1830,8 +1831,7 @@ def prop_reject_grind(sbox):
   sbox.simple_propdel('del.edit', iota_path)
   sbox.simple_propdel('del.edit2', iota_path)
   sbox.simple_propdel('del.diff', iota_path)
-  ### don't delete this. causes a segfault :-)
-  #sbox.simple_propdel('del.del', iota_path)
+  sbox.simple_propdel('del.del', iota_path)
   sbox.simple_propdel('del.add', iota_path)
   sbox.simple_commit()
 
@@ -1852,9 +1852,69 @@ def prop_reject_grind(sbox):
   svntest.main.run_svn(False, 'merge', '-r2:3', sbox.repo_url + '/iota',
                        mu_path)
 
-  ### need to verify mu.prej
-  ### note that del.add has been erroneously deleted!
-
+  # Check that A/mu.prej reports the expected conflicts:
+  expected_prej = svntest.verify.UnorderedOutput([
+   "Trying to change property 'edit.none' from 'repos' to 'repos.changed',\n"
+   "but the property does not exist.\n",
+
+   "Trying to delete property 'del.del' with value 'repos',\n"
+   "but property with value 'local' is locally deleted.\n",
+
+   "Trying to delete property 'del.edit' with value 'repos',\n"
+   "but the local value is 'local.changed'.\n",
+
+   "Trying to change property 'edit.del' from 'repos' to 'repos.changed',\n"
+   "but it has been locally deleted.\n",
+
+   "Trying to change property 'edit.edit' from 'repos' to 'repos.changed',\n"
+   "but the property has been locally changed from 'local' to 'local.changed'.\n",
+
+   "Trying to delete property 'del.edit2' with value 'repos',\n"
+   "but it has been modified from 'repos' to 'repos.changed'.\n",
+
+   "Trying to delete property 'del.add' with value 'repos',\n"
+   "but property has been locally added with value 'local'.\n",
+
+   "Trying to delete property 'del.diff' with value 'repos',\n"
+   "but the local value is 'local'.\n",
+
+   "Trying to change property 'edit.add' from 'repos' to 'repos.changed',\n"
+   "but property has been locally added with value 'local'.\n",
+
+   "Trying to change property 'edit.diff' from 'repos' to 'repos.changed',\n"
+   "but property already exists with value 'local'.\n",
+
+   "Trying to add new property 'add.add' with value 'repos',\n"
+   "but property already exists with value 'local'.\n",
+
+   "Trying to add new property 'add.diff' with value 'repos',\n"
+   "but property already exists with value 'local'.\n",
+
+   "Trying to create property 'add.del' with value 'repos',\n"
+   "but it has been locally deleted.\n",
+
+   "Trying to add new property 'add.edit' with value 'repos',\n"
+   "but property already exists with value 'local.changed'.\n",
+
+   "\n"  
+   ])
+
+  # Get the contents of mu.prej.  The error messages in the prej file are
+  # two lines each, but there is no guarantee as to order, so remove the
+  # newline between each two line error message and then split the whole
+  # thing into a list of strings on the remaining newline...
+  raw_prej = open(mu_prej_path,
+                     'r').read().replace('\nbut', ' but').split('\n')
+  # ...then put the newlines back in each list item.  That leaves us with
+  # list of two lines strings we can compare to the unordered expected
+  # prej file.
+  actual_prej = []
+  for line in raw_prej:
+      repaired_line = line.replace(' but', '\nbut')
+      actual_prej.append(repaired_line + '\n')
+  
+  svntest.verify.verify_outputs("Expected mu.prej doesn't match actual mu.prej",
+                                actual_prej, None, expected_prej, None)
 
 def obstructed_subdirs(sbox):
   """test properties of obstructed subdirectories"""

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/revert_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/revert_tests.py?rev=984234&r1=984233&r2=984234&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/revert_tests.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/revert_tests.py Tue Aug 10 22:07:24 2010
@@ -968,7 +968,7 @@ def revert_add_over_not_present_dir(sbox
   
   main.run_svn(None, 'mkdir', os.path.join(wc_dir, 'A/C'))
   
-  # This fails in the current WC-NG state (r927318).
+  # This failed in some WC-NG intermediate format (r927318-r958992).
   main.run_svn(None, 'revert', os.path.join(wc_dir, 'A/C'))
   
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
@@ -1000,7 +1000,7 @@ test_list = [ None,
                    status_of_missing_dir_after_revert_replaced_with_history_dir),
               revert_replaced_with_history_file_2,
               revert_tree_conflicts_in_updated_files,
-              XFail(revert_add_over_not_present_dir),
+              revert_add_over_not_present_dir,
              ]
 
 if __name__ == '__main__':

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=984234&r1=984233&r2=984234&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 22:07:24 2010
@@ -457,14 +457,14 @@ def verify_windows_paths_in_repos(sbox):
 # numbered REV in REPO_DIR, which must be in the first shard if we're
 # using a sharded repository.
 def fsfs_file(repo_dir, kind, rev):
-  if svntest.main.server_minor_version >= 5:
-    if svntest.main.fsfs_sharding is None:
+  if svntest.main.options.server_minor_version >= 5:
+    if svntest.main.options.fsfs_sharding is None:
       return os.path.join(repo_dir, 'db', kind, '0', rev)
     else:
       shard = int(rev) // svntest.main.fsfs_sharding
       path = os.path.join(repo_dir, 'db', kind, str(shard), rev)
 
-      if svntest.main.fsfs_packing is None or kind == 'revprops':
+      if svntest.main.options.fsfs_packing is None or kind == 'revprops':
         # we don't pack revprops
         return path
       elif os.path.exists(path):
@@ -1188,6 +1188,67 @@ def dont_drop_valid_mergeinfo_during_inc
                                      'propget', 'svn:mergeinfo', '-R',
                                      sbox.repo_url)
 
+
+def hotcopy_symlink(sbox):
+  "'svnadmin hotcopy' replicates symlink"
+
+  ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2591. ##
+
+  original_repo = sbox.repo_dir
+
+  hotcopy_repo, hotcopy_url = sbox.add_repo_path('hotcopy')
+
+  # Create a repository.
+  svntest.main.safe_rmtree(original_repo, 1)
+  svntest.main.create_repos(original_repo)
+
+  # Create a file, a dir and a missing path outside the repoitory.
+  svntest.main.safe_rmtree(sbox.wc_dir, 1)
+  os.mkdir(sbox.wc_dir)
+  external_file_path = os.path.join(sbox.wc_dir, "file")
+  svntest.main.file_write(external_file_path, "An existing file")
+  external_dir_path = os.path.join(sbox.wc_dir, "dir")
+  os.mkdir(external_dir_path)
+  external_missing_path = os.path.join(sbox.wc_dir, "missing")
+
+  # Symlink definitions: base name -> target relpath.
+  # Check both existing and nonexistent targets.
+  # Check targets both within and outside the source repository.
+  symlinks = [
+    ('in_repos_file',    'format'),
+    ('in_repos_dir',     'conf'),
+    ('in_repos_missing', 'missing'),
+    ('external_file',    os.path.join('..', '..', '..', external_file_path)),
+    ('external_dir',     os.path.join('..', '..', '..', external_dir_path)),
+    ('external_missing', os.path.join('..', '..', '..', external_missing_path)),
+  ]
+
+  # Create symlinks within the repository directory.
+  for name, target_relpath in symlinks:
+    target_path = os.path.join(original_repo, target_relpath)
+    target_abspath = os.path.abspath(target_path)
+
+    # Create two symlinks to each target - one relative, one absolute.
+    symlink_path = os.path.join(original_repo, name)
+    os.symlink(target_relpath, symlink_path + '_rel')
+    os.symlink(target_abspath, symlink_path + '_abs')
+
+  svntest.actions.run_and_verify_svnadmin(
+    None, None, [],
+    "hotcopy", original_repo, hotcopy_repo)
+
+  # Check if the symlinks were copied correctly.
+  for name, target_relpath in symlinks:
+    target_path = os.path.join(original_repo, target_relpath)
+    target_abspath = os.path.abspath(target_path)
+
+    # Check two symlinks to each target - one relative, one absolute.
+    symlink_path = os.path.join(hotcopy_repo, name)
+    if os.readlink(symlink_path + '_rel') != target_relpath:
+      raise svntest.Failure
+    if os.readlink(symlink_path + '_abs') != target_abspath:
+      raise svntest.Failure
+
 ########################################################################
 # Run the tests
 
@@ -1216,6 +1277,7 @@ test_list = [ None,
               SkipUnless(verify_with_invalid_revprops,
                          svntest.main.is_fs_type_fsfs),
               XFail(dont_drop_valid_mergeinfo_during_incremental_loads),
+              SkipUnless(hotcopy_symlink, svntest.main.is_posix_os),
              ]
 
 if __name__ == '__main__':

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=984234&r1=984233&r2=984234&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 22:07:24 2010
@@ -674,9 +674,7 @@ fp.close()"""
 # list all tests here, starting with None:
 test_list = [ None,
               test_misc,
-              ### it would be nice to XFail this, but it throws an assertion
-              ### which leaves a core dump. let's not leave turds right now.
-              Skip(delete_file_in_moved_dir),
+              delete_file_in_moved_dir,
               test_print_property_diffs,
               info_bad_newlines,
               changed_copy_info,

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/__init__.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/__init__.py?rev=984234&r1=984233&r2=984234&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/__init__.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/__init__.py Tue Aug 10 22:07:24 2010
@@ -17,8 +17,10 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-#
-__all__ = ["main", "tree", "actions"]
+
+# any bozos that do "from svntest import *" should die. export nothing
+# to the dumbasses.
+__all__ = [ ]
 
 import sys
 if sys.hexversion < 0x2040000:
@@ -29,6 +31,7 @@ if sys.hexversion < 0x2040000:
 
   # we're skipping this test, not failing, so exit with 0
   sys.exit(0)
+
 try:
   import sqlite3
 except ImportError:

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=984234&r1=984233&r2=984234&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 22:07:24 2010
@@ -1609,7 +1609,7 @@ def get_wc_base_rev(wc_dir):
 def hook_failure_message(hook_name):
   """Return the error message that the client prints for failure of the
   specified hook HOOK_NAME. The wording changed with Subversion 1.5."""
-  if svntest.main.server_minor_version < 5:
+  if svntest.main.options.server_minor_version < 5:
     return "'%s' hook failed with error output:\n" % hook_name
   else:
     if hook_name in ["start-commit", "pre-commit"]:

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/factory.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/factory.py?rev=984234&r1=984233&r2=984234&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/factory.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/factory.py Tue Aug 10 22:07:24 2010
@@ -434,6 +434,9 @@ class TestFactory:
       if second in ['update','up']:
         return self.cmd_svn_update(args[2:])
 
+      if second in ['switch','sw']:
+        return self.cmd_svn_switch(args[2:])
+
       if second in ['copy', 'cp',
                     'move', 'mv', 'rename', 'ren']:
         return self.cmd_svn_copy_move(args[1:])
@@ -596,7 +599,7 @@ class TestFactory:
 
 
   def cmd_svn_update(self, update_args):
-    "Runs svnn update, looks what happened and writes the script for it."
+    "Runs svn update, looks what happened and writes the script for it."
 
     pyargs, runargs, do_chdir, targets = self.args2svntest(
                                   update_args, True, self.keep_args_of, 0)
@@ -637,6 +640,72 @@ class TestFactory:
     return py
 
 
+  def cmd_svn_switch(self, switch_args):
+    "Runs svn switch, looks what happened and writes the script for it."
+
+    pyargs, runargs, do_chdir, targets = self.args2svntest(
+                                  switch_args, True, self.keep_args_of, 0)
+
+    # Sort out the targets. We need one URL and one wc node, in that order.
+    if len(targets) < 2:
+      raise Failure("Sorry, I'm currently enforcing two targets for svn " +
+                    "switch. If you want to supply less, remove this " +
+                    "check and implement whatever seems appropriate.")
+
+    wc_arg = targets[1]
+    del pyargs[wc_arg.argnr]
+    del runargs[wc_arg.argnr]
+    url_arg = targets[0]
+    del pyargs[url_arg.argnr]
+    del runargs[url_arg.argnr]
+
+    wc = wc_arg.wc
+
+    pychdir = self.chdir(do_chdir, wc)
+
+    #if '--force' in runargs:
+    #  self.really_safe_rmtree(wc_arg.runarg)
+
+    code, output, err = main.run_svn('Maybe', 'sw',
+                                     url_arg.runarg, wc_arg.runarg,
+                                     *runargs)
+
+    py = ""
+
+    if code == 0 and len(err) < 1:
+      # write a test that expects success
+
+      actual_out = tree.build_tree_from_checkout(output)
+      py = ("expected_output = " +
+            self.tree2py(actual_out, wc) + "\n\n")
+
+      pydisk = self.get_current_disk(wc)
+      py += pydisk
+
+      pystatus = self.get_current_status(wc)
+      py += pystatus
+
+      py += pychdir
+      py += ("actions.run_and_verify_switch(" + wc.py + ", " +
+             wc_arg.pyarg + ", " + url_arg.pyarg + ", " +
+             "expected_output, expected_disk, expected_status, " +
+             "None, None, None, None, None, False")
+    else:
+      # write a test that expects error
+      py = "expected_error = " + self.strlist2py(err) + "\n\n"
+      py += pychdir
+      py += ("actions.run_and_verify_switch(" + wc.py + ", " +
+             wc_arg.pyarg + ", " + url_arg.pyarg + ", " +
+             "None, None, None, expected_error, None, None, None, None, False")
+
+    if len(pyargs) > 0:
+      py += ', ' + ', '.join(pyargs)
+    py += ")"
+    py += self.chdir_back(do_chdir)
+
+    return py
+
+
   def cmd_svn_checkout(self, checkout_args):
     "Runs svn checkout, looks what happened and writes the script for it."
 
@@ -661,8 +730,8 @@ class TestFactory:
 
     pychdir = self.chdir(do_chdir, wc)
 
-    if '--force' in runargs:
-      self.really_safe_rmtree(wc_arg.runarg)
+    #if '--force' in runargs:
+    #  self.really_safe_rmtree(wc_arg.runarg)
 
     code, output, err = main.run_svn('Maybe', 'co',
                                      url_arg.runarg, wc_arg.runarg,
@@ -761,7 +830,7 @@ class TestFactory:
           if i != len(echo_args)-1:
             raise Failure("don't understand: echo " + " ".join(echo_args))
 
-        contents = " ".join(echo_args[:i])
+        contents = " ".join(echo_args[:i]) + '\n'
 
     if target_arg is None:
       raise Failure("echo needs a '>' pipe to a file name: echo " +
@@ -801,8 +870,12 @@ class TestFactory:
     for arg in rm_args:
       if not arg.startswith('-'):
         target = self.path2svntest(arg)
-        self.really_safe_rmtree(target.runarg)
-        out += "main.safe_rmtree(" + target.pyarg + ")\n"
+        if os.path.isfile(target.runarg):
+          os.remove(target.runarg)
+          out += "os.remove(" + target.pyarg + ")\n"
+        else:
+          self.really_safe_rmtree(target.runarg)
+          out += "main.safe_rmtree(" + target.pyarg + ")\n"
     return out
 
 
@@ -1030,16 +1103,18 @@ class TestFactory:
   def get_sorted_vars_by_pathlen(self):
     """Compose a listing of variable names to be expanded in script output.
     This is intended to be stored in self.sorted_vars_by_pathlen."""
-    list = []
+    lst = []
 
     for dict in [self.vars, self.other_wc_dirs]:
       for name in dict:
         runpath = dict[name][1]
+        if not runpath:
+          continue
         strlen = len(runpath)
         item = [strlen, name, runpath]
-        bisect.insort(list, item)
+        bisect.insort(lst, item)
 
-    return list
+    return lst
 
 
   def get_sorted_var_names(self):
@@ -1275,6 +1350,10 @@ class TestFactory:
         # Check if the actual tree had this anyway all the way through.
         name = mod[0]
         val = mod[1]
+
+        if name == 'contents' and val is None:
+          continue;
+
         def check_node(node):
           if (
               (name == 'contents' and node.contents == val)
@@ -1397,7 +1476,7 @@ class TestFactory:
     return py
 
 
-  def path2svntest(self, path, argnr=None):
+  def path2svntest(self, path, argnr=None, do_remove_on_new_wc_path=True):
     """Given an input argument, do one hell of a path expansion on it.
     ARGNR is simply inserted into the resulting Target.
     Returns a self.Target instance.
@@ -1494,12 +1573,18 @@ class TestFactory:
     if varname in self.other_wc_dirs:
       return self.other_wc_dirs[varname][1]
 
-    # else, we must still create one.
-    path = self.sbox.add_wc_path(suffix, do_remove)
-    py = "sbox.add_wc_path(" + str2py(suffix)
-    if not do_remove:
-      py += ", remove=False"
-    py += ')'
+    # see if there is a wc already in the sbox
+    path = self.sbox.wc_dir + '.' + suffix
+    if path in self.sbox.test_paths:
+      py = "sbox.wc_dir + '." + suffix + "'"
+    else:
+      # else, we must still create one.
+      path = self.sbox.add_wc_path(suffix, do_remove)
+      py = "sbox.add_wc_path(" + str2py(suffix)
+      if not do_remove:
+        py += ", remove=False"
+      py += ')'
+
     value = [py, path]
     self.other_wc_dirs[varname] = [py, path]
     self.sorted_vars_by_pathlen = self.get_sorted_vars_by_pathlen()

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=984234&r1=984233&r2=984234&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 22:07:24 2010
@@ -33,6 +33,8 @@ import copy    # for deepcopy()
 import time    # for time()
 import traceback # for print_exc()
 import threading
+import optparse # for argument parsing
+
 try:
   # Python >=3.0
   import queue
@@ -44,12 +46,6 @@ except ImportError:
   from urllib import quote as urllib_parse_quote
   from urllib import unquote as urllib_parse_unquote
 
-import getopt
-try:
-  my_getopt = getopt.gnu_getopt
-except AttributeError:
-  my_getopt = getopt.getopt
-
 import svntest
 from svntest import Failure
 from svntest import Skip
@@ -150,11 +146,6 @@ def url2pathname(path):
   return os.path.normpath(urllib_parse_unquote(path))
 
 ######################################################################
-# Global variables set during option parsing.  These should not be used
-# until the variable command_line_parsed has been set to True, as is
-# done in run_tests below.
-command_line_parsed = False
-
 # The locations of the svn, svnadmin and svnlook binaries, relative to
 # the only scripts that import this file right now (they live in ../).
 # Use --bin to override these defaults.
@@ -167,56 +158,12 @@ svndumpfilter_binary = os.path.abspath('
                                        _exe)
 entriesdump_binary = os.path.abspath('entries-dump' + _exe)
 
-# Global variable indicating if we want verbose output, that is,
-# details of what commands each test does as it does them.  This is
-# incompatible with quiet_mode.
-verbose_mode = False
-
-# Global variable indicating if we want quiet output, that is, don't
-# show PASS, XFAIL, or SKIP notices, but do show FAIL and XPASS.  This
-# is incompatible with verbose_mode.
-quiet_mode = False
-
-# Global variable indicating if we want test data cleaned up after success
-cleanup_mode = False
-
-# Global variable indicating if svnserve should use Cyrus SASL
-enable_sasl = False
-
-# Global variable indicating that SVNKit binaries should be used
-use_jsvn = False
-
-# Global variable indicating which DAV library to use if both are available
-# ('neon', 'serf').
-preferred_http_library = 'serf'
-
-# Global variable: Number of shards to use in FSFS
-# 'None' means "use FSFS's default"
-fsfs_sharding = None
-
-# Global variable: automatically pack FSFS repositories after every commit
-fsfs_packing = None
-
-# Configuration file (copied into FSFS fsfs.conf).
-config_file = None
-
-# Global variable indicating what the minor version of the server
-# tested against is (4 for 1.4.x, for example).
-server_minor_version = 7
-
-# Global variable indicating if this is a child process and no cleanup
-# of global directories is needed.
-is_child_process = False
-
-# Global URL to testing area.  Default to ra_local, current working dir.
-test_area_url = file_scheme_prefix + pathname2url(os.path.abspath(os.getcwd()))
-
 # Location to the pristine repository, will be calculated from test_area_url
 # when we know what the user specified for --url.
 pristine_url = None
 
-# Global variable indicating the FS type for repository creations.
-fs_type = None
+# Global variable to track all of our options
+options = None
 
 # End of command-line-set global variables.
 ######################################################################
@@ -467,14 +414,14 @@ def wait_on_pipe(waiter, binary_mode, st
     if stderr_lines is not None:
       sys.stderr.write("".join(stderr_lines))
       sys.stderr.flush()
-    if verbose_mode:
+    if options.verbose:
       # show the whole path to make it easier to start a debugger
       sys.stderr.write("CMD: %s terminated by signal %d\n"
                        % (command_string, exit_signal))
       sys.stderr.flush()
     raise SVNProcessTerminatedBySignal
   else:
-    if exit_code and verbose_mode:
+    if exit_code and options.verbose:
       sys.stderr.write("CMD: %s exited with %d\n"
                        % (command_string, exit_code))
     return stdout_lines, stderr_lines, exit_code
@@ -494,7 +441,7 @@ def spawn_process(command, bufsize=0, bi
     raise TypeError("stdin_lines should have list type")
 
   # Log the command line
-  if verbose_mode and not command.endswith('.py'):
+  if options.verbose and not command.endswith('.py'):
     sys.stdout.write('CMD: %s %s\n' % (os.path.basename(command),
                                       ' '.join([_quote_arg(x) for x in varargs])))
     sys.stdout.flush()
@@ -527,7 +474,7 @@ def run_command_stdin(command, error_exp
   line terminators).
   If ERROR_EXPECTED is None, any stderr also will be printed."""
 
-  if verbose_mode:
+  if options.verbose:
     start = time.time()
 
   exit_code, stdout_lines, stderr_lines = spawn_process(command,
@@ -536,7 +483,7 @@ def run_command_stdin(command, error_exp
                                                         stdin_lines,
                                                         *varargs)
 
-  if verbose_mode:
+  if options.verbose:
     stop = time.time()
     print('<TIME = %.6f>' % (stop - start))
     for x in stdout_lines:
@@ -545,7 +492,7 @@ def run_command_stdin(command, error_exp
       sys.stdout.write(x)
 
   if (not error_expected) and (stderr_lines):
-    if not verbose_mode:
+    if not options.verbose:
       for x in stderr_lines:
         sys.stdout.write(x)
     raise Failure
@@ -579,8 +526,8 @@ interactive-conflicts = false
   # define default server file contents if none provided
   if server_contents is None:
     http_library_str = ""
-    if preferred_http_library:
-      http_library_str = "http-library=%s" % (preferred_http_library)
+    if options.http_library:
+      http_library_str = "http-library=%s" % (options.http_library)
     server_contents = """
 #
 [global]
@@ -645,7 +592,7 @@ def run_entriesdump(path):
   # to stdout in verbose mode.
   exit_code, stdout_lines, stderr_lines = spawn_process(entriesdump_binary,
                                                         0, 0, None, path)
-  if verbose_mode:
+  if options.verbose:
     ### finish the CMD output
     print
   if exit_code or stderr_lines:
@@ -721,14 +668,14 @@ def create_repos(path):
     os.makedirs(path) # this creates all the intermediate dirs, if neccessary
 
   opts = ("--bdb-txn-nosync",)
-  if server_minor_version < 5:
+  if options.server_minor_version < 5:
     opts += ("--pre-1.5-compatible",)
-  elif server_minor_version < 6:
+  elif options.server_minor_version < 6:
     opts += ("--pre-1.6-compatible",)
-  elif server_minor_version < 7:
+  elif options.server_minor_version < 7:
     opts += ("--pre-1.7-compatible",)
-  if fs_type is not None:
-    opts += ("--fs-type=" + fs_type,)
+  if options.fs_type is not None:
+    opts += ("--fs-type=" + options.fs_type,)
   exit_code, stdout, stderr = run_command(svnadmin_binary, 1, 0, "create",
                                           path, *opts)
 
@@ -744,7 +691,7 @@ def create_repos(path):
   # Allow unauthenticated users to write to the repos, for ra_svn testing.
   file_write(get_svnserve_conf_file_path(path),
              "[general]\nauth-access = write\n");
-  if enable_sasl:
+  if options.enable_sasl:
     file_append(get_svnserve_conf_file_path(path),
                 "realm = svntest\n[sasl]\nuse-sasl = true\n")
   else:
@@ -752,17 +699,17 @@ def create_repos(path):
     file_append(os.path.join(path, "conf", "passwd"),
                 "[users]\njrandom = rayjandom\njconstant = rayjandom\n");
 
-  if fs_type is None or fs_type == 'fsfs':
+  if options.fs_type is None or options.fs_type == 'fsfs':
     # fsfs.conf file
-    if config_file is not None:
-      shutil.copy(config_file, get_fsfs_conf_file_path(path))
+    if options.config_file is not None:
+      shutil.copy(options.config_file, get_fsfs_conf_file_path(path))
 
     # format file
-    if fsfs_sharding is not None:
+    if options.fsfs_sharding is not None:
       def transform_line(line):
         if line.startswith('layout '):
-          if fsfs_sharding > 0:
-            line = 'layout sharded %d' % fsfs_sharding
+          if options.fsfs_sharding > 0:
+            line = 'layout sharded %d' % options.fsfs_sharding
           else:
             line = 'layout linear'
         return line
@@ -785,7 +732,7 @@ def create_repos(path):
     # post-commit
     # Note that some tests (currently only commit_tests) create their own
     # post-commit hooks, which would override this one. :-(
-    if fsfs_packing:
+    if options.fsfs_packing:
       # some tests chdir.
       abs_path = os.path.abspath(path)
       create_python_hook_script(get_post_commit_hook_path(abs_path),
@@ -814,7 +761,7 @@ def copy_repos(src_path, dst_path, head_
 
   if ignore_uuid:
     load_args = load_args + ['--ignore-uuid']
-  if verbose_mode:
+  if options.verbose:
     sys.stdout.write('CMD: %s %s | %s %s\n' %
                      (os.path.basename(svnadmin_binary), ' '.join(dump_args),
                       os.path.basename(svnadmin_binary), ' '.join(load_args)))
@@ -828,7 +775,7 @@ def copy_repos(src_path, dst_path, head_
     stdin=dump_out) # Attached to dump_kid
 
   stop = time.time()
-  if verbose_mode:
+  if options.verbose:
     print('<TIME = %.6f>' % (stop - start))
 
   load_stdout, load_stderr, load_exit_code = wait_on_pipe(load_kid, True)
@@ -906,7 +853,7 @@ def write_restrictive_svnserve_conf(repo
   fp = open(get_svnserve_conf_file_path(repo_dir), 'w')
   fp.write("[general]\nanon-access = %s\nauth-access = write\n"
            "authz-db = authz\n" % anon_access)
-  if enable_sasl == 1:
+  if options.enable_sasl:
     fp.write("realm = svntest\n[sasl]\nuse-sasl = true\n");
   else:
     fp.write("password-db = passwd\n")
@@ -1032,43 +979,32 @@ def make_log_msg():
 # Functions which check the test configuration
 # (useful for conditional XFails)
 
-def _check_command_line_parsed():
-  """Raise an exception if the command line has not yet been parsed."""
-  if not command_line_parsed:
-    raise Failure("Condition cannot be tested until command line is parsed")
-
 def is_ra_type_dav():
-  _check_command_line_parsed()
-  return test_area_url.startswith('http')
+  return options.test_area_url.startswith('http')
 
 def is_ra_type_dav_neon():
   """Return True iff running tests over RA-Neon.
      CAUTION: Result is only valid if svn was built to support both."""
-  _check_command_line_parsed()
-  return test_area_url.startswith('http') and \
-    (preferred_http_library == "neon")
+  return options.test_area_url.startswith('http') and \
+    (options.http_library == "neon")
 
 def is_ra_type_dav_serf():
   """Return True iff running tests over RA-Serf.
      CAUTION: Result is only valid if svn was built to support both."""
-  _check_command_line_parsed()
-  return test_area_url.startswith('http') and \
-    (preferred_http_library == "serf")
+  return options.test_area_url.startswith('http') and \
+    (options.http_library == "serf")
 
 def is_ra_type_svn():
   """Return True iff running tests over RA-svn."""
-  _check_command_line_parsed()
-  return test_area_url.startswith('svn')
+  return options.test_area_url.startswith('svn')
 
 def is_ra_type_file():
   """Return True iff running tests over RA-local."""
-  _check_command_line_parsed()
-  return test_area_url.startswith('file')
+  return options.test_area_url.startswith('file')
 
 def is_fs_type_fsfs():
-  _check_command_line_parsed()
   # This assumes that fsfs is the default fs implementation.
-  return fs_type == 'fsfs' or fs_type is None
+  return options.fs_type == 'fsfs' or options.fs_type is None
 
 def is_os_windows():
   return os.name == 'nt'
@@ -1083,32 +1019,25 @@ def is_fs_case_insensitive():
   return (is_os_darwin() or is_os_windows())
 
 def server_has_mergeinfo():
-  _check_command_line_parsed()
-  return server_minor_version >= 5
+  return options.server_minor_version >= 5
 
 def server_has_revprop_commit():
-  _check_command_line_parsed()
-  return server_minor_version >= 5
+  return options.server_minor_version >= 5
 
 def server_sends_copyfrom_on_update():
-  _check_command_line_parsed()
-  return server_minor_version >= 5
+  return options.server_minor_version >= 5
 
 def server_authz_has_aliases():
-  _check_command_line_parsed()
-  return server_minor_version >= 5
+  return options.server_minor_version >= 5
 
 def server_gets_client_capabilities():
-  _check_command_line_parsed()
-  return server_minor_version >= 5
+  return options.server_minor_version >= 5
 
 def server_has_partial_replay():
-  _check_command_line_parsed()
-  return server_minor_version >= 5
+  return options.server_minor_version >= 5
 
 def server_enforces_date_syntax():
-  _check_command_line_parsed()
-  return server_minor_version >= 5
+  return options.server_minor_version >= 5
 
 ######################################################################
 
@@ -1138,20 +1067,20 @@ class TestSpawningThread(threading.Threa
     args.append(str(index))
     args.append('-c')
     # add some startup arguments from this process
-    if fs_type:
-      args.append('--fs-type=' + fs_type)
-    if test_area_url:
-      args.append('--url=' + test_area_url)
-    if verbose_mode:
+    if options.fs_type:
+      args.append('--fs-type=' + options.fs_type)
+    if options.test_area_url:
+      args.append('--url=' + options.test_area_url)
+    if options.verbose:
       args.append('-v')
-    if cleanup_mode:
+    if options.cleanup:
       args.append('--cleanup')
-    if enable_sasl:
+    if options.enable_sasl:
       args.append('--enable-sasl')
-    if preferred_http_library:
-      args.append('--http-library=' + preferred_http_library)
-    if server_minor_version:
-      args.append('--server-minor-version=' + str(server_minor_version))
+    if options.http_library:
+      args.append('--http-library=' + options.http_library)
+    if options.server_minor_version:
+      args.append('--server-minor-version=' + str(options.server_minor_version))
 
     result, stdout_lines, stderr_lines = spawn_process(command, 0, 0, None,
                                                        *args)
@@ -1173,7 +1102,7 @@ class TestRunner:
     self.index = index
 
   def list(self):
-    if verbose_mode and self.pred.inprogress:
+    if options.verbose and self.pred.inprogress:
       print(" %2d     %-5s  %s [[%s]]" % (self.index,
                                         self.pred.list_mode(),
                                         self.pred.description,
@@ -1219,7 +1148,7 @@ class TestRunner:
     os.environ['SVN_EDITOR'] = ''
     os.environ['SVNTEST_EDITOR_FUNC'] = ''
 
-    if use_jsvn:
+    if options.use_jsvn:
       # Set this SVNKit specific variable to the current test (test name plus
       # its index) being run so that SVNKit daemon could use this test name
       # for its separate log file
@@ -1267,9 +1196,9 @@ class TestRunner:
 
     os.chdir(saved_dir)
     exit_code, result_text, result_benignity = self.pred.results(result)
-    if not (quiet_mode and result_benignity):
+    if not (options.quiet and result_benignity):
       self._print_name(result_text)
-    if sandbox is not None and exit_code != 1 and cleanup_mode:
+    if sandbox is not None and exit_code != 1 and options.cleanup:
       sandbox.cleanup_test_paths()
     return exit_code
 
@@ -1349,46 +1278,97 @@ def _internal_run_tests(test_list, testn
   return exit_code
 
 
-def usage():
-  prog_name = os.path.basename(sys.argv[0])
-  print("%s [--url] [--fs-type] [--verbose|--quiet] [--parallel] \\" %
-        prog_name)
-  print("%s [--enable-sasl] [--cleanup] [--bin] [<test> ...]"
-      % (" " * len(prog_name)))
-  print("%s " % (" " * len(prog_name)))
-  print("%s [--list] [<test> ...]\n" % prog_name)
-  print("Arguments:")
-  print(" <test>  The number of the test to run, or a range of test\n"
-        "         numbers, like 10:12 or 10-12. Multiple numbers and\n"
-        "         ranges are ok. If you supply none, all tests are run.\n"
-        "         You can also pass the name of a test function to run.\n")
-  print("Options:")
-  print(" --list          Print test doc strings instead of running them")
-  print(" --fs-type       Subversion file system type (fsfs or bdb)")
-  print(" --http-library  Make svn use this DAV library (neon or serf) if\n"
-        "                 it supports both, else assume it's using this one;\n"
-        "                 the default is neon")
-  print(" --url           Base url to the repos (e.g. svn://localhost)")
-  print(" --verbose       Print binary command-lines (not with --quiet)")
-  print(" --quiet         Print only unexpected results (not with --verbose)")
-  print(" --cleanup       Whether to clean up")
-  print(" --enable-sasl   Whether to enable SASL authentication")
-  print(" --parallel      Run the tests in parallel")
-  print(" --bin           Use the svn binaries installed in this path")
-  print(" --use-jsvn      Use the jsvn (SVNKit based) binaries. Can be\n"
-        "                 combined with --bin to point to a specific path")
-  print(" --development   Test development mode: provides more detailed test\n"
-        "                 output and ignores all exceptions in the \n"
-        "                 run_and_verify* functions. This option is only \n"
-        "                 useful during test development!")
-  print(" --server-minor-version  Set the minor version for the server ('4',\n"
-        "                 '5', or '6').")
-  print(" --fsfs-sharding Default shard size (for fsfs)\n"
-        " --fsfs-packing  Run 'svnadmin pack' automatically")
-  print(" --config-file   Configuration file for tests.")
-  print(" --keep-local-tmp  Don't remove svn-test-work/local_tmp after test\n"
-        "                 run is complete.  Useful for debugging failures.")
-  print(" --help          This information")
+def create_default_options():
+  """Set the global options to the defaults, as provided by the argument
+     parser."""
+  _parse_options([])
+
+
+def _create_parser():
+  """Return a parser for our test suite."""
+  # set up the parser
+  usage = 'usage: %prog [options] [<test> ...]'
+  parser = optparse.OptionParser(usage=usage)
+  parser.add_option('-l', '--list', action='store_true', dest='list_tests',
+                    help='Print test doc strings instead of running them')
+  parser.add_option('-v', '--verbose', action='store_true',
+                    help='Print binary command-lines (not with --quiet)')
+  parser.add_option('-q', '--quiet', action='store_true',
+                    help='Print only unexpected results (not with --verbose)')
+  parser.add_option('-p', '--parallel', action='store_const', const=5,
+                    dest='parallel',
+                    help='Run the tests in parallel')
+  parser.add_option('-c', action='store_true', dest='is_child_process',
+                    help='Flag if we are running this python test as a ' +
+                         'child process')
+  parser.add_option('--url', action='store',
+                    help='Base url to the repos (e.g. svn://localhost)')
+  parser.add_option('--fs-type', action='store',
+                    help='Subversion file system type (fsfs or bdb)')
+  parser.add_option('--cleanup', action='store_true',
+                    help='Whether to clean up')
+  parser.add_option('--enable-sasl', action='store_true',
+                    help='Whether to enable SASL authentication')
+  parser.add_option('--bin', action='store', dest='svn_bin',
+                    help='Use the svn binaries installed in this path')
+  parser.add_option('--use-jsvn', action='store_true',
+                    help="Use the jsvn (SVNKit based) binaries. Can be " +
+                         "combined with --bin to point to a specific path")
+  parser.add_option('--http-library', action='store',
+                    help="Make svn use this DAV library (neon or serf) if " +
+                         "it supports both, else assume it's using this " +
+                         "one; the default is neon")
+  parser.add_option('--server-minor-version', type='int', action='store',
+                    help="Set the minor version for the server ('4', " +
+                         "'5', or '6').")
+  parser.add_option('--fsfs-packing', action='store_true',
+                    help="Run 'svnadmin pack' automatically")
+  parser.add_option('--fsfs-sharding', action='store', type='int',
+                    help='Default shard size (for fsfs)')
+  parser.add_option('--config-file', action='store',
+                    help="Configuration file for tests.")
+  parser.add_option('--keep-local-tmp', action='store_true',
+                    help="Don't remove svn-test-work/local_tmp after test " +
+                         "run is complete.  Useful for debugging failures.")
+  parser.add_option('--development', action='store_true',
+                    help='Test development mode: provides more detailed ' +
+                         'test output and ignores all exceptions in the ' +
+                         'run_and_verify* functions. This option is only ' +
+                         'useful during test development!')
+
+  # most of the defaults are None, but some are other values, set them here
+  parser.set_defaults(
+        server_minor_version=7,
+        url=file_scheme_prefix + pathname2url(os.path.abspath(os.getcwd())),
+        http_library='serf')
+
+  return parser
+
+
+def _parse_options(arglist=sys.argv[1:]):
+  """Parse the arguments in arg_list, and set the global options object with
+     the results"""
+
+  global options
+
+  parser = _create_parser()
+  (options, args) = parser.parse_args(arglist)
+
+  # some sanity checking
+  if options.verbose and options.quiet:
+    parser.error("'verbose' and 'quiet' are incompatible")
+  if options.fsfs_packing and not options.fsfs_sharding:
+    parser.error("--fsfs-packing requires --fsfs-sharding")
+  if options.server_minor_version < 4 or options.server_minor_version > 7:
+    parser.error("test harness only supports server minor versions 4-7")
+
+  if options.url:
+    if options.url[-1:] == '/': # Normalize url to have no trailing slash
+      options.test_area_url = options.url[:-1]
+    else:
+      options.test_area_url = options.url
+
+  return (parser, args)
 
 
 # Main func.  This is the "entry point" that all the test scripts call
@@ -1402,211 +1382,106 @@ def run_tests(test_list, serial_only = F
         appropriate exit code.
   """
 
-  global test_area_url
   global pristine_url
-  global fs_type
-  global verbose_mode
-  global quiet_mode
-  global cleanup_mode
-  global enable_sasl
-  global is_child_process
   global svn_binary
   global svnadmin_binary
   global svnlook_binary
   global svnsync_binary
   global svndumpfilter_binary
   global svnversion_binary
-  global command_line_parsed
-  global preferred_http_library
-  global fsfs_sharding
-  global fsfs_packing
-  global config_file
-  global server_minor_version
-  global use_jsvn
+  global options
 
   testnums = []
-  # Should the tests be listed (as opposed to executed)?
-  list_tests = False
 
-  parallel = 0
-  svn_bin = None
-  use_jsvn = False
-  keep_local_tmp = False
-  config_file = None
-
-  try:
-    opts, args = my_getopt(sys.argv[1:], 'vqhpc',
-                           ['url=', 'fs-type=', 'verbose', 'quiet', 'cleanup',
-                            'list', 'enable-sasl', 'help', 'parallel',
-                            'bin=', 'http-library=', 'server-minor-version=',
-                            'fsfs-packing', 'fsfs-sharding=',
-                            'use-jsvn', 'development', 'keep-local-tmp',
-                            'config-file='])
-  except getopt.GetoptError, e:
-    print("ERROR: %s\n" % e)
-    usage()
-    sys.exit(1)
+  if not options:
+    (parser, args) = _parse_options()
+  else:
+    args = []
+    parser = _create_parser()
 
+  # parse the positional arguments (test nums, names)
   for arg in args:
-    if arg == "list":
-      # This is an old deprecated variant of the "--list" option:
-      list_tests = True
-    elif arg.startswith('BASE_URL='):
-      test_area_url = arg[9:]
-    else:
+    appended = False
+    try:
+      testnums.append(int(arg))
+      appended = True
+    except ValueError:
+      # Do nothing for now.
       appended = False
-      try:
-        testnums.append(int(arg))
-        appended = True
-      except ValueError:
-        # Do nothing for now.
-        appended = False
 
-      if not appended:
-        try:
-          # Check if the argument is a range
-          numberstrings = arg.split(':');
+    if not appended:
+      try:
+        # Check if the argument is a range
+        numberstrings = arg.split(':');
+        if len(numberstrings) != 2:
+          numberstrings = arg.split('-');
           if len(numberstrings) != 2:
-            numberstrings = arg.split('-');
-            if len(numberstrings) != 2:
-              raise ValueError
-          left = int(numberstrings[0])
-          right = int(numberstrings[1])
-          if left > right:
             raise ValueError
+        left = int(numberstrings[0])
+        right = int(numberstrings[1])
+        if left > right:
+          raise ValueError
 
-          for nr in range(left,right+1):
-            testnums.append(nr)
-          else:
-            appended = True
-        except ValueError:
-          appended = False
-
-      if not appended:
-        try:
-          # Check if the argument is a function name, and translate
-          # it to a number if possible
-          for testnum in list(range(1, len(test_list))):
-            test_case = TestRunner(test_list[testnum], testnum)
-            if test_case.get_function_name() == str(arg):
-              testnums.append(testnum)
-              appended = True
-              break
-        except ValueError:
-          appended = False
-
-      if not appended:
-        print("ERROR: invalid test number, range of numbers, " +
-              "or function '%s'\n" % arg)
-        usage()
-        sys.exit(1)
-
-  for opt, val in opts:
-    if opt == "--url":
-      test_area_url = val
-
-    elif opt == "--fs-type":
-      fs_type = val
-
-    elif opt == "-v" or opt == "--verbose":
-      verbose_mode = True
-
-    elif opt == "-q" or opt == "--quiet":
-      quiet_mode = True
-
-    elif opt == "--cleanup":
-      cleanup_mode = True
-
-    elif opt == "--list":
-      list_tests = True
-
-    elif opt == "--enable-sasl":
-      enable_sasl = True
-
-    elif opt == "-h" or opt == "--help":
-      usage()
-      sys.exit(0)
-
-    elif opt == '-p' or opt == "--parallel":
-      parallel = 5   # use 5 parallel threads.
-
-    elif opt == '-c':
-      is_child_process = True
-
-    elif opt == '--bin':
-      svn_bin = val
-
-    elif opt == '--http-library':
-      preferred_http_library = val
-
-    elif opt == '--fsfs-sharding':
-      fsfs_sharding = int(val)
-    elif opt == '--fsfs-packing':
-      fsfs_packing = 1
-
-    elif opt == '--server-minor-version':
-      server_minor_version = int(val)
-      if server_minor_version < 4 or server_minor_version > 7:
-        print("ERROR: test harness only supports server minor versions 4-6")
-        sys.exit(1)
-
-    elif opt == '--use-jsvn':
-      use_jsvn = True
-
-    elif opt == '--keep-local-tmp':
-      keep_local_tmp = True
-
-    elif opt == '--development':
-      setup_development_mode()
-
-    elif opt == '--config-file':
-      config_file = val
-
-  if fsfs_packing is not None and fsfs_sharding is None:
-    raise Exception('--fsfs-packing requires --fsfs-sharding')
+        for nr in range(left,right+1):
+          testnums.append(nr)
+        else:
+          appended = True
+      except ValueError:
+        appended = False
 
-  if test_area_url[-1:] == '/': # Normalize url to have no trailing slash
-    test_area_url = test_area_url[:-1]
+    if not appended:
+      try:
+        # Check if the argument is a function name, and translate
+        # it to a number if possible
+        for testnum in list(range(1, len(test_list))):
+          test_case = TestRunner(test_list[testnum], testnum)
+          if test_case.get_function_name() == str(arg):
+            testnums.append(testnum)
+            appended = True
+            break
+      except ValueError:
+        appended = False
 
-  if verbose_mode and quiet_mode:
-    sys.stderr.write("ERROR: 'verbose' and 'quiet' are incompatible\n")
-    sys.exit(1)
+    if not appended:
+      parser.error("invalid test number, range of numbers, " +
+                   "or function '%s'\n" % arg)
 
   # Calculate pristine_url from test_area_url.
-  pristine_url = test_area_url + '/' + pathname2url(pristine_dir)
+  pristine_url = options.test_area_url + '/' + pathname2url(pristine_dir)
 
-  if use_jsvn:
-    if svn_bin is None:
-      svn_bin = ''
-    svn_binary = os.path.join(svn_bin, 'jsvn' + _bat)
-    svnadmin_binary = os.path.join(svn_bin, 'jsvnadmin' + _bat)
-    svnlook_binary = os.path.join(svn_bin, 'jsvnlook' + _bat)
-    svnsync_binary = os.path.join(svn_bin, 'jsvnsync' + _bat)
-    svndumpfilter_binary = os.path.join(svn_bin, 'jsvndumpfilter' + _bat)
-    svnversion_binary = os.path.join(svn_bin, 'jsvnversion' + _bat)
+  if options.use_jsvn:
+    if options.svn_bin is None:
+      options.svn_bin = ''
+    svn_binary = os.path.join(options.svn_bin, 'jsvn' + _bat)
+    svnadmin_binary = os.path.join(options.svn_bin, 'jsvnadmin' + _bat)
+    svnlook_binary = os.path.join(options.svn_bin, 'jsvnlook' + _bat)
+    svnsync_binary = os.path.join(options.svn_bin, 'jsvnsync' + _bat)
+    svndumpfilter_binary = os.path.join(options.svn_bin,
+                                        'jsvndumpfilter' + _bat)
+    svnversion_binary = os.path.join(options.svn_bin,
+                                     'jsvnversion' + _bat)
   else:
-    if svn_bin:
-      svn_binary = os.path.join(svn_bin, 'svn' + _exe)
-      svnadmin_binary = os.path.join(svn_bin, 'svnadmin' + _exe)
-      svnlook_binary = os.path.join(svn_bin, 'svnlook' + _exe)
-      svnsync_binary = os.path.join(svn_bin, 'svnsync' + _exe)
-      svndumpfilter_binary = os.path.join(svn_bin, 'svndumpfilter' + _exe)
-      svnversion_binary = os.path.join(svn_bin, 'svnversion' + _exe)
-
-  command_line_parsed = True
+    if options.svn_bin:
+      svn_binary = os.path.join(options.svn_bin, 'svn' + _exe)
+      svnadmin_binary = os.path.join(options.svn_bin, 'svnadmin' + _exe)
+      svnlook_binary = os.path.join(options.svn_bin, 'svnlook' + _exe)
+      svnsync_binary = os.path.join(options.svn_bin, 'svnsync' + _exe)
+      svndumpfilter_binary = os.path.join(options.svn_bin,
+                                          'svndumpfilter' + _exe)
+      svnversion_binary = os.path.join(options.svn_bin, 'svnversion' + _exe)
 
   ######################################################################
 
   # Cleanup: if a previous run crashed or interrupted the python
   # interpreter, then `temp_dir' was never removed.  This can cause wonkiness.
-  if not is_child_process:
+  if not options.is_child_process:
     safe_rmtree(temp_dir, 1)
 
   if not testnums:
     # If no test numbers were listed explicitly, include all of them:
     testnums = list(range(1, len(test_list)))
 
-  if list_tests:
+  if options.list_tests:
     print("Test #  Mode   Test Description")
     print("------  -----  ----------------")
     for testnum in testnums:
@@ -1618,9 +1493,9 @@ def run_tests(test_list, serial_only = F
   # don't run tests in parallel when the tests don't support it or there
   # are only a few tests to run.
   if serial_only or len(testnums) < 2:
-    parallel = 0
+    options.parallel = 0
 
-  if not is_child_process:
+  if not options.is_child_process:
     # Build out the default configuration directory
     create_config_dir(default_config_dir)
 
@@ -1628,11 +1503,11 @@ def run_tests(test_list, serial_only = F
     svntest.actions.setup_pristine_repository()
 
   # Run the tests.
-  exit_code = _internal_run_tests(test_list, testnums, parallel)
+  exit_code = _internal_run_tests(test_list, testnums, options.parallel)
 
   # Remove all scratchwork: the 'pristine' repository, greek tree, etc.
   # This ensures that an 'import' will happen the next time we run.
-  if not is_child_process and not keep_local_tmp:
+  if not options.is_child_process and not options.keep_local_tmp:
     safe_rmtree(temp_dir, 1)
 
   # Cleanup after ourselves.

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/sandbox.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/sandbox.py?rev=984234&r1=984233&r2=984234&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/sandbox.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/sandbox.py Tue Aug 10 22:07:24 2010
@@ -48,7 +48,7 @@ class Sandbox:
     self.wc_dir = os.path.join(svntest.main.general_wc_dir, self.name)
     if not read_only:
       self.repo_dir = os.path.join(svntest.main.general_repo_dir, self.name)
-      self.repo_url = (svntest.main.test_area_url + '/'
+      self.repo_url = (svntest.main.options.test_area_url + '/'
                        + svntest.main.pathname2url(self.repo_dir))
     else:
       self.repo_dir = svntest.main.pristine_dir
@@ -117,7 +117,8 @@ class Sandbox:
        Return (REPOS-PATH, REPOS-URL)."""
     path = (os.path.join(svntest.main.general_repo_dir, self.name)
             + '.' + suffix)
-    url = svntest.main.test_area_url + '/' + svntest.main.pathname2url(path)
+    url = svntest.main.options.test_area_url + \
+                                        '/' + svntest.main.pathname2url(path)
     self.add_test_path(path, remove)
     return path, url
 
@@ -211,7 +212,7 @@ def cleanup_deferred_test_paths():
 
 
 def _cleanup_test_path(path, retrying=False):
-  if svntest.main.verbose_mode:
+  if svntest.main.options.verbose:
     if retrying:
       print("CLEANUP: RETRY: %s" % path)
     else:

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=984234&r1=984233&r2=984234&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 22:07:24 2010
@@ -47,6 +47,14 @@ class TextColors:
     cls.FAILURE = ''
     cls.SUCCESS = ''
 
+  @classmethod
+  def success(cls, str):
+    return lambda: cls.SUCCESS + str + cls.ENDC
+
+  @classmethod
+  def failure(cls, str):
+    return lambda: cls.FAILURE + str + cls.ENDC
+
 
 if not sys.stdout.isatty() or sys.platform == 'win32':
   TextColors.disable()
@@ -57,9 +65,9 @@ class TestCase:
   several methods that need to be overridden."""
 
   _result_map = {
-    RESULT_OK:   (0, TextColors.SUCCESS + 'PASS: ' + TextColors.ENDC, True),
-    RESULT_FAIL: (1, TextColors.FAILURE + 'FAIL: ' + TextColors.ENDC, False),
-    RESULT_SKIP: (2, TextColors.SUCCESS + 'SKIP: ' + TextColors.ENDC, True),
+    RESULT_OK:   (0, TextColors.success('PASS: '), True),
+    RESULT_FAIL: (1, TextColors.failure('FAIL: '), False),
+    RESULT_SKIP: (2, TextColors.success('SKIP: '), True),
     }
 
   def __init__(self, delegate=None, cond_func=lambda: True, doc=None, wip=None):
@@ -105,7 +113,9 @@ class TestCase:
   def results(self, result):
     # if our condition applied, then use our result map. otherwise, delegate.
     if self._cond_func():
-      return self._result_map[result]
+      val = list(self._result_map[result])
+      val[1] = val[1]()
+      return val
     return self._delegate.results(result)
 
 
@@ -164,9 +174,9 @@ class XFail(TestCase):
   """A test that is expected to fail, if its condition is true."""
 
   _result_map = {
-    RESULT_OK:   (1, TextColors.FAILURE + 'XPASS:' + TextColors.ENDC, False),
-    RESULT_FAIL: (0, TextColors.SUCCESS + 'XFAIL:' + TextColors.ENDC, True),
-    RESULT_SKIP: (2, TextColors.SUCCESS + 'SKIP: ' + TextColors.ENDC, True),
+    RESULT_OK:   (1, TextColors.failure('XPASS:'), False),
+    RESULT_FAIL: (0, TextColors.success('XFAIL:'), True),
+    RESULT_SKIP: (2, TextColors.success('SKIP: '), True),
     }
 
   def __init__(self, test_case, cond_func=lambda: True, wip=None):
@@ -193,9 +203,9 @@ class Wimp(XFail):
   is not considered a test failure."""
 
   _result_map = {
-    RESULT_OK:   (0, TextColors.SUCCESS + 'XPASS:' + TextColors.ENDC, True),
-    RESULT_FAIL: (0, TextColors.SUCCESS + 'XFAIL:' + TextColors.ENDC, True),
-    RESULT_SKIP: (2, TextColors.SUCCESS + 'SKIP: ' + TextColors.ENDC, True),
+    RESULT_OK:   (0, TextColors.success('XPASS:'), True),
+    RESULT_FAIL: (0, TextColors.success('XFAIL:'), True),
+    RESULT_SKIP: (2, TextColors.success('SKIP: '), True),
     }
 
   def __init__(self, wip, test_case, cond_func=lambda: True):

Modified: subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/wc.py
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/wc.py?rev=984234&r1=984233&r2=984234&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/wc.py (original)
+++ subversion/branches/ignore-mergeinfo/subversion/tests/cmdline/svntest/wc.py Tue Aug 10 22:07:24 2010
@@ -820,8 +820,31 @@ def text_base_path(file_path):
   """Return the path to the text-base file for the versioned file
      FILE_PATH."""
   dot_svn = svntest.main.get_admin_name()
-  return os.path.join(os.path.dirname(file_path), dot_svn, 'text-base',
-                      os.path.basename(file_path) + '.svn-base')
+  parent_path, file_name = os.path.split(file_path)
+
+  ### Temporary until the wc format changes.
+  text_base_path = os.path.join(parent_path, dot_svn, 'text-base')
+  if os.path.exists(text_base_path):
+    return os.path.join(text_base_path, file_name + '.svn-base')
+
+  db_path = os.path.join(parent_path, dot_svn, 'wc.db')
+  db = svntest.sqlite3.connect(db_path)
+  c = db.cursor()
+  c.execute("""select checksum from working_node
+               where local_relpath = '""" + file_name + """'""")
+  checksum = c.fetchone()
+  if checksum is None:
+    c.execute("""select checksum from base_node
+                 where local_relpath = '""" + file_name + """'""")
+    checksum = c.fetchone()[0]
+  if checksum is not None and checksum[0:6] == "$md5 $":
+    c.execute("""select checksum from pristine
+                 where md5_checksum = '""" + checksum + """'""")
+    checksum = c.fetchone()[0]
+  if checksum is None:
+    raise svntest.Failure("No SHA1 checksum for " + file_path)
+  db.close()
+  return os.path.join(parent_path, dot_svn, 'pristine', checksum[6:])
 
 
 # ------------