subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From br...@apache.org
Subject svn commit: r1649205 [29/30] - in /subversion/branches/authzperf: ./ build/ build/ac-macros/ notes/ subversion/bindings/ctypes-python/ subversion/bindings/cxxhl/ subversion/bindings/javahl/tests/org/apache/subversion/javahl/ subversion/bindings/swig/ s...
Date Sat, 03 Jan 2015 14:00:44 GMT
Modified: subversion/branches/authzperf/subversion/tests/cmdline/svnsync_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/tests/cmdline/svnsync_tests.py?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/tests/cmdline/svnsync_tests.py (original)
+++ subversion/branches/authzperf/subversion/tests/cmdline/svnsync_tests.py Sat Jan  3 14:00:41 2015
@@ -209,8 +209,12 @@ def setup_and_sync(sbox, dump_file_conte
 
   return dest_sbox
 
-def verify_mirror(dest_sbox, src_sbox):
-  """Compare the contents of the DEST_SBOX repository with EXP_DUMP_FILE_CONTENTS."""
+def verify_mirror(dest_sbox, exp_dump_file_contents):
+  """Compare the contents of the mirror repository in DEST_SBOX with
+     EXP_DUMP_FILE_CONTENTS, by comparing the parsed dump stream content.
+
+     First remove svnsync rev-props from the DEST_SBOX repository.
+  """
 
   # Remove some SVNSync-specific housekeeping properties from the
   # mirror repository in preparation for the comparison dump.
@@ -222,10 +226,9 @@ def verify_mirror(dest_sbox, src_sbox):
 
   # Create a dump file from the mirror repository.
   dest_dump = svntest.actions.run_and_verify_dump(dest_sbox.repo_dir)
-  src_dump = svntest.actions.run_and_verify_dump(src_sbox.repo_dir)
 
   svntest.verify.compare_dump_files(
-    "Dump files", "DUMP", src_dump, dest_dump)
+    "Dump files", "DUMP", exp_dump_file_contents, dest_dump)
 
 def run_test(sbox, dump_file_name, subdir=None, exp_dump_file_name=None,
              bypass_prop_validation=False, source_prop_encoding=None,
@@ -251,16 +254,12 @@ or another dump file."""
   # dump file (used to create the master repository) or another specified dump
   # file.
   if exp_dump_file_name:
-    build_repos(sbox)
-    svntest.actions.run_and_verify_load(sbox.repo_dir,
-                                        open(os.path.join(svnsync_tests_dir,
-                                                          exp_dump_file_name),
-                                             'rb').readlines())
-    src_sbox = sbox
+    exp_dump_file_contents = open(os.path.join(svnsync_tests_dir,
+                                  exp_dump_file_name), 'rb').readlines()
   else:
-    src_sbox = sbox
+    exp_dump_file_contents = master_dumpfile_contents
 
-  verify_mirror(dest_sbox, sbox)
+  verify_mirror(dest_sbox, exp_dump_file_contents)
 
 
 
@@ -564,9 +563,7 @@ def delete_revprops(sbox):
   run_copy_revprops(dest_sbox.repo_url, sbox.repo_url)
 
   # Does the result look as we expected?
-  build_repos(sbox)
-  svntest.actions.run_and_verify_load(sbox.repo_dir, expected_contents)
-  verify_mirror(dest_sbox, sbox)
+  verify_mirror(dest_sbox, expected_contents)
 
 @Issue(3870)
 @SkipUnless(svntest.main.is_posix_os)
@@ -576,6 +573,15 @@ def fd_leak_sync_from_serf_to_local(sbox
   resource.setrlimit(resource.RLIMIT_NOFILE, (128, 128))
   run_test(sbox, "largemods.dump", is_src_ra_local=None, is_dest_ra_local=True)
 
+#----------------------------------------------------------------------
+
+@Issue(4476)
+def mergeinfo_contains_r0(sbox):
+  "mergeinfo contains r0"
+  run_test(sbox, "mergeinfo-contains-r0.dump",
+           exp_dump_file_name="mergeinfo-contains-r0.expected.dump",
+           bypass_prop_validation=True)
+
 
 ########################################################################
 # Run the tests
@@ -612,6 +618,7 @@ test_list = [ None,
               descend_into_replace,
               delete_revprops,
               fd_leak_sync_from_serf_to_local, # calls setrlimit
+              mergeinfo_contains_r0,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/authzperf/subversion/tests/cmdline/svntest/main.py
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/tests/cmdline/svntest/main.py?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/tests/cmdline/svntest/main.py (original)
+++ subversion/branches/authzperf/subversion/tests/cmdline/svntest/main.py Sat Jan  3 14:00:41 2015
@@ -908,68 +908,10 @@ def file_substitute(path, contents, new_
   fcontent = open(path, 'r').read().replace(contents, new_contents)
   open(path, 'w').write(fcontent)
 
-def _unpack_precooked_repos(path, template):
-  testdir = os.path.dirname(os.path.abspath(os.path.dirname(__file__)))
-  repozip = os.path.join(os.path.dirname(testdir), "templates", template)
-  zipfile.ZipFile(repozip, 'r').extractall(path)
-
-# For creating new, pre-cooked greek repositories
-def unpack_greek_repos(path):
-  template = "greek-fsfs-v%d.zip" % options.fsfs_version
-  _unpack_precooked_repos(path, template)
-
-# For creating blank new repositories
-def create_repos(path, minor_version = None):
-  """Create a brand-new SVN repository at PATH.  If PATH does not yet
-  exist, create it."""
-
-  if not os.path.exists(path):
-    os.makedirs(path) # this creates all the intermediate dirs, if necessary
-
-  if options.fsfs_version is None:
-    if options.fs_type == "bdb":
-      opts = ("--bdb-txn-nosync",)
-    else:
-      opts = ()
-    if minor_version is None or minor_version > options.server_minor_version:
-      minor_version = options.server_minor_version
-    opts += ("--compatible-version=1.%d" % (minor_version),)
-    if options.fs_type is not None:
-      opts += ("--fs-type=" + options.fs_type,)
-    exit_code, stdout, stderr = run_command(svnadmin_binary, 1, False,
-                                            "create", path, *opts)
-  else:
-    # Copy a pre-cooked FSFS repository
-    assert options.fs_type == "fsfs"
-    template = "empty-fsfs-v%d.zip" % options.fsfs_version
-    _unpack_precooked_repos(path, template)
-    exit_code, stdout, stderr = run_command(svnadmin_binary, 1, False,
-                                            "setuuid", path)
-
-  # Skip tests if we can't create the repository.
-  if stderr:
-    stderr_lines = 0
-    not_using_fsfs_backend = (options.fs_type != "fsfs")
-    backend_deprecation_warning = False
-    for line in stderr:
-      stderr_lines += 1
-      if line.find('Unknown FS type') != -1:
-        raise Skip
-      if not_using_fsfs_backend:
-        if 0 < line.find('repository back-end is deprecated, consider using'):
-          backend_deprecation_warning = True
-
-    # Creating BDB repositories will cause svnadmin to print a warning
-    # which should be ignored.
-    if (stderr_lines == 1
-        and not_using_fsfs_backend
-        and backend_deprecation_warning):
-      pass
-    else:
-      # If the FS type is known and we noticed more than just the
-      # BDB-specific warning, assume the repos couldn't be created
-      # (e.g. due to a missing 'svnadmin' binary).
-      raise SVNRepositoryCreateFailure("".join(stderr).rstrip())
+# For setting up authz and hooks in existing repos
+def _post_create_repos(path, minor_version = None):
+  """Set default access right configurations for svnserve and mod_dav
+  as well as hooks etc. in the SVN repository at PATH."""
 
   # Require authentication to write to the repos, for ra_svn testing.
   file_write(get_svnserve_conf_file_path(path),
@@ -1039,6 +981,73 @@ def create_repos(path, minor_version = N
   # make the repos world-writeable, for mod_dav_svn's sake.
   chmod_tree(path, 0666, 0666)
 
+def _unpack_precooked_repos(path, template):
+  testdir = os.path.dirname(os.path.abspath(os.path.dirname(__file__)))
+  repozip = os.path.join(os.path.dirname(testdir), "templates", template)
+  zipfile.ZipFile(repozip, 'r').extractall(path)
+
+# For creating new, pre-cooked greek repositories
+def unpack_greek_repos(path):
+  template = "greek-fsfs-v%d.zip" % options.fsfs_version
+  _unpack_precooked_repos(path, template)
+  _post_create_repos(path, options.server_minor_version)
+
+# For creating blank new repositories
+def create_repos(path, minor_version = None):
+  """Create a brand-new SVN repository at PATH.  If PATH does not yet
+  exist, create it."""
+
+  if not os.path.exists(path):
+    os.makedirs(path) # this creates all the intermediate dirs, if necessary
+
+  if options.fsfs_version is None:
+    if options.fs_type == "bdb":
+      opts = ("--bdb-txn-nosync",)
+    else:
+      opts = ()
+    if minor_version is None or minor_version > options.server_minor_version:
+      minor_version = options.server_minor_version
+    opts += ("--compatible-version=1.%d" % (minor_version),)
+    if options.fs_type is not None:
+      opts += ("--fs-type=" + options.fs_type,)
+    exit_code, stdout, stderr = run_command(svnadmin_binary, 1, False,
+                                            "create", path, *opts)
+  else:
+    # Copy a pre-cooked FSFS repository
+    assert options.fs_type == "fsfs"
+    template = "empty-fsfs-v%d.zip" % options.fsfs_version
+    _unpack_precooked_repos(path, template)
+    exit_code, stdout, stderr = run_command(svnadmin_binary, 1, False,
+                                            "setuuid", path)
+
+  # Skip tests if we can't create the repository.
+  if stderr:
+    stderr_lines = 0
+    not_using_fsfs_backend = (options.fs_type != "fsfs")
+    backend_deprecation_warning = False
+    for line in stderr:
+      stderr_lines += 1
+      if line.find('Unknown FS type') != -1:
+        raise Skip
+      if not_using_fsfs_backend:
+        if 0 < line.find('repository back-end is deprecated, consider using'):
+          backend_deprecation_warning = True
+
+    # Creating BDB repositories will cause svnadmin to print a warning
+    # which should be ignored.
+    if (stderr_lines == 1
+        and not_using_fsfs_backend
+        and backend_deprecation_warning):
+      pass
+    else:
+      # If the FS type is known and we noticed more than just the
+      # BDB-specific warning, assume the repos couldn't be created
+      # (e.g. due to a missing 'svnadmin' binary).
+      raise SVNRepositoryCreateFailure("".join(stderr).rstrip())
+
+  # Configure the new repository.
+  _post_create_repos(path, minor_version)
+
 # For copying a repository
 def copy_repos(src_path, dst_path, head_revision, ignore_uuid = 1,
                minor_version = None):
@@ -1465,6 +1474,17 @@ def is_plaintext_password_storage_disabl
     return False
   return True
 
+
+# https://issues.apache.org/bugzilla/show_bug.cgi?id=56480
+__mod_dav_url_quoting_broken_versions = frozenset([
+    '2.2.26',
+    '2.4.9',
+])
+def is_mod_dav_url_quoting_broken():
+    if is_ra_type_dav():
+        return (options.httpd_version in __mod_dav_url_quoting_broken_versions)
+    return None
+
 ######################################################################
 
 
@@ -1526,6 +1546,8 @@ class TestSpawningThread(threading.Threa
       args.append('--http-proxy-username=' + options.http_proxy_username)
     if options.http_proxy_password:
       args.append('--http-proxy-password=' + options.http_proxy_password)
+    if options.httpd_version:
+      args.append('--httpd-version=' + options.httpd_version)
     if options.exclusive_wc_locks:
       args.append('--exclusive-wc-locks')
     if options.memcached_server:
@@ -1887,6 +1909,8 @@ def _create_parser():
                     help='Username for the HTTP Proxy.')
   parser.add_option('--http-proxy-password', action='store',
                     help='Password for the HTTP Proxy.')
+  parser.add_option('--httpd-version', action='store',
+                    help='Assume HTTPD is this version.')
   parser.add_option('--tools-bin', action='store', dest='tools_bin',
                     help='Use the svn tools installed in this path')
   parser.add_option('--exclusive-wc-locks', action='store_true',

Modified: subversion/branches/authzperf/subversion/tests/cmdline/switch_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/tests/cmdline/switch_tests.py?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/tests/cmdline/switch_tests.py (original)
+++ subversion/branches/authzperf/subversion/tests/cmdline/switch_tests.py Sat Jan  3 14:00:41 2015
@@ -2860,6 +2860,49 @@ def switch_keywords(sbox):
                                         None, expected_disk, expected_status,
                                         None, None, None, None, None)
 
+@Issue(4524)
+def switch_moves(sbox):
+  "switch moves on wc checkpoint"
+
+  sbox.build()
+
+  sbox.simple_move('A/B', 'B')
+  sbox.simple_rm('A')
+
+  branch_url = sbox.repo_url + '/branch'
+
+  svntest.actions.run_and_verify_svn(None, None, [],
+                                     'cp', sbox.wc_dir, branch_url,
+                                     '-m', '')
+
+  expected_disk = svntest.wc.State('', {
+    'B/E/alpha' : Item(contents="This is the file 'alpha'.\n"),
+    'B/E/beta'  : Item(contents="This is the file 'beta'.\n"),
+    'B/lambda'  : Item(contents="This is the file 'lambda'.\n"),
+    'B/F'       : Item(),
+    'iota'      : Item(contents="This is the file 'iota'.\n"),
+  })
+
+  expected_status = svntest.wc.State(sbox.wc_dir, {
+    ''          : Item(status='  ', wc_rev='2'),
+    'B'         : Item(status='R ', copied='+', treeconflict='C', wc_rev='-'),
+    'B/lambda'  : Item(status='  ', copied='+', wc_rev='-'),
+    'B/F'       : Item(status='  ', copied='+', wc_rev='-'),
+    'B/E'       : Item(status='  ', copied='+', wc_rev='-'),
+    'B/E/beta'  : Item(status='  ', copied='+', wc_rev='-'),
+    'B/E/alpha' : Item(status='  ', copied='+', wc_rev='-'),
+    'A'         : Item(status='! ', treeconflict='C'),
+    'iota'      : Item(status='  ', wc_rev='2'),
+  })
+
+  # In Subversion 1.8 this scenario causes an Sqlite row not found error.
+  # It would be nice if we could handle the tree conflict more intelligent, as
+  # the working copy matches the incomming change.
+  svntest.actions.run_and_verify_switch(sbox.wc_dir, sbox.ospath(''), branch_url,
+                                        None, expected_disk, expected_status,
+                                        None, None, None, None, None)
+
+
 ########################################################################
 # Run the tests
 
@@ -2903,6 +2946,7 @@ test_list = [ None,
               switch_to_spaces,
               switch_across_replacement,
               switch_keywords,
+              switch_moves,
               ]
 
 if __name__ == '__main__':

Modified: subversion/branches/authzperf/subversion/tests/libsvn_fs/fs-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/tests/libsvn_fs/fs-test.c?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/tests/libsvn_fs/fs-test.c (original)
+++ subversion/branches/authzperf/subversion/tests/libsvn_fs/fs-test.c Sat Jan  3 14:00:41 2015
@@ -41,6 +41,7 @@
 #include "svn_private_config.h"
 #include "private/svn_fs_util.h"
 #include "private/svn_fs_private.h"
+#include "private/svn_fspath.h"
 
 #include "../svn_test_fs.h"
 
@@ -207,24 +208,35 @@ reopen_trivial_transaction(const svn_tes
 {
   svn_fs_t *fs;
   svn_fs_txn_t *txn;
+  svn_fs_root_t *root;
   const char *txn_name;
   apr_pool_t *subpool = svn_pool_create(pool);
 
   SVN_ERR(svn_test__create_fs(&fs, "test-repo-reopen-trivial-txn",
                               opts, pool));
 
-  /* Begin a new transaction that is based on revision 0.  */
+  /* Create a first transaction - we don't want that one to reopen. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
+
+  /* Begin a second transaction that is based on revision 0.  */
   SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
 
   /* Don't use the subpool, txn_name must persist beyond the current txn */
   SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
 
+  /* Create a third transaction - we don't want that one to reopen. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
+
   /* Close the transaction. */
   svn_pool_clear(subpool);
 
   /* Reopen the transaction by name */
   SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, subpool));
 
+  /* Does it have the same name? */
+  SVN_ERR(svn_fs_txn_root(&root, txn, subpool));
+  SVN_TEST_STRING_ASSERT(svn_fs_txn_root_name(root, subpool), txn_name);
+
   /* Close the transaction ... again. */
   svn_pool_destroy(subpool);
 
@@ -1126,6 +1138,8 @@ basic_commit(const svn_test_opts_t *opts
 
   /* Create the greek tree. */
   SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
+  SVN_TEST_ASSERT(svn_fs_is_txn_root(txn_root));
+  SVN_TEST_ASSERT(!svn_fs_is_revision_root(txn_root));
 
   /* Commit it. */
   SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, pool));
@@ -1139,6 +1153,8 @@ basic_commit(const svn_test_opts_t *opts
 
   /* Get root of the revision */
   SVN_ERR(svn_fs_revision_root(&revision_root, fs, after_rev, pool));
+  SVN_TEST_ASSERT(!svn_fs_is_txn_root(revision_root));
+  SVN_TEST_ASSERT(svn_fs_is_revision_root(revision_root));
 
   /* Check the tree. */
   SVN_ERR(svn_test__check_greek_tree(revision_root, pool));
@@ -1580,6 +1596,10 @@ merging_commit(const svn_test_opts_t *op
     SVN_ERR(svn_fs_make_file(txn_root, "theta", pool));
     SVN_ERR(svn_test__set_file_contents
             (txn_root, "theta", "This is another file 'theta'.\n", pool));
+
+    /* TXN must actually be based upon revisions[4] (instead of HEAD). */
+    SVN_TEST_ASSERT(svn_fs_txn_base_revision(txn) == revisions[4]);
+
     SVN_ERR(test_commit_txn(&failed_rev, txn, "/theta", pool));
     SVN_ERR(svn_fs_abort_txn(txn, pool));
 
@@ -1603,6 +1623,9 @@ merging_commit(const svn_test_opts_t *op
     SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
     SVN_ERR(svn_fs_delete(txn_root, "A/D/H", pool));
 
+    /* TXN must actually be based upon revisions[1] (instead of HEAD). */
+    SVN_TEST_ASSERT(svn_fs_txn_base_revision(txn) == revisions[1]);
+
     /* We used to create the revision like this before fixing issue
        #2751 -- Directory prop mods reverted in overlapping commits scenario.
 
@@ -4171,6 +4194,12 @@ check_related(const svn_test_opts_t *opt
       { "E", 7 }, { "E", 8 }, { "F", 9 }, { "F", 10 }
     };
 
+    /* Latest revision that touched the respective path. */
+    struct path_rev_t latest_changes[6] = {
+      { "A", 4 }, { "B", 6 }, { "C", 6 },
+      { "D", 7 }, { "E", 8 }, { "F", 10 }
+    };
+
     int related_matrix[16][16] = {
       /* A1 ... F10 across the top here*/
       { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* A1 */
@@ -4200,14 +4229,16 @@ check_related(const svn_test_opts_t *opt
             struct path_rev_t pr2 = path_revs[j];
             const svn_fs_id_t *id1, *id2;
             int related = 0;
+            svn_fs_node_relation_t relation;
+            svn_fs_root_t *rev_root1, *rev_root2;
 
             /* Get the ID for the first path/revision combination. */
-            SVN_ERR(svn_fs_revision_root(&rev_root, fs, pr1.rev, subpool));
-            SVN_ERR(svn_fs_node_id(&id1, rev_root, pr1.path, subpool));
+            SVN_ERR(svn_fs_revision_root(&rev_root1, fs, pr1.rev, subpool));
+            SVN_ERR(svn_fs_node_id(&id1, rev_root1, pr1.path, subpool));
 
             /* Get the ID for the second path/revision combination. */
-            SVN_ERR(svn_fs_revision_root(&rev_root, fs, pr2.rev, subpool));
-            SVN_ERR(svn_fs_node_id(&id2, rev_root, pr2.path, subpool));
+            SVN_ERR(svn_fs_revision_root(&rev_root2, fs, pr2.rev, subpool));
+            SVN_ERR(svn_fs_node_id(&id2, rev_root2, pr2.path, subpool));
 
             /* <exciting> Now, run the relationship check! </exciting> */
             related = svn_fs_check_related(id1, id2) ? 1 : 0;
@@ -4230,9 +4261,74 @@ check_related(const svn_test_opts_t *opt
                    pr1.path, (int)pr1.rev, pr2.path, (int)pr2.rev);
               }
 
+            /* Asking directly, i.e. without involving the noderev IDs as
+             * an intermediate, should yield the same results. */
+            SVN_ERR(svn_fs_node_relation(&relation, rev_root1, pr1.path,
+                                         rev_root2, pr2.path, subpool));
+            if (i == j)
+              {
+                /* Identical note. */
+                if (!related || relation != svn_fs_node_same)
+                  {
+                    return svn_error_createf
+                      (SVN_ERR_TEST_FAILED, NULL,
+                      "expected '%s:%d' to be the same as '%s:%d';"
+                      " it was not",
+                      pr1.path, (int)pr1.rev, pr2.path, (int)pr2.rev);
+                  }
+              }
+            else if (related && relation != svn_fs_node_common_ancestor)
+              {
+                return svn_error_createf
+                  (SVN_ERR_TEST_FAILED, NULL,
+                   "expected '%s:%d' to have a common ancestor with '%s:%d';"
+                   " it had not",
+                   pr1.path, (int)pr1.rev, pr2.path, (int)pr2.rev);
+              }
+            else if (!related && relation != svn_fs_node_unrelated)
+              {
+                return svn_error_createf
+                  (SVN_ERR_TEST_FAILED, NULL,
+                   "expected '%s:%d' to not be related to '%s:%d'; it was",
+                   pr1.path, (int)pr1.rev, pr2.path, (int)pr2.rev);
+              }
+
             svn_pool_clear(subpool);
           } /* for ... */
       } /* for ... */
+
+    /* Verify that the noderevs stay the same after their last change. */
+    for (i = 0; i < 6; ++i)
+      {
+        const char *path = latest_changes[i].path;
+        svn_revnum_t latest = latest_changes[i].rev;
+        svn_fs_root_t *latest_root;
+        svn_revnum_t rev;
+        svn_fs_node_relation_t relation;
+
+        /* FS root of the latest change. */
+        svn_pool_clear(subpool);
+        SVN_ERR(svn_fs_revision_root(&latest_root, fs, latest, subpool));
+
+        /* All future revisions. */
+        for (rev = latest + 1; rev <= 10; ++rev)
+          {
+            /* Query their noderev relationship to the latest change. */
+            SVN_ERR(svn_fs_revision_root(&rev_root, fs, rev, subpool));
+            SVN_ERR(svn_fs_node_relation(&relation, latest_root, path,
+                                         rev_root, path, subpool));
+
+            /* They shall use the same noderevs */
+            if (relation != svn_fs_node_same)
+              {
+                return svn_error_createf
+                  (SVN_ERR_TEST_FAILED, NULL,
+                  "expected '%s:%d' to be the same as '%s:%d';"
+                  " it was not",
+                  path, (int)latest, path, (int)rev);
+              }
+          } /* for ... */
+      } /* for ... */
   }
 
   /* Destroy the subpool. */
@@ -4320,22 +4416,49 @@ branch_test(const svn_test_opts_t *opts,
 }
 
 
+/* Verify that file FILENAME under ROOT has the same contents checksum
+ * as CONTENTS when comparing the checksums of the given TYPE.
+ * Use POOL for temporary allocations. */
+static svn_error_t *
+verify_file_checksum(svn_stringbuf_t *contents,
+                     svn_fs_root_t *root,
+                     const char *filename,
+                     svn_checksum_kind_t type,
+                     apr_pool_t *pool)
+{
+  svn_checksum_t *expected_checksum, *actual_checksum;
+
+  /* Write a file, compare the repository's idea of its checksum
+     against our idea of its checksum.  They should be the same. */
+  SVN_ERR(svn_checksum(&expected_checksum, type, contents->data,
+                       contents->len, pool));
+  SVN_ERR(svn_fs_file_checksum(&actual_checksum, type, root, filename, TRUE,
+                               pool));
+  if (!svn_checksum_match(expected_checksum, actual_checksum))
+    return svn_error_createf
+      (SVN_ERR_FS_GENERAL, NULL,
+       "verify-checksum: checksum mismatch:\n"
+       "   expected:  %s\n"
+       "     actual:  %s\n",
+       svn_checksum_to_cstring(expected_checksum, pool),
+       svn_checksum_to_cstring(actual_checksum, pool));
+
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 verify_checksum(const svn_test_opts_t *opts,
                 apr_pool_t *pool)
 {
   svn_fs_t *fs;
   svn_fs_txn_t *txn;
-  svn_fs_root_t *txn_root;
+  svn_fs_root_t *txn_root, *rev_root;
   svn_stringbuf_t *str;
-  svn_checksum_t *expected_checksum, *actual_checksum;
+  svn_revnum_t rev;
 
   /* Write a file, compare the repository's idea of its checksum
      against our idea of its checksum.  They should be the same. */
-
   str = svn_stringbuf_create("My text editor charges me rent.", pool);
-  SVN_ERR(svn_checksum(&expected_checksum, svn_checksum_md5, str->data,
-                       str->len, pool));
 
   SVN_ERR(svn_test__create_fs(&fs, "test-repo-verify-checksum",
                               opts, pool));
@@ -4343,17 +4466,20 @@ verify_checksum(const svn_test_opts_t *o
   SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
   SVN_ERR(svn_fs_make_file(txn_root, "fact", pool));
   SVN_ERR(svn_test__set_file_contents(txn_root, "fact", str->data, pool));
-  SVN_ERR(svn_fs_file_checksum(&actual_checksum, svn_checksum_md5, txn_root,
-                               "fact", TRUE, pool));
 
-  if (!svn_checksum_match(expected_checksum, actual_checksum))
-    return svn_error_createf
-      (SVN_ERR_FS_GENERAL, NULL,
-       "verify-checksum: checksum mismatch:\n"
-       "   expected:  %s\n"
-       "     actual:  %s\n",
-       svn_checksum_to_cstring(expected_checksum, pool),
-       svn_checksum_to_cstring(actual_checksum, pool));
+  /* Do it for the txn. */
+  SVN_ERR(verify_file_checksum(str, txn_root, "fact", svn_checksum_md5,
+                               pool));
+  SVN_ERR(verify_file_checksum(str, txn_root, "fact", svn_checksum_sha1,
+                               pool));
+
+  /* Do it again - this time for the revision. */
+  SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool));
+  SVN_ERR(svn_fs_revision_root(&rev_root, fs, rev, pool));
+  SVN_ERR(verify_file_checksum(str, rev_root, "fact", svn_checksum_md5,
+                               pool));
+  SVN_ERR(verify_file_checksum(str, rev_root, "fact", svn_checksum_sha1,
+                               pool));
 
   return SVN_NO_ERROR;
 }
@@ -5403,6 +5529,1172 @@ reopen_modify(const svn_test_opts_t *opt
 #endif
 }
 
+static svn_error_t *
+upgrade_while_committing(const svn_test_opts_t *opts,
+                         apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_revnum_t head_rev = 0;
+  svn_fs_root_t *root;
+  svn_fs_txn_t *txn1, *txn2;
+  const char *fs_path;
+  apr_hash_t *fs_config = apr_hash_make(pool);
+
+  /* Bail (with success) on known-untestable scenarios */
+  if (strcmp(opts->fs_type, "fsfs") != 0)
+    return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
+                            "this will test FSFS repositories only");
+
+  if (opts->server_minor_version && (opts->server_minor_version < 6))
+    return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
+                            "pre-1.6 SVN doesn't support FSFS packing");
+
+  /* Create test repository with greek tree. */
+  fs_path = "test-upgrade-while-committing";
+
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_COMPATIBLE_VERSION, "1.7");
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_SHARD_SIZE, "2");
+  SVN_ERR(svn_test__create_fs2(&fs, fs_path, opts, fs_config, pool));
+
+  SVN_ERR(svn_fs_begin_txn(&txn1, fs, head_rev, pool));
+  SVN_ERR(svn_fs_txn_root(&root, txn1, pool));
+  SVN_ERR(svn_test__create_greek_tree(root, pool));
+  SVN_ERR(test_commit_txn(&head_rev, txn1, NULL, pool));
+
+  /* Create txn with changes. */
+  SVN_ERR(svn_fs_begin_txn(&txn1, fs, head_rev, pool));
+  SVN_ERR(svn_fs_txn_root(&root, txn1, pool));
+  SVN_ERR(svn_fs_make_dir(root, "/foo", pool));
+
+  /* Upgrade filesystem, but keep existing svn_fs_t object. */
+  SVN_ERR(svn_fs_upgrade(fs_path, pool));
+
+  /* Creating a new txn for the old svn_fs_t should not fail. */
+  SVN_ERR(svn_fs_begin_txn(&txn2, fs, head_rev, pool));
+
+  /* Committing the already existing txn should not fail. */
+  SVN_ERR(test_commit_txn(&head_rev, txn1, NULL, pool));
+
+  /* Verify filesystem content. */
+  SVN_ERR(svn_fs_verify(fs_path, NULL, 0, SVN_INVALID_REVNUM, NULL, NULL,
+                        NULL, NULL, pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Utility method for test_paths_changed. Verify that REV in FS changes
+ * exactly one path and that that change is a property change.  Expect
+ * the MERGEINFO_MOD flag of the change to have the given value.
+ */
+static svn_error_t *
+verify_root_prop_change(svn_fs_t *fs,
+                        svn_revnum_t rev,
+                        svn_tristate_t mergeinfo_mod,
+                        apr_pool_t *pool)
+{
+  svn_fs_path_change2_t *change;
+  svn_fs_root_t *root;
+  apr_hash_t *changes;
+
+  SVN_ERR(svn_fs_revision_root(&root, fs, rev, pool));
+  SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
+  SVN_TEST_ASSERT(apr_hash_count(changes) == 1);
+  change = svn_hash_gets(changes, "/");
+
+  SVN_TEST_ASSERT(change->node_rev_id);
+  SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_modify);
+  SVN_TEST_ASSERT(   change->node_kind == svn_node_dir
+                  || change->node_kind == svn_node_unknown);
+  SVN_TEST_ASSERT(change->text_mod == FALSE);
+  SVN_TEST_ASSERT(change->prop_mod == TRUE);
+
+  if (change->copyfrom_known)
+    {
+      SVN_TEST_ASSERT(change->copyfrom_rev == SVN_INVALID_REVNUM);
+      SVN_TEST_ASSERT(change->copyfrom_path == NULL);
+    }
+
+  SVN_TEST_ASSERT(change->mergeinfo_mod == mergeinfo_mod);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_paths_changed(const svn_test_opts_t *opts,
+                   apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_revnum_t head_rev = 0;
+  svn_fs_root_t *root;
+  svn_fs_txn_t *txn;
+  const char *fs_path;
+  apr_hash_t *changes;
+  svn_boolean_t has_mergeinfo_mod = FALSE;
+  apr_hash_index_t *hi;
+  int i;
+
+  /* The "mergeinfo_mod flag will say "unknown" until recently. */
+  if (   strcmp(opts->fs_type, "bdb") != 0
+      && (!opts->server_minor_version || (opts->server_minor_version >= 9)))
+    has_mergeinfo_mod = TRUE;
+
+  /* Create test repository with greek tree. */
+  fs_path = "test-paths-changed";
+
+  SVN_ERR(svn_test__create_fs2(&fs, fs_path, opts, NULL, pool));
+
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
+  SVN_ERR(svn_fs_txn_root(&root, txn, pool));
+  SVN_ERR(svn_test__create_greek_tree(root, pool));
+  SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
+
+  /* Create txns with various prop changes. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
+  SVN_ERR(svn_fs_txn_root(&root, txn, pool));
+  SVN_ERR(svn_fs_change_node_prop(root, "/", "propname",
+                                  svn_string_create("propval", pool), pool));
+  SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
+
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
+  SVN_ERR(svn_fs_txn_root(&root, txn, pool));
+  SVN_ERR(svn_fs_change_node_prop(root, "/", "svn:mergeinfo",
+                                  svn_string_create("/: 1\n", pool), pool));
+  SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
+
+  /* Verify changed path lists. */
+
+  /* Greek tree creation rev. */
+  SVN_ERR(svn_fs_revision_root(&root, fs, head_rev - 2, pool));
+  SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
+
+  /* Reports all paths? */
+  for (i = 0; svn_test__greek_tree_nodes[i].path; ++i)
+    {
+      const char *path
+        = svn_fspath__canonicalize(svn_test__greek_tree_nodes[i].path, pool);
+
+      SVN_TEST_ASSERT(svn_hash_gets(changes, path));
+    }
+
+  SVN_TEST_ASSERT(apr_hash_count(changes) == i);
+
+  /* Verify per-path info. */
+  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+    {
+      svn_fs_path_change2_t *change = apr_hash_this_val(hi);
+
+      SVN_TEST_ASSERT(change->node_rev_id);
+      SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_add);
+      SVN_TEST_ASSERT(   change->node_kind == svn_node_file
+                      || change->node_kind == svn_node_dir
+                      || change->node_kind == svn_node_unknown);
+
+      if (change->node_kind != svn_node_unknown)
+        SVN_TEST_ASSERT(change->text_mod == (   change->node_kind
+                                             == svn_node_file));
+
+      SVN_TEST_ASSERT(change->prop_mod == FALSE);
+
+      if (change->copyfrom_known)
+        {
+          SVN_TEST_ASSERT(change->copyfrom_rev == SVN_INVALID_REVNUM);
+          SVN_TEST_ASSERT(change->copyfrom_path == NULL);
+        }
+
+      if (has_mergeinfo_mod)
+        SVN_TEST_ASSERT(change->mergeinfo_mod == svn_tristate_false);
+      else
+        SVN_TEST_ASSERT(change->mergeinfo_mod == svn_tristate_unknown);
+    }
+
+  /* Propset rev. */
+  SVN_ERR(verify_root_prop_change(fs, head_rev - 1,
+                                  has_mergeinfo_mod ? svn_tristate_false
+                                                    : svn_tristate_unknown,
+                                  pool));
+
+  /* Mergeinfo set rev. */
+  SVN_ERR(verify_root_prop_change(fs, head_rev,
+                                  has_mergeinfo_mod ? svn_tristate_true
+                                                    : svn_tristate_unknown,
+                                  pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_delete_replaced_paths_changed(const svn_test_opts_t *opts,
+                                   apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_revnum_t head_rev = 0;
+  svn_fs_root_t *root;
+  svn_fs_txn_t *txn;
+  const char *fs_path;
+  apr_hash_t *changes;
+  svn_fs_path_change2_t *change;
+  const svn_fs_id_t *file_id;
+
+  /* Create test repository with greek tree. */
+  fs_path = "test-delete-replace-paths-changed";
+
+  SVN_ERR(svn_test__create_fs2(&fs, fs_path, opts, NULL, pool));
+
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
+  SVN_ERR(svn_fs_txn_root(&root, txn, pool));
+  SVN_ERR(svn_test__create_greek_tree(root, pool));
+  SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
+
+  /* Create that replaces a file with a folder and then deletes that
+   * replacement.  Start with the deletion. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, head_rev, pool));
+  SVN_ERR(svn_fs_txn_root(&root, txn, pool));
+  SVN_ERR(svn_fs_delete(root, "/iota", pool));
+
+  /* The change list should now report a deleted file. */
+  SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
+  change = svn_hash_gets(changes, "/iota");
+  file_id = change->node_rev_id;
+  SVN_TEST_ASSERT(   change->node_kind == svn_node_file
+                  || change->node_kind == svn_node_unknown);
+  SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_delete);
+
+  /* Add a replacement. */
+  SVN_ERR(svn_fs_make_dir(root, "/iota", pool));
+
+  /* The change list now reports a replacement by a directory. */
+  SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
+  change = svn_hash_gets(changes, "/iota");
+  SVN_TEST_ASSERT(   change->node_kind == svn_node_dir
+                  || change->node_kind == svn_node_unknown);
+  SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_replace);
+  SVN_TEST_ASSERT(svn_fs_compare_ids(change->node_rev_id, file_id) != 0);
+
+  /* Delete the replacement again. */
+  SVN_ERR(svn_fs_delete(root, "/iota", pool));
+
+  /* The change list should now be reported as a deleted file again. */
+  SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
+  change = svn_hash_gets(changes, "/iota");
+  SVN_TEST_ASSERT(   change->node_kind == svn_node_file
+                  || change->node_kind == svn_node_unknown);
+  SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_delete);
+  SVN_TEST_ASSERT(svn_fs_compare_ids(change->node_rev_id, file_id) == 0);
+
+  /* Finally, commit the change. */
+  SVN_ERR(test_commit_txn(&head_rev, txn, NULL, pool));
+
+  /* The committed revision should still report the same change. */
+  SVN_ERR(svn_fs_revision_root(&root, fs, head_rev, pool));
+  SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
+  change = svn_hash_gets(changes, "/iota");
+  SVN_TEST_ASSERT(   change->node_kind == svn_node_file
+                  || change->node_kind == svn_node_unknown);
+  SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_delete);
+
+  return SVN_NO_ERROR;
+}
+
+/* Get rid of transaction NAME in FS.  This function deals with backend-
+ * specific behavior as permitted by the API. */
+static svn_error_t *
+cleanup_txn(svn_fs_t *fs,
+            const char *name,
+            apr_pool_t *scratch_pool)
+{
+  /* Get rid of the txns one at a time. */
+  svn_error_t *err = svn_fs_purge_txn(fs, name, scratch_pool);
+
+  /* Some backends (BDB) don't support purging transactions that have never
+   * seen an abort or commit attempt.   Simply abort those txns. */
+  if (err && err->apr_err == SVN_ERR_FS_TRANSACTION_NOT_DEAD)
+    {
+      svn_fs_txn_t *txn;
+      svn_error_clear(err);
+      err = SVN_NO_ERROR;
+
+      SVN_ERR(svn_fs_open_txn(&txn, fs, name, scratch_pool));
+      SVN_ERR(svn_fs_abort_txn(txn, scratch_pool));
+
+      /* Should be gone now ... */
+      SVN_TEST_ASSERT_ERROR(svn_fs_open_txn(&txn, fs, name, scratch_pool),
+                            SVN_ERR_FS_NO_SUCH_TRANSACTION);
+    }
+
+  return svn_error_trace(err);
+}
+
+/* Make sure we get txn lists correctly. */
+static svn_error_t *
+purge_txn_test(const svn_test_opts_t *opts,
+               apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_fs_txn_t *txn;
+  const char *name1, *name2;
+  apr_array_header_t *txn_list;
+  apr_pool_t *subpool = svn_pool_create(pool);
+
+  SVN_ERR(svn_test__create_fs(&fs, "test-repo-purge-txn",
+                              opts, pool));
+
+  /* Begin a new transaction, get its name (in the top pool), close it.  */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
+  SVN_ERR(svn_fs_txn_name(&name1, txn, pool));
+
+  /* Begin *another* transaction, get its name (in the top pool), close it.  */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
+  SVN_ERR(svn_fs_txn_name(&name2, txn, pool));
+  svn_pool_clear(subpool);
+
+  /* Get rid of the txns one at a time. */
+  SVN_ERR(cleanup_txn(fs, name1, pool));
+
+  /* There should be exactly one left. */
+  SVN_ERR(svn_fs_list_transactions(&txn_list, fs, pool));
+
+  /* Check the list. It should have *exactly* one entry. */
+  SVN_TEST_ASSERT(   txn_list->nelts == 1
+                  && !strcmp(name2, APR_ARRAY_IDX(txn_list, 0, const char *)));
+
+  /* Get rid of the other txn as well. */
+  SVN_ERR(cleanup_txn(fs, name2, pool));
+
+  /* There should be exactly one left. */
+  SVN_ERR(svn_fs_list_transactions(&txn_list, fs, pool));
+
+  /* Check the list. It should have no entries. */
+  SVN_TEST_ASSERT(txn_list->nelts == 0);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+compare_contents(const svn_test_opts_t *opts,
+                 apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_fs_txn_t *txn;
+  svn_fs_root_t *txn_root, *root1, *root2;
+  const char *original = "original contents";
+  svn_revnum_t rev;
+  int i;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+
+  /* Two similar but different texts that yield the same MD5 digest. */
+  const char *evil_text1
+    = "\xd1\x31\xdd\x02\xc5\xe6\xee\xc4\x69\x3d\x9a\x06\x98\xaf\xf9\x5c"
+      "\x2f\xca\xb5\x87\x12\x46\x7e\xab\x40\x04\x58\x3e\xb8\xfb\x7f\x89"
+      "\x55\xad\x34\x06\x09\xf4\xb3\x02\x83\xe4\x88\x83\x25\x71\x41\x5a"
+      "\x08\x51\x25\xe8\xf7\xcd\xc9\x9f\xd9\x1d\xbd\xf2\x80\x37\x3c\x5b"
+      "\xd8\x82\x3e\x31\x56\x34\x8f\x5b\xae\x6d\xac\xd4\x36\xc9\x19\xc6"
+      "\xdd\x53\xe2\xb4\x87\xda\x03\xfd\x02\x39\x63\x06\xd2\x48\xcd\xa0"
+      "\xe9\x9f\x33\x42\x0f\x57\x7e\xe8\xce\x54\xb6\x70\x80\xa8\x0d\x1e"
+      "\xc6\x98\x21\xbc\xb6\xa8\x83\x93\x96\xf9\x65\x2b\x6f\xf7\x2a\x70";
+  const char *evil_text2
+    = "\xd1\x31\xdd\x02\xc5\xe6\xee\xc4\x69\x3d\x9a\x06\x98\xaf\xf9\x5c"
+      "\x2f\xca\xb5\x07\x12\x46\x7e\xab\x40\x04\x58\x3e\xb8\xfb\x7f\x89"
+      "\x55\xad\x34\x06\x09\xf4\xb3\x02\x83\xe4\x88\x83\x25\xf1\x41\x5a"
+      "\x08\x51\x25\xe8\xf7\xcd\xc9\x9f\xd9\x1d\xbd\x72\x80\x37\x3c\x5b"
+      "\xd8\x82\x3e\x31\x56\x34\x8f\x5b\xae\x6d\xac\xd4\x36\xc9\x19\xc6"
+      "\xdd\x53\xe2\x34\x87\xda\x03\xfd\x02\x39\x63\x06\xd2\x48\xcd\xa0"
+      "\xe9\x9f\x33\x42\x0f\x57\x7e\xe8\xce\x54\xb6\x70\x80\x28\x0d\x1e"
+      "\xc6\x98\x21\xbc\xb6\xa8\x83\x93\x96\xf9\x65\xab\x6f\xf7\x2a\x70";
+  svn_checksum_t *checksum1, *checksum2;
+
+  /* (path, rev) pairs to compare plus the expected API return values */
+  struct 
+    {
+      svn_revnum_t rev1;
+      const char *path1;
+      svn_revnum_t rev2;
+      const char *path2;
+
+      svn_boolean_t different;  /* result of svn_fs_*_different */
+      svn_tristate_t changed;   /* result of svn_fs_*_changed */
+    } to_compare[] =
+    {
+      /* same representation */
+      { 1, "foo", 2, "foo", FALSE, svn_tristate_false },
+      { 1, "foo", 2, "bar", FALSE, svn_tristate_false },
+      { 2, "foo", 2, "bar", FALSE, svn_tristate_false },
+
+      /* different content but MD5 check is not reliable */
+      { 3, "foo", 3, "bar", TRUE, svn_tristate_true },
+
+      /* different contents */
+      { 1, "foo", 3, "bar", TRUE, svn_tristate_true },
+      { 1, "foo", 3, "foo", TRUE, svn_tristate_true },
+      { 3, "foo", 4, "bar", TRUE, svn_tristate_true },
+      { 3, "foo", 4, "bar", TRUE, svn_tristate_true },
+      { 2, "bar", 3, "bar", TRUE, svn_tristate_true },
+      { 3, "bar", 4, "bar", TRUE, svn_tristate_true },
+
+      /* variations on the same theme: same content, possibly different rep */
+      { 4, "foo", 4, "bar", FALSE, svn_tristate_unknown }, 
+      { 1, "foo", 4, "bar", FALSE, svn_tristate_unknown }, 
+      { 2, "foo", 4, "bar", FALSE, svn_tristate_unknown }, 
+      { 1, "foo", 4, "foo", FALSE, svn_tristate_unknown }, 
+      { 2, "foo", 4, "foo", FALSE, svn_tristate_unknown }, 
+      { 2, "bar", 4, "bar", FALSE, svn_tristate_unknown }, 
+
+      /* EOL */
+      { 0 },
+    };
+
+  /* Same same, but different.
+   * Just checking that we actually have an MD5 collision. */
+  SVN_ERR(svn_checksum(&checksum1, svn_checksum_md5, evil_text1,
+                       strlen(evil_text1), pool));
+  SVN_ERR(svn_checksum(&checksum2, svn_checksum_md5, evil_text2,
+                       strlen(evil_text2), pool));
+  SVN_TEST_ASSERT(svn_checksum_match(checksum1, checksum1));
+  SVN_TEST_ASSERT(strcmp(evil_text1, evil_text2));
+
+  /* Now, build up our test repo. */
+  SVN_ERR(svn_test__create_fs(&fs, "test-repo-compare-contents",
+                              opts, pool));
+
+  /* Rev 1: create a file. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, iterpool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
+  SVN_ERR(svn_fs_make_file(txn_root, "foo", iterpool));
+  SVN_ERR(svn_test__set_file_contents(txn_root, "foo", original, iterpool));
+  SVN_ERR(svn_fs_change_node_prop(txn_root, "foo", "prop",
+                                  svn_string_create(original, iterpool),
+                                  iterpool));
+  SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, iterpool));
+  SVN_TEST_ASSERT(rev == 1);
+  svn_pool_clear(iterpool);
+
+  /* Rev 2: copy that file. */
+  SVN_ERR(svn_fs_revision_root(&root1, fs, rev, iterpool));
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, iterpool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
+  SVN_ERR(svn_fs_copy(root1, "foo", txn_root, "bar", iterpool));
+  SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, iterpool));
+  SVN_TEST_ASSERT(rev == 2);
+  svn_pool_clear(iterpool);
+
+  /* Rev 3: modify both files.
+   * The new contents differs for both files but has the same length and MD5.
+   */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, iterpool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
+  SVN_ERR(svn_test__set_file_contents(txn_root, "foo", evil_text1, iterpool));
+  SVN_ERR(svn_test__set_file_contents(txn_root, "bar", evil_text2, iterpool));
+  SVN_ERR(svn_fs_change_node_prop(txn_root, "foo", "prop",
+                                  svn_string_create(evil_text1, iterpool),
+                                  iterpool));
+  SVN_ERR(svn_fs_change_node_prop(txn_root, "bar", "prop",
+                                  svn_string_create(evil_text2, iterpool),
+                                  iterpool));
+  SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, iterpool));
+  SVN_TEST_ASSERT(rev == 3);
+  svn_pool_clear(iterpool);
+
+  /* Rev 4: revert both file contents.
+   */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, iterpool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
+  SVN_ERR(svn_test__set_file_contents(txn_root, "foo", original, iterpool));
+  SVN_ERR(svn_test__set_file_contents(txn_root, "bar", original, iterpool));
+  SVN_ERR(svn_fs_change_node_prop(txn_root, "foo", "prop",
+                                  svn_string_create(original, iterpool),
+                                  iterpool));
+  SVN_ERR(svn_fs_change_node_prop(txn_root, "bar", "prop",
+                                  svn_string_create(original, iterpool),
+                                  iterpool));
+  SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, iterpool));
+  SVN_TEST_ASSERT(rev == 4);
+  svn_pool_clear(iterpool);
+
+  /* Perform all comparisons listed in TO_COMPARE. */
+  for (i = 0; to_compare[i].rev1 > 0; ++i)
+    {
+      svn_boolean_t text_different;
+      svn_boolean_t text_changed;
+      svn_boolean_t props_different;
+      svn_boolean_t props_changed;
+
+      svn_pool_clear(iterpool);
+      SVN_ERR(svn_fs_revision_root(&root1, fs, to_compare[i].rev1, iterpool));
+      SVN_ERR(svn_fs_revision_root(&root2, fs, to_compare[i].rev2, iterpool));
+
+      /* Compare node texts. */
+      SVN_ERR(svn_fs_contents_different(&text_different,
+                                        root1, to_compare[i].path1,
+                                        root2, to_compare[i].path2,
+                                        iterpool));
+      SVN_ERR(svn_fs_contents_changed(&text_changed,
+                                      root1, to_compare[i].path1,
+                                      root2, to_compare[i].path2,
+                                      iterpool));
+
+      /* Compare properties. */
+      SVN_ERR(svn_fs_props_different(&props_different,
+                                     root1, to_compare[i].path1,
+                                     root2, to_compare[i].path2,
+                                     iterpool));
+      SVN_ERR(svn_fs_props_changed(&props_changed,
+                                   root1, to_compare[i].path1,
+                                   root2, to_compare[i].path2,
+                                   iterpool));
+
+      /* Check results. */
+      SVN_TEST_ASSERT(text_different == to_compare[i].different);
+      SVN_TEST_ASSERT(props_different == to_compare[i].different);
+
+      switch (to_compare[i].changed)
+        {
+        case svn_tristate_true:
+          SVN_TEST_ASSERT(text_changed);
+          SVN_TEST_ASSERT(props_changed);
+          break;
+
+        case svn_tristate_false:
+          SVN_TEST_ASSERT(!text_changed);
+          SVN_TEST_ASSERT(!props_changed);
+          break;
+
+        default:
+          break;
+        }
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_path_change_create(const svn_test_opts_t *opts,
+                        apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_fs_root_t *root;
+  const svn_fs_id_t *id;
+  svn_fs_path_change2_t *change;
+
+  /* Build an empty test repo ... */
+  SVN_ERR(svn_test__create_fs(&fs, "test-repo-path-change-create",
+                              opts, pool));
+
+  /* ... just to give us a valid ID. */
+  SVN_ERR(svn_fs_revision_root(&root, fs, 0, pool));
+  SVN_ERR(svn_fs_node_id(&id, root, "", pool));
+
+  /* Do what we came here for. */
+  change = svn_fs_path_change2_create(id, svn_fs_path_change_replace, pool);
+
+  SVN_TEST_ASSERT(change);
+  SVN_TEST_ASSERT(change->node_rev_id == id);
+  SVN_TEST_ASSERT(change->change_kind == svn_fs_path_change_replace);
+
+  /* All other fields should be "empty" / "unused". */
+  SVN_TEST_ASSERT(change->node_kind == svn_node_none);
+
+  SVN_TEST_ASSERT(change->text_mod == FALSE);
+  SVN_TEST_ASSERT(change->prop_mod == FALSE);
+  SVN_TEST_ASSERT(change->mergeinfo_mod == svn_tristate_unknown);
+
+  SVN_TEST_ASSERT(change->copyfrom_known == FALSE);
+  SVN_TEST_ASSERT(change->copyfrom_rev == SVN_INVALID_REVNUM);
+  SVN_TEST_ASSERT(change->copyfrom_path == NULL);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_node_created_info(const svn_test_opts_t *opts,
+                       apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_fs_txn_t *txn;
+  svn_fs_root_t *txn_root, *root;
+  svn_revnum_t rev;
+  int i;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+
+  /* Test vectors. */
+  struct
+    {
+      svn_revnum_t rev;
+      const char *path;
+      svn_revnum_t crev;
+      const char *cpath;
+    } to_check[] =
+    {
+      /* New noderev only upon modification. */
+      { 1, "A/B/E/beta",  1, "/A/B/E/beta" },
+      { 2, "A/B/E/beta",  1, "/A/B/E/beta" },
+      { 3, "A/B/E/beta",  3, "/A/B/E/beta" },
+      { 4, "A/B/E/beta",  3, "/A/B/E/beta" },
+
+      /* Lazily copied node. */
+      { 2, "Z/B/E/beta",  1, "/A/B/E/beta" },
+      { 3, "Z/B/E/beta",  1, "/A/B/E/beta" },
+      { 4, "Z/B/E/beta",  4, "/Z/B/E/beta" },
+
+      /* Bubble-up upon sub-tree change. */
+      { 2, "Z",  2, "/Z" },
+      { 3, "Z",  2, "/Z" },
+      { 4, "Z",  4, "/Z" },
+
+      { 0 }
+    };
+
+  /* Start with a new repo and the greek tree in rev 1. */
+  SVN_ERR(svn_test__create_fs(&fs, "test-repo-node-created-path",
+                              opts, pool));
+
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, iterpool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
+  SVN_ERR(svn_test__create_greek_tree(txn_root, iterpool));
+  SVN_ERR(test_commit_txn(&rev, txn, NULL, iterpool));
+  svn_pool_clear(iterpool);
+
+  /* r2: copy a subtree */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, iterpool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
+  SVN_ERR(svn_fs_revision_root(&root, fs, rev, iterpool));
+  SVN_ERR(svn_fs_copy(root, "A", txn_root, "Z", iterpool));
+  SVN_ERR(test_commit_txn(&rev, txn, NULL, iterpool));
+  svn_pool_clear(iterpool);
+
+  /* r3: touch node in copy source */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, iterpool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
+  SVN_ERR(svn_test__set_file_contents(txn_root, "A/B/E/beta", "new", iterpool));
+  SVN_ERR(test_commit_txn(&rev, txn, NULL, iterpool));
+  svn_pool_clear(iterpool);
+
+  /* r4: touch same relative node in copy target */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, iterpool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
+  SVN_ERR(svn_test__set_file_contents(txn_root, "Z/B/E/beta", "new", iterpool));
+  SVN_ERR(test_commit_txn(&rev, txn, NULL, iterpool));
+  svn_pool_clear(iterpool);
+
+  /* Now ask for some 'node created' info. */
+  for (i = 0; to_check[i].rev > 0; ++i)
+    {
+      svn_revnum_t crev;
+      const char *cpath;
+
+      svn_pool_clear(iterpool);
+
+      /* Get created path and rev. */
+      SVN_ERR(svn_fs_revision_root(&root, fs, to_check[i].rev, iterpool));
+      SVN_ERR(svn_fs_node_created_path(&cpath, root, to_check[i].path,
+                                       iterpool));
+      SVN_ERR(svn_fs_node_created_rev(&crev, root, to_check[i].path,
+                                      iterpool));
+
+      /* Compare the results with our expectations. */
+      SVN_TEST_STRING_ASSERT(cpath, to_check[i].cpath);
+
+      if (crev != to_check[i].crev)
+        return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+                                 "created rev mismatch for %s@%ld:\n"
+                                 "  expected '%ld'\n"
+                                 "     found '%ld",
+                                 to_check[i].path,
+                                 to_check[i].rev,
+                                 to_check[i].crev,
+                                 crev);
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_print_modules(const svn_test_opts_t *opts,
+                   apr_pool_t *pool)
+{
+  const char *expected, *module_name;
+  svn_stringbuf_t *modules = svn_stringbuf_create_empty(pool);
+
+  /* Name of the providing module */
+  if (strcmp(opts->fs_type, "fsx") == 0)
+    module_name = "fs_x";
+  else if (strcmp(opts->fs_type, "fsfs") == 0)
+    module_name = "fs_fs";
+  else if (strcmp(opts->fs_type, "bdb") == 0)
+    module_name = "fs_base";
+  else
+    return svn_error_createf(SVN_ERR_TEST_SKIPPED, NULL,
+                             "don't know the module name for %s",
+                             opts->fs_type);
+
+  SVN_ERR(svn_fs_print_modules(modules, pool));
+
+  /* The requested FS type must be listed amongst the available modules. */
+  expected = apr_psprintf(pool, "* %s : ", module_name);
+  SVN_TEST_ASSERT(strstr(modules->data, expected));
+
+  return SVN_NO_ERROR;
+}
+
+/* Baton to be used with process_file_contents. */
+typedef struct process_file_contents_baton_t
+{
+  const char *contents;
+  svn_boolean_t processed;
+} process_file_contents_baton_t;
+
+/* Implements svn_fs_process_contents_func_t.
+ * We flag the BATON as "processed" and compare the CONTENTS we've got to
+ * what we expect through the BATON.
+ */
+static svn_error_t *
+process_file_contents(const unsigned char *contents,
+                      apr_size_t len,
+                      void *baton,
+                      apr_pool_t *scratch_pool)
+{
+  process_file_contents_baton_t *b = baton;
+
+  SVN_TEST_ASSERT(strlen(b->contents) == len);
+  SVN_TEST_ASSERT(memcmp(b->contents, contents, len) == 0);
+  b->processed = TRUE;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_zero_copy_processsing(const svn_test_opts_t *opts,
+                           apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_fs_txn_t *txn;
+  svn_fs_root_t *txn_root, *root;
+  svn_revnum_t rev;
+  const struct svn_test__tree_entry_t *node;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+
+  /* Start with a new repo and the greek tree in rev 1. */
+  SVN_ERR(svn_test__create_fs(&fs, "test-repo-zero-copy-processing",
+                              opts, pool));
+
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, iterpool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
+  SVN_ERR(svn_test__create_greek_tree(txn_root, iterpool));
+  SVN_ERR(test_commit_txn(&rev, txn, NULL, iterpool));
+  svn_pool_clear(iterpool);
+
+  SVN_ERR(svn_fs_revision_root(&root, fs, rev, pool));
+
+  /* Prime the full-text cache by reading all file contents. */
+  for (node = svn_test__greek_tree_nodes; node->path; node++)
+    if (node->contents)
+      {
+        svn_stream_t *stream;
+        svn_pool_clear(iterpool);
+
+        SVN_ERR(svn_fs_file_contents(&stream, root, node->path, iterpool));
+        SVN_ERR(svn_stream_copy3(stream, svn_stream_buffered(iterpool),
+                                NULL, NULL, iterpool));
+      }
+
+  /* Now, try to get the data directly from cache
+   * (if the backend has caches). */
+  for (node = svn_test__greek_tree_nodes; node->path; node++)
+    if (node->contents)
+      {
+        svn_boolean_t success;
+
+        process_file_contents_baton_t baton;
+        baton.contents = node->contents;
+        baton.processed = FALSE;
+
+        svn_pool_clear(iterpool);
+
+        SVN_ERR(svn_fs_try_process_file_contents(&success, root, node->path,
+                                                process_file_contents, &baton,
+                                                iterpool));
+        SVN_TEST_ASSERT(success == baton.processed);
+      }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_dir_optimal_order(const svn_test_opts_t *opts,
+                       apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_fs_txn_t *txn;
+  svn_fs_root_t *txn_root, *root;
+  svn_revnum_t rev;
+  apr_hash_t *unordered;
+  apr_array_header_t *ordered;
+  int i;
+  apr_hash_index_t *hi;
+
+  /* Create a new repo and the greek tree in rev 1. */
+  SVN_ERR(svn_test__create_fs(&fs, "test-repo-dir-optimal-order",
+                              opts, pool));
+
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+  SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
+  SVN_ERR(test_commit_txn(&rev, txn, NULL, pool));
+
+  SVN_ERR(svn_fs_revision_root(&root, fs, rev, pool));
+
+  /* Call the API function we are interested in. */
+  SVN_ERR(svn_fs_dir_entries(&unordered, root, "A", pool));
+  SVN_ERR(svn_fs_dir_optimal_order(&ordered, root, unordered, pool));
+
+  /* Verify that all entries are returned. */
+  SVN_TEST_ASSERT(ordered->nelts == apr_hash_count(unordered));
+  for (hi = apr_hash_first(pool, unordered); hi; hi = apr_hash_next(hi))
+    {
+      svn_boolean_t found = FALSE;
+      const char *name = apr_hash_this_key(hi);
+
+      /* Compare hash -> array because the array might contain the same
+       * entry more than once.  Since that can't happen in the hash, doing
+       * it in this direction ensures ORDERED won't contain duplicates.
+       */
+      for (i = 0; !found && i < ordered->nelts; ++i)
+        {
+          svn_fs_dirent_t *item = APR_ARRAY_IDX(ordered, i, svn_fs_dirent_t*);
+          if (strcmp(item->name, name) == 0)
+            {
+              found = TRUE;
+              SVN_TEST_ASSERT(item == apr_hash_this_val(hi));
+            }
+        }
+
+      SVN_TEST_ASSERT(found);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_config_files(const svn_test_opts_t *opts,
+                  apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  apr_array_header_t *files;
+  int i;
+  const char *repo_name = "test-repo-config-files";
+
+  /* Create a empty and get its config files. */
+  SVN_ERR(svn_test__create_fs(&fs, repo_name, opts, pool));
+  SVN_ERR(svn_fs_info_config_files(&files, fs, pool, pool));
+
+  /* All files should exist and be below the repo. */
+  for (i = 0; i < files->nelts; ++i)
+    {
+      svn_node_kind_t kind;
+      const char *path = APR_ARRAY_IDX(files, i, const char*);
+
+      SVN_ERR(svn_io_check_path(path, &kind, pool));
+
+      SVN_TEST_ASSERT(kind == svn_node_file);
+      SVN_TEST_ASSERT(svn_dirent_is_ancestor(repo_name, path));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_delta_file_stream(const svn_test_opts_t *opts,
+                       apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_fs_txn_t *txn;
+  svn_fs_root_t *txn_root, *root1, *root2;
+  svn_revnum_t rev;
+
+  const char *old_content = "some content";
+  const char *new_content = "some more content";
+  svn_txdelta_window_handler_t delta_handler;
+  void *delta_baton;
+  svn_txdelta_stream_t *delta_stream;
+  svn_stringbuf_t *source = svn_stringbuf_create_empty(pool);
+  svn_stringbuf_t *dest = svn_stringbuf_create_empty(pool);
+
+  /* Create a new repo. */
+  SVN_ERR(svn_test__create_fs(&fs, "test-repo-delta-file-stream",
+                              opts, pool));
+
+  /* Revision 1: create a file. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+  SVN_ERR(svn_fs_make_file(txn_root, "foo", pool));
+  SVN_ERR(svn_test__set_file_contents(txn_root, "foo", old_content, pool));
+  SVN_ERR(test_commit_txn(&rev, txn, NULL, pool));
+
+  /* Revision 2: create a file. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+  SVN_ERR(svn_test__set_file_contents(txn_root, "foo", new_content, pool));
+  SVN_ERR(test_commit_txn(&rev, txn, NULL, pool));
+
+  SVN_ERR(svn_fs_revision_root(&root1, fs, 1, pool));
+  SVN_ERR(svn_fs_revision_root(&root2, fs, 2, pool));
+
+  /* Test 1: Get delta against empty target. */
+  SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream,
+                                       NULL, NULL, root1, "foo", pool));
+
+  svn_stringbuf_setempty(source);
+  svn_stringbuf_setempty(dest);
+
+  svn_txdelta_apply(svn_stream_from_stringbuf(source, pool),
+                    svn_stream_from_stringbuf(dest, pool),
+                    NULL, NULL, pool, &delta_handler, &delta_baton);
+  SVN_ERR(svn_txdelta_send_txstream(delta_stream,
+                                    delta_handler,
+                                    delta_baton,
+                                    pool));
+  SVN_TEST_STRING_ASSERT(old_content, dest->data);
+
+  /* Test 2: Get delta against previous version. */
+  SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream,
+                                       root1, "foo", root2, "foo", pool));
+
+  svn_stringbuf_set(source, old_content);
+  svn_stringbuf_setempty(dest);
+
+  svn_txdelta_apply(svn_stream_from_stringbuf(source, pool),
+                    svn_stream_from_stringbuf(dest, pool),
+                    NULL, NULL, pool, &delta_handler, &delta_baton);
+  SVN_ERR(svn_txdelta_send_txstream(delta_stream,
+                                    delta_handler,
+                                    delta_baton,
+                                    pool));
+  SVN_TEST_STRING_ASSERT(new_content, dest->data);
+
+  /* Test 3: Get reverse delta. */
+  SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream,
+                                       root2, "foo", root1, "foo", pool));
+
+  svn_stringbuf_set(source, new_content);
+  svn_stringbuf_setempty(dest);
+
+  svn_txdelta_apply(svn_stream_from_stringbuf(source, pool),
+                    svn_stream_from_stringbuf(dest, pool),
+                    NULL, NULL, pool, &delta_handler, &delta_baton);
+  SVN_ERR(svn_txdelta_send_txstream(delta_stream,
+                                    delta_handler,
+                                    delta_baton,
+                                    pool));
+  SVN_TEST_STRING_ASSERT(old_content, dest->data);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_fs_merge(const svn_test_opts_t *opts,
+              apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_fs_txn_t *txn;
+  svn_fs_root_t *txn_root, *root0, *root1;
+  svn_revnum_t rev;
+
+  /* Very basic test for svn_fs_merge because all the other interesting
+   * cases get tested implicitly with concurrent txn / commit tests.
+   * This API is just a thin layer around the internal merge function
+   * and we simply check that the plumbing between them works.
+   */
+
+  /* Create a new repo. */
+  SVN_ERR(svn_test__create_fs(&fs, "test-repo-fs-merge",
+                              opts, pool));
+  SVN_ERR(svn_fs_revision_root(&root0, fs, 0, pool));
+
+  /* Revision 1: create a file. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+  SVN_ERR(svn_fs_make_file(txn_root, "foo", pool));
+  SVN_ERR(test_commit_txn(&rev, txn, NULL, pool));
+  SVN_ERR(svn_fs_revision_root(&root1, fs, rev, pool));
+
+  /* Merge-able txn: create another file. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+  SVN_ERR(svn_fs_make_file(txn_root, "bar", pool));
+
+  SVN_ERR(svn_fs_merge(NULL, root1, "/", txn_root, "/", root0, "/", pool));
+
+  /* Not merge-able: create the same file file. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+  SVN_ERR(svn_fs_make_file(txn_root, "foo", pool));
+
+  SVN_TEST_ASSERT_ERROR(svn_fs_merge(NULL, root1, "/", txn_root, "/", root0,
+                                     "/", pool), SVN_ERR_FS_CONFLICT);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_fsfs_config_opts(const svn_test_opts_t *opts,
+                      apr_pool_t *pool)
+{
+  apr_hash_t *fs_config;
+  svn_fs_t *fs;
+  const svn_fs_info_placeholder_t *fs_info;
+  const svn_fs_fsfs_info_t *fsfs_info;
+
+  /* Bail (with SKIP) on known-untestable scenarios */
+  if (strcmp(opts->fs_type, "fsfs") != 0)
+    return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
+                            "this will test FSFS repositories only");
+
+  /* Remove the test directory from previous runs. */
+  SVN_ERR(svn_io_remove_dir2("test-fsfs-config-opts", TRUE, NULL, NULL, pool));
+
+  /* Create the test directory and add it to the test cleanup list. */
+  SVN_ERR(svn_io_dir_make("test-fsfs-config-opts", APR_OS_DEFAULT, pool));
+  svn_test_add_dir_cleanup("test-fsfs-config-opts");
+
+  /* Create an FSFS filesystem with default config.*/
+  fs_config = apr_hash_make(pool);
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FS_TYPE, SVN_FS_TYPE_FSFS);
+  SVN_ERR(svn_fs_create(&fs, "test-fsfs-config-opts/default", fs_config, pool));
+
+  /* Re-open FS to test the data on disk. */
+  SVN_ERR(svn_fs_open2(&fs, "test-fsfs-config-opts/default", NULL, pool, pool));
+
+  SVN_ERR(svn_fs_info(&fs_info, fs, pool, pool));
+  SVN_TEST_STRING_ASSERT(fs_info->fs_type, SVN_FS_TYPE_FSFS);
+  fsfs_info = (const void *) fs_info;
+
+  /* Check FSFS specific info. Don't check the SHARD_SIZE, because it depends
+   * on a compile-time constant and may be overridden. */
+  SVN_TEST_ASSERT(fsfs_info->log_addressing);
+  SVN_TEST_ASSERT(fsfs_info->min_unpacked_rev == 0);
+
+  /* Create an FSFS filesystem with custom settings: disabled log-addressing
+   * and custom shard size (123). */
+  fs_config = apr_hash_make(pool);
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FS_TYPE, SVN_FS_TYPE_FSFS);
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_LOG_ADDRESSING, "false");
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_SHARD_SIZE, "123");
+  SVN_ERR(svn_fs_create(&fs, "test-fsfs-config-opts/custom", fs_config, pool));
+
+  /* Re-open FS to test the data on disk. */
+  SVN_ERR(svn_fs_open2(&fs, "test-fsfs-config-opts/custom", NULL, pool, pool));
+
+  SVN_ERR(svn_fs_info(&fs_info, fs, pool, pool));
+  SVN_TEST_STRING_ASSERT(fs_info->fs_type, SVN_FS_TYPE_FSFS);
+  fsfs_info = (const void *) fs_info;
+
+  /* Check FSFS specific info, including the SHARD_SIZE. */
+  SVN_TEST_ASSERT(fsfs_info->log_addressing == FALSE);
+  SVN_TEST_ASSERT(fsfs_info->shard_size == 123);
+  SVN_TEST_ASSERT(fsfs_info->min_unpacked_rev == 0);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_txn_pool_lifetime(const svn_test_opts_t *opts,
+                       apr_pool_t *pool)
+{
+  /* Technically, the FS API makes no assumption on the lifetime of logically
+   * dependent objects.  In particular, a txn root object may get destroyed
+   * after the FS object that it has been built upon.  Actual data access is
+   * implied to be invalid without a valid svn_fs_t.
+   *
+   * This test verifies that at least the destruction order of those two
+   * objects is arbitrary.
+   */
+  svn_fs_t *fs;
+  svn_fs_txn_t *txn;
+  svn_fs_root_t *txn_root;
+
+  /* We will allocate FS in FS_POOL.  Using a separate allocator makes
+   * sure that we actually free the memory when destroying the pool.
+   */
+  apr_allocator_t *fs_allocator = svn_pool_create_allocator(FALSE);
+  apr_pool_t *fs_pool = apr_allocator_owner_get(fs_allocator);
+
+  /* Create a new repo. */
+  SVN_ERR(svn_test__create_fs(&fs, "test-repo-pool-lifetime",
+                              opts, fs_pool));
+
+  /* Create a TXN_ROOT referencing FS. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+
+  /* Destroy FS.  Depending on the actual allocator implementation,
+   * these memory pages becomes inaccessible. */
+  svn_pool_destroy(fs_pool);
+
+  /* Unclean implementations will try to access FS and may segfault here. */
+  svn_fs_close_root(txn_root);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_modify_txn_being_written(const svn_test_opts_t *opts,
+                              apr_pool_t *pool)
+{
+  /* FSFS has a limitation (and check) that only one file can be
+   * modified in TXN at time: see r861812 and svn_fs_apply_text() docstring.
+   * This is regression test for this behavior. */
+  svn_fs_t *fs;
+  svn_fs_txn_t *txn;
+  const char *txn_name;
+  svn_fs_root_t *txn_root;
+  svn_stream_t *foo_contents;
+  svn_stream_t *bar_contents;
+
+  /* Bail (with success) on known-untestable scenarios */
+  if (strcmp(opts->fs_type, SVN_FS_TYPE_FSFS) != 0)
+    return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
+                            "this will test FSFS repositories only");
+
+  /* Create a new repo. */
+  SVN_ERR(svn_test__create_fs(&fs, "test-modify-txn-being-written",
+                              opts, pool));
+
+  /* Create a TXN_ROOT referencing FS. */
+  SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
+  SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+
+  /* Make file /foo and open for writing.*/
+  SVN_ERR(svn_fs_make_file(txn_root, "/foo", pool));
+  SVN_ERR(svn_fs_apply_text(&foo_contents, txn_root, "/foo", NULL, pool));
+
+  /* Attempt to modify another file '/bar' -- FSFS doesn't allow this. */
+  SVN_ERR(svn_fs_make_file(txn_root, "/bar", pool));
+  SVN_TEST_ASSERT_ERROR(
+      svn_fs_apply_text(&bar_contents, txn_root, "/bar", NULL, pool),
+      SVN_ERR_FS_REP_BEING_WRITTEN);
+
+  /* *Reopen TXN. */
+  SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+
+  /* Check that file '/bar' still cannot be modified */
+  SVN_TEST_ASSERT_ERROR(
+      svn_fs_apply_text(&bar_contents, txn_root, "/bar", NULL, pool),
+      SVN_ERR_FS_REP_BEING_WRITTEN);
+
+  /* Close file '/foo'. */
+  SVN_ERR(svn_stream_close(foo_contents));
+
+  /* Now file '/bar' can be modified. */
+  SVN_ERR(svn_fs_apply_text(&bar_contents, txn_root, "/bar", NULL, pool));
+
+  return SVN_NO_ERROR;
+}
 
 /* ------------------------------------------------------------------------ */
 
@@ -5501,6 +6793,38 @@ static struct svn_test_descriptor_t test
     SVN_TEST_OPTS_WIMP(reopen_modify,
                        "test reopen and modify txn",
                        "txn_dir_cache fail in FSFS"),
+    SVN_TEST_OPTS_PASS(upgrade_while_committing,
+                       "upgrade while committing"),
+    SVN_TEST_OPTS_PASS(test_paths_changed,
+                       "test svn_fs_paths_changed"),
+    SVN_TEST_OPTS_PASS(test_delete_replaced_paths_changed,
+                       "test deletion after replace in changed paths list"),
+    SVN_TEST_OPTS_PASS(purge_txn_test,
+                       "test purging transactions"),
+    SVN_TEST_OPTS_PASS(compare_contents,
+                       "compare contents of different nodes"),
+    SVN_TEST_OPTS_PASS(test_path_change_create,
+                       "test svn_fs_path_change2_create"),
+    SVN_TEST_OPTS_PASS(test_node_created_info,
+                       "test FS 'node created' info"),
+    SVN_TEST_OPTS_PASS(test_print_modules,
+                       "test FS module listing"),
+    SVN_TEST_OPTS_PASS(test_zero_copy_processsing,
+                       "test zero copy file processing"),
+    SVN_TEST_OPTS_PASS(test_dir_optimal_order,
+                       "test svn_fs_dir_optimal_order"),
+    SVN_TEST_OPTS_PASS(test_config_files,
+                       "get configuration files"),
+    SVN_TEST_OPTS_PASS(test_delta_file_stream,
+                       "get a delta stream on a file"),
+    SVN_TEST_OPTS_PASS(test_fs_merge,
+                       "get merging txns with newer revisions"),
+    SVN_TEST_OPTS_PASS(test_fsfs_config_opts,
+                       "test creating FSFS repository with different opts"),
+    SVN_TEST_OPTS_PASS(test_txn_pool_lifetime,
+                       "test pool lifetime dependencies with txn roots"),
+    SVN_TEST_OPTS_PASS(test_modify_txn_being_written,
+                       "test modify txn being written in FSFS"),
     SVN_TEST_NULL
   };
 

Propchange: subversion/branches/authzperf/subversion/tests/libsvn_fs_fs/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Sat Jan  3 14:00:41 2015
@@ -4,7 +4,7 @@ Release
 test-repo-*
 upgrade_*
 fs-pack-test
-fs-fs-pack-test
+fs-fs-*-test
 test-get-set-revprop-packed-fs
 get_set_multiple_huge_revprops_packed_fs
 *.o

Modified: subversion/branches/authzperf/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c (original)
+++ subversion/branches/authzperf/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c Sat Jan  3 14:00:41 2015
@@ -1,4 +1,4 @@
-/* fs-pack-test.c --- tests for the filesystem
+/* fs-fs-pack-test.c --- tests for the FSFS filesystem
  *
  * ====================================================================
  *    Licensed to the Apache Software Foundation (ASF) under one
@@ -33,7 +33,6 @@
 #include "svn_props.h"
 #include "svn_fs.h"
 #include "private/svn_string_private.h"
-#include "private/svn_string_private.h"
 
 #include "../svn_test_fs.h"
 
@@ -51,65 +50,6 @@ ignore_fs_warnings(void *baton, svn_erro
   return;
 }
 
-/* Write the format number and maximum number of files per directory
-   to a new format file in PATH, overwriting a previously existing
-   file.  Use POOL for temporary allocation.
-
-   (This implementation is largely stolen from libsvn_fs_fs/fs_fs.c.) */
-static svn_error_t *
-write_format(const char *path,
-             int format,
-             int max_files_per_dir,
-             apr_pool_t *pool)
-{
-  const char *contents;
-
-  path = svn_dirent_join(path, "format", pool);
-
-  if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
-    {
-      if (format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT)
-        {
-          if (max_files_per_dir)
-            contents = apr_psprintf(pool,
-                                    "%d\n"
-                                    "layout sharded %d\n"
-                                    "addressing logical 0\n",
-                                    format, max_files_per_dir);
-          else
-            /* linear layouts never use logical addressing */
-            contents = apr_psprintf(pool,
-                                    "%d\n"
-                                    "layout linear\n"
-                                    "addressing physical\n",
-                                    format);
-        }
-      else
-        {
-          if (max_files_per_dir)
-            contents = apr_psprintf(pool,
-                                    "%d\n"
-                                    "layout sharded %d\n",
-                                    format, max_files_per_dir);
-          else
-            contents = apr_psprintf(pool,
-                                    "%d\n"
-                                    "layout linear\n",
-                                    format);
-        }
-    }
-  else
-    {
-      contents = apr_psprintf(pool, "%d\n", format);
-    }
-
-  SVN_ERR(svn_io_write_atomic(path, contents, strlen(contents),
-                              NULL /* copy perms */, pool));
-
-  /* And set the perms to make it read only */
-  return svn_io_set_file_read_only(path, FALSE, pool);
-}
-
 /* Return the expected contents of "iota" in revision REV. */
 static const char *
 get_rev_contents(svn_revnum_t rev, apr_pool_t *pool)
@@ -178,7 +118,7 @@ create_packed_filesystem(const char *dir
   apr_pool_t *subpool = svn_pool_create(pool);
   struct pack_notify_baton pnb;
   apr_pool_t *iterpool;
-  int version;
+  apr_hash_t *fs_config;
 
   /* Bail (with success) on known-untestable scenarios */
   if (strcmp(opts->fs_type, "fsfs") != 0)
@@ -189,25 +129,12 @@ create_packed_filesystem(const char *dir
     return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
                             "pre-1.6 SVN doesn't support FSFS packing");
 
-  /* Create a filesystem, then close it */
-  SVN_ERR(svn_test__create_fs(&fs, dir, opts, subpool));
-  svn_pool_destroy(subpool);
-
-  subpool = svn_pool_create(pool);
+  fs_config = apr_hash_make(pool);
+  svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_SHARD_SIZE,
+                apr_itoa(pool, shard_size));
 
-  /* Rewrite the format file.  (The rest of this function is backend-agnostic,
-     so we just avoid adding the FSFS-specific format information if we run on
-     some other backend.) */
-  if ((strcmp(opts->fs_type, "fsfs") == 0))
-    {
-      SVN_ERR(svn_io_read_version_file(&version,
-                                       svn_dirent_join(dir, "format", subpool),
-                                       subpool));
-      SVN_ERR(write_format(dir, version, shard_size, subpool));
-    }
-
-  /* Reopen the filesystem */
-  SVN_ERR(svn_fs_open2(&fs, dir, NULL, subpool, subpool));
+  /* Create a filesystem. */
+  SVN_ERR(svn_test__create_fs2(&fs, dir, opts, fs_config, subpool));
 
   /* Revision 1: the Greek tree */
   SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool));
@@ -569,7 +496,7 @@ get_set_large_revprop_packed_fs(const sv
    * files but do not exceed the pack size limit. */
   for (rev = 0; rev <= MAX_REV; ++rev)
     SVN_ERR(svn_fs_change_rev_prop(fs, rev, SVN_PROP_REVISION_LOG,
-                                   large_log(rev, 15000, pool),
+                                   large_log(rev, 1000, pool),
                                    pool));
 
   /* verify */
@@ -578,20 +505,20 @@ get_set_large_revprop_packed_fs(const sv
       SVN_ERR(svn_fs_revision_prop(&prop_value, fs, rev,
                                    SVN_PROP_REVISION_LOG, pool));
       SVN_TEST_STRING_ASSERT(prop_value->data,
-                             large_log(rev, 15000, pool)->data);
+                             large_log(rev, 1000, pool)->data);
     }
 
   /* Put a larger revprop into the last, some middle and the first revision
    * of a pack.  This should cause the packs to split in the middle. */
   SVN_ERR(svn_fs_change_rev_prop(fs, 3, SVN_PROP_REVISION_LOG,
                                  /* rev 0 is not packed */
-                                 large_log(3, 37000, pool),
+                                 large_log(3, 2400, pool),
                                  pool));
   SVN_ERR(svn_fs_change_rev_prop(fs, 5, SVN_PROP_REVISION_LOG,
-                                 large_log(5, 25000, pool),
+                                 large_log(5, 1500, pool),
                                  pool));
   SVN_ERR(svn_fs_change_rev_prop(fs, 8, SVN_PROP_REVISION_LOG,
-                                 large_log(8, 25000, pool),
+                                 large_log(8, 1500, pool),
                                  pool));
 
   /* verify */
@@ -602,13 +529,13 @@ get_set_large_revprop_packed_fs(const sv
 
       if (rev == 3)
         SVN_TEST_STRING_ASSERT(prop_value->data,
-                               large_log(rev, 37000, pool)->data);
+                               large_log(rev, 2400, pool)->data);
       else if (rev == 5 || rev == 8)
         SVN_TEST_STRING_ASSERT(prop_value->data,
-                               large_log(rev, 25000, pool)->data);
+                               large_log(rev, 1500, pool)->data);
       else
         SVN_TEST_STRING_ASSERT(prop_value->data,
-                               large_log(rev, 15000, pool)->data);
+                               large_log(rev, 1000, pool)->data);
     }
 
   return SVN_NO_ERROR;
@@ -856,7 +783,7 @@ pack_shard_size_one(const svn_test_opts_
 #undef SHARD_SIZE
 #undef MAX_REV
 /* ------------------------------------------------------------------------ */
-#define REPO_NAME "get_set_multiple_huge_revprops_packed_fs"
+#define REPO_NAME "test-repo-get_set_multiple_huge_revprops_packed_fs"
 #define SHARD_SIZE 4
 #define MAX_REV 9
 static svn_error_t *
@@ -1091,7 +1018,7 @@ upgrade_txns_to_log_addressing(const svn
 }
 #undef SHARD_SIZE
 
-#define REPO_NAME "upgrade_new_txns_to_log_addressing"
+#define REPO_NAME "test-repo-upgrade_new_txns_to_log_addressing"
 #define MAX_REV 8
 static svn_error_t *
 upgrade_new_txns_to_log_addressing(const svn_test_opts_t *opts,
@@ -1106,7 +1033,7 @@ upgrade_new_txns_to_log_addressing(const
 #undef MAX_REV
 
 /* ------------------------------------------------------------------------ */
-#define REPO_NAME "upgrade_old_txns_to_log_addressing"
+#define REPO_NAME "test-repo-upgrade_old_txns_to_log_addressing"
 #define MAX_REV 8
 static svn_error_t *
 upgrade_old_txns_to_log_addressing(const svn_test_opts_t *opts,
@@ -1123,7 +1050,7 @@ upgrade_old_txns_to_log_addressing(const
 
 /* ------------------------------------------------------------------------ */
 
-#define REPO_NAME "metadata_checksumming"
+#define REPO_NAME "test-repo-metadata_checksumming"
 static svn_error_t *
 metadata_checksumming(const svn_test_opts_t *opts,
                   apr_pool_t *pool)
@@ -1179,7 +1106,7 @@ metadata_checksumming(const svn_test_opt
 
 /* ------------------------------------------------------------------------ */
 
-#define REPO_NAME "revprop_caching_on_off"
+#define REPO_NAME "test-repo-revprop_caching_on_off"
 static svn_error_t *
 revprop_caching_on_off(const svn_test_opts_t *opts,
                        apr_pool_t *pool)

Propchange: subversion/branches/authzperf/subversion/tests/libsvn_fs_x/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Sat Jan  3 14:00:41 2015
@@ -80,4 +80,4 @@
 /subversion/branches/verify-at-commit/subversion/tests/libsvn_fs_x:1462039-1462408
 /subversion/branches/verify-keep-going/subversion/tests/libsvn_fs_x:1439280-1492639,1546002-1546110
 /subversion/branches/wc-collate-path/subversion/tests/libsvn_fs_x:1402685-1480384
-/subversion/trunk/subversion/tests/libsvn_fs_x:1414756-1509914,1613053-1617077
+/subversion/trunk/subversion/tests/libsvn_fs_x:1414756-1509914,1613053-1649188

Modified: subversion/branches/authzperf/subversion/tests/libsvn_fs_x/fs-x-pack-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/authzperf/subversion/tests/libsvn_fs_x/fs-x-pack-test.c?rev=1649205&r1=1649204&r2=1649205&view=diff
==============================================================================
--- subversion/branches/authzperf/subversion/tests/libsvn_fs_x/fs-x-pack-test.c (original)
+++ subversion/branches/authzperf/subversion/tests/libsvn_fs_x/fs-x-pack-test.c Sat Jan  3 14:00:41 2015
@@ -1,4 +1,4 @@
-/* fs-pack-test.c --- tests for the filesystem
+/* fs-x-pack-test.c --- tests for the FSX filesystem
  *
  * ====================================================================
  *    Licensed to the Apache Software Foundation (ASF) under one
@@ -294,30 +294,12 @@ pack_filesystem(const svn_test_opts_t *o
                                   apr_psprintf(pool, "%d.pack", i / SHARD_SIZE),
                                   "pack", SVN_VA_NULL);
 
-      /* These files should exist. */
+      /* This file should exist. */
       SVN_ERR(svn_io_check_path(path, &kind, pool));
       if (kind != svn_node_file)
         return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
                                  "Expected pack file '%s' not found", path);
 
-      path = svn_dirent_join_many(pool, REPO_NAME, "revs",
-                                  apr_psprintf(pool, "%d.pack", i / SHARD_SIZE),
-                                  "pack.l2p", SVN_VA_NULL);
-      SVN_ERR(svn_io_check_path(path, &kind, pool));
-      if (kind != svn_node_file)
-        return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
-                                  "Expected log-to-phys index file '%s' not found",
-                                  path);
-
-      path = svn_dirent_join_many(pool, REPO_NAME, "revs",
-                                  apr_psprintf(pool, "%d.pack", i / SHARD_SIZE),
-                                  "pack.p2l", SVN_VA_NULL);
-      SVN_ERR(svn_io_check_path(path, &kind, pool));
-      if (kind != svn_node_file)
-        return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
-                                  "Expected phys-to-log index file '%s' not found",
-                                  path);
-
       /* This directory should not exist. */
       path = svn_dirent_join_many(pool, REPO_NAME, "revs",
                                   apr_psprintf(pool, "%d", i / SHARD_SIZE),



Mime
View raw message