Modified: 
subversion/branches/addremove/subversion/tests/libsvn_client/mtcc-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_client/mtcc-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_client/mtcc-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_client/mtcc-test.c 
Sat May 23 14:16:56 2020
@@ -693,7 +693,7 @@ test_file_revs_both_ways(const svn_test_
                                 subpool));
   SVN_TEST_ASSERT(hrb.last == 6);
 
-  /* Ressurect mu */
+  /* Resurrect mu */
   svn_pool_clear(subpool);
   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 7, ctx, subpool, subpool));
   SVN_ERR(svn_client__mtcc_add_copy("mu", 6, "mu", mtcc, subpool));

Propchange: subversion/branches/addremove/subversion/tests/libsvn_delta/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Sat May 23 14:16:56 2020
@@ -8,6 +8,7 @@ vdelta-test
 random-test
 xml-output-test
 svndiff-test
+svndiff-stream-test
 window-test
 editor-test
 combined

Modified: subversion/branches/addremove/subversion/tests/libsvn_fs/fs-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_fs/fs-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_fs/fs-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_fs/fs-test.c Sat May 
23 14:16:56 2020
@@ -5479,7 +5479,7 @@ commit_timestamp(const svn_test_opts_t *
                           APR_HASH_KEY_STRING);
   SVN_TEST_ASSERT(!svn_date);
 
-  /* Commit that overwites a missing svn:date. */
+  /* Commit that overwrites a missing svn:date. */
   SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, pool));
   SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
   SVN_ERR(svn_fs_make_dir(txn_root, "/zig", pool));
@@ -7219,7 +7219,7 @@ test_cache_clear_during_stream(const svn
    * Just to be sure, make it not too uniform to keep self-txdelta at bay. */
   SVN_ERR(svn_fs_apply_textdelta(&consumer_func, &consumer_baton,
                                  txn_root, "/foo", NULL, NULL, subpool));
-  stream = svn_txdelta_target_push(consumer_func, consumer_baton, 
+  stream = svn_txdelta_target_push(consumer_func, consumer_baton,
                                    svn_stream_empty(subpool), subpool);
   for (i = 0; i < 10000; ++ i)
     {
@@ -7369,6 +7369,106 @@ closest_copy_test_svn_4677(const svn_tes
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_closest_copy_file_replaced_with_dir(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 *rev_root;
+  svn_revnum_t youngest_rev;
+  svn_fs_root_t *copy_root;
+  const char *copy_path;
+
+  /* Prepare a filesystem. */
+  SVN_ERR(svn_test__create_fs(&fs, "test-closest-copy-file-replaced-with-dir",
+                              opts, pool));
+
+  youngest_rev = 0;
+
+  /* Modeled after the case described in the thread:
+       "[PATCH] A test for "Can't get entries" error"
+       
https://lists.apache.org/thread.html/693a95b0da834387e78a7f08df2392b634397d32f37428c81c02f8c5@%3Cdev.subversion.apache.org%3E
+  */
+  /* r1: Add a directory with a file. */
+  SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+  SVN_ERR(svn_fs_make_dir(txn_root, "/A", pool));
+  SVN_ERR(svn_fs_make_file(txn_root, "/A/mu", pool));
+  SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool));
+  SVN_TEST_INT_ASSERT(youngest_rev, 1);
+
+  /* r2: Copy the directory. */
+  SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+  SVN_ERR(svn_fs_revision_root(&rev_root, fs, 1, pool));
+  SVN_ERR(svn_fs_copy(rev_root, "/A", txn_root, "/B", pool));
+  SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool));
+  SVN_TEST_INT_ASSERT(youngest_rev, 2);
+
+  /* r3: Delete the file. */
+  SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+  SVN_ERR(svn_fs_delete(txn_root, "/B/mu", pool));
+  SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool));
+  SVN_TEST_INT_ASSERT(youngest_rev, 3);
+
+  /* r4: Replace the file with a new directory containing a file. */
+  SVN_ERR(svn_fs_begin_txn2(&txn, fs, youngest_rev, 0, pool));
+  SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+  SVN_ERR(svn_fs_make_dir(txn_root, "/B/mu", pool));
+  SVN_ERR(svn_fs_make_file(txn_root, "/B/mu/iota", pool));
+  SVN_ERR(test_commit_txn(&youngest_rev, txn, NULL, pool));
+  SVN_TEST_INT_ASSERT(youngest_rev, 4);
+
+  /* Test a couple of svn_fs_closest_copy() calls; the second call used
+     to fail with an unexpected SVN_ERR_FS_NOT_DIRECTORY error. */
+
+  SVN_ERR(svn_fs_revision_root(&rev_root, fs, 2, pool));
+  SVN_ERR(svn_fs_closest_copy(&copy_root, &copy_path, rev_root, "/B/mu", 
pool));
+
+  SVN_TEST_ASSERT(copy_root != NULL);
+  SVN_TEST_INT_ASSERT(svn_fs_revision_root_revision(copy_root), 2);
+  SVN_TEST_STRING_ASSERT(copy_path, "/B");
+
+  SVN_ERR(svn_fs_revision_root(&rev_root, fs, 4, pool));
+  SVN_ERR(svn_fs_closest_copy(&copy_root, &copy_path, rev_root, "/B/mu/iota", 
pool));
+
+  SVN_TEST_ASSERT(copy_root == NULL);
+  SVN_TEST_ASSERT(copy_path == NULL);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_unrecognized_ioctl(const svn_test_opts_t *opts,
+                        apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_error_t *err;
+  svn_fs_ioctl_code_t code = {0};
+
+  SVN_ERR(svn_test__create_fs(&fs, "test-unrecognized-ioctl", opts, pool));
+
+  code.fs_type = "NON-EXISTING";
+  code.code = 98765;
+  err = svn_fs_ioctl(fs, code, NULL, NULL, NULL, NULL, pool, pool);
+  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE);
+
+  code.fs_type = "NON-EXISTING";
+  code.code = 98765;
+  err = svn_fs_ioctl(NULL, code, NULL, NULL, NULL, NULL, pool, pool);
+  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNKNOWN_FS_TYPE);
+
+  code.fs_type = opts->fs_type;
+  code.code = 98765;
+  err = svn_fs_ioctl(NULL, code, NULL, NULL, NULL, NULL, pool, pool);
+  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE);
+
+  return SVN_NO_ERROR;
+}
+
 /* ------------------------------------------------------------------------ */
 
 /* The test table.  */
@@ -7513,6 +7613,10 @@ static struct svn_test_descriptor_t test
                        "test rep-sharing on content rather than SHA1"),
     SVN_TEST_OPTS_PASS(closest_copy_test_svn_4677,
                        "test issue SVN-4677 regression"),
+    SVN_TEST_OPTS_PASS(test_closest_copy_file_replaced_with_dir,
+                       "svn_fs_closest_copy after replacing file with dir"),
+    SVN_TEST_OPTS_PASS(test_unrecognized_ioctl,
+                       "test svn_fs_ioctl with unrecognized code"),
     SVN_TEST_NULL
   };
 

Modified: subversion/branches/addremove/subversion/tests/libsvn_fs/locks-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_fs/locks-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_fs/locks-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_fs/locks-test.c Sat 
May 23 14:16:56 2020
@@ -1089,6 +1089,9 @@ lock_cb_error(const svn_test_opts_t *opt
   return SVN_NO_ERROR;
 }
 
+/* XXX NOTE:
+   This test will fail on most Unix-like systems when run as the
+   root user, because flock() will ignore file permissions. */
 static svn_error_t *
 obtain_write_lock_failure(const svn_test_opts_t *opts,
                           apr_pool_t *pool)

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- 
subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c 
(original)
+++ 
subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c 
Sat May 23 14:16:56 2020
@@ -59,7 +59,8 @@ static const char *
 get_rev_contents(svn_revnum_t rev, apr_pool_t *pool)
 {
   /* Toss in a bunch of magic numbers for spice. */
-  apr_int64_t num = ((rev * 1234353 + 4358) * 4583 + ((rev % 4) << 1)) / 42;
+  apr_int64_t rev64 = rev;
+  apr_int64_t num = ((rev64 * 1234353 + 4358) * 4583 + ((rev64 % 4) << 1)) / 
42;
   return apr_psprintf(pool, "%" APR_INT64_T_FMT "\n", num);
 }
 
@@ -1631,8 +1632,8 @@ delta_chain_with_plain(const svn_test_op
   svn_hash_sets(props, "p", svn_string_create(prop_value->data, pool));
 
   hash_rep = svn_stringbuf_create_empty(pool);
-  svn_hash_write2(props, svn_stream_from_stringbuf(hash_rep, pool), "END",
-                  pool);
+  SVN_ERR(svn_hash_write2(props, svn_stream_from_stringbuf(hash_rep, pool),
+                          "END", pool));
 
   SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, pool));
   SVN_ERR(svn_fs_txn_root(&root, txn, pool));
@@ -1693,7 +1694,7 @@ compare_0_length_rep(const svn_test_opts
 
   enum { COUNT = 5 };
   const char *file_names[COUNT] = { no_rep_file,
-                                    empty_plain_file, 
+                                    empty_plain_file,
                                     plain_file,
                                     empty_delta_file,
                                     delta_file };

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- 
subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c
 (original)
+++ 
subversion/branches/addremove/subversion/tests/libsvn_fs_fs/fs-fs-private-test.c
 Sat May 23 14:16:56 2020
@@ -35,6 +35,8 @@
 #include "private/svn_subr_private.h"
 
 #include "../../libsvn_fs_fs/index.h"
+#include "../../libsvn_fs_fs/rep-cache.h"
+#include "../../libsvn_fs/fs-loader.h"
 
 #include "../svn_test_fs.h"
 
@@ -199,8 +201,10 @@ get_repo_stats(const svn_test_opts_t *op
   svn_repos_t *repos;
   svn_revnum_t rev;
   apr_size_t i;
-  svn_fs_fs__stats_t *stats;
   svn_fs_fs__extension_info_t *extension_info;
+  svn_fs_fs__ioctl_get_stats_input_t input = {0};
+  svn_fs_fs__ioctl_get_stats_output_t *output;
+  const svn_fs_fs__stats_t *stats;
 
   /* Bail (with success) on known-untestable scenarios */
   if (strcmp(opts->fs_type, "fsfs") != 0)
@@ -211,8 +215,9 @@ get_repo_stats(const svn_test_opts_t *op
   SVN_ERR(create_greek_repo(&repos, &rev, opts, REPO_NAME, pool, pool));
 
   /* Gather statistics info on that repo. */
-  SVN_ERR(svn_fs_fs__get_stats(&stats, svn_repos_fs(repos), NULL, NULL,
-                               NULL, NULL, pool, pool));
+  SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_GET_STATS,
+                       &input, (void**)&output, NULL, NULL, pool, pool));
+  stats = output->stats;
 
   /* Check that the stats make sense. */
   SVN_TEST_ASSERT(stats->total_size > 1000 && stats->total_size < 10000);
@@ -324,6 +329,7 @@ dump_index(const svn_test_opts_t *opts,
   svn_repos_t *repos;
   svn_revnum_t rev;
   dump_baton_t baton;
+  svn_fs_fs__ioctl_dump_index_input_t input = {0};
 
   /* Bail (with success) on known-untestable scenarios */
   if (strcmp(opts->fs_type, "fsfs") != 0)
@@ -342,8 +348,12 @@ dump_index(const svn_test_opts_t *opts,
   baton.offset = 0;
   baton.revision = rev;
   baton.numbers_seen = svn_bit_array__create(100, pool);
-  SVN_ERR(svn_fs_fs__dump_index(svn_repos_fs(repos), rev, dump_index_entry,
-                                &baton, NULL, NULL, pool));
+
+  input.revision = rev;
+  input.callback_func = dump_index_entry;
+  input.callback_baton = &baton;
+  SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_DUMP_INDEX,
+                       &input, NULL, NULL, NULL, pool, pool));
 
   /* Check that we've got all data (20 noderevs + 20 reps + 1 changes list). */
   SVN_TEST_ASSERT(baton.invocations == 41);
@@ -377,6 +387,8 @@ load_index(const svn_test_opts_t *opts,
   apr_array_header_t *entries = apr_array_make(pool, 41, sizeof(void *));
   apr_array_header_t *alt_entries = apr_array_make(pool, 1, sizeof(void *));
   svn_fs_fs__p2l_entry_t entry;
+  svn_fs_fs__ioctl_dump_index_input_t dump_input = {0};
+  svn_fs_fs__ioctl_load_index_input_t load_input = {0};
 
   /* Bail (with success) on known-untestable scenarios */
   if (strcmp(opts->fs_type, "fsfs") != 0)
@@ -391,8 +403,11 @@ load_index(const svn_test_opts_t *opts,
   SVN_ERR(create_greek_repo(&repos, &rev, opts, REPO_NAME, pool, pool));
 
   /* Read the original index contents for REV in ENTRIES. */
-  SVN_ERR(svn_fs_fs__dump_index(svn_repos_fs(repos), rev, receive_index,
-                                entries, NULL, NULL, pool));
+  dump_input.revision = rev;
+  dump_input.callback_func = receive_index;
+  dump_input.callback_baton = entries;
+  SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_DUMP_INDEX,
+                       &dump_input, NULL, NULL, NULL, pool, pool));
 
   /* Replace it with an index that declares the whole revision contents as
    * "unused". */
@@ -404,14 +419,21 @@ load_index(const svn_test_opts_t *opts,
   entry.item.revision = SVN_INVALID_REVNUM;
   APR_ARRAY_PUSH(alt_entries, svn_fs_fs__p2l_entry_t *) = &entry;
 
-  SVN_ERR(svn_fs_fs__load_index(svn_repos_fs(repos), rev, alt_entries, pool));
+  load_input.revision = rev;
+  load_input.entries = alt_entries;
+  SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_LOAD_INDEX,
+                       &load_input, NULL, NULL, NULL, pool, pool));
+
   SVN_TEST_ASSERT_ERROR(svn_repos_verify_fs3(repos, rev, rev, FALSE, FALSE,
                                              NULL, NULL, NULL, NULL, NULL,
                                              NULL, pool),
                         SVN_ERR_FS_INDEX_CORRUPTION);
 
   /* Restore the original index. */
-  SVN_ERR(svn_fs_fs__load_index(svn_repos_fs(repos), rev, entries, pool));
+  load_input.revision = rev;
+  load_input.entries = entries;
+  SVN_ERR(svn_fs_ioctl(svn_repos_fs(repos), SVN_FS_FS__IOCTL_LOAD_INDEX,
+                       &load_input, NULL, NULL, NULL, pool, pool));
   SVN_ERR(svn_repos_verify_fs3(repos, rev, rev, FALSE, FALSE, NULL, NULL,
                                NULL, NULL, NULL, NULL, pool));
 
@@ -420,6 +442,63 @@ load_index(const svn_test_opts_t *opts,
 
 #undef REPO_NAME
 
+/* ------------------------------------------------------------------------ */
+
+static svn_error_t *
+build_rep_cache(const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  fs_fs_data_t *ffd;
+  svn_fs_txn_t *txn;
+  svn_fs_root_t *txn_root;
+  svn_revnum_t rev;
+  svn_boolean_t exists;
+  const char *fs_path;
+  svn_fs_fs__ioctl_build_rep_cache_input_t input = {0};
+
+  /* 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 rep-sharing");
+
+  /* Create a filesystem and explicitly disable rep-sharing. */
+  fs_path = "test-repo-build-rep-cache-test";
+  SVN_ERR(svn_test__create_fs2(&fs, fs_path, opts, NULL, pool));
+  ffd = fs->fsap_data;
+  ffd->rep_sharing_allowed = FALSE;
+
+  /* Add the Greek tree. */
+  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(svn_fs_commit_txn(NULL, &rev, txn, pool));
+  SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev));
+
+  /* Make sure the rep-cache does not exist. */
+  SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, fs, pool));
+  SVN_TEST_ASSERT(!exists);
+
+  /* Build and verify the rep-cache. */
+  ffd->rep_sharing_allowed = TRUE;
+
+  input.start_rev = rev;
+  input.end_rev = rev;
+  SVN_ERR(svn_fs_ioctl(fs, SVN_FS_FS__IOCTL_BUILD_REP_CACHE,
+                       &input, NULL, NULL, NULL, pool, pool));
+
+  SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, fs, pool));
+  SVN_TEST_ASSERT(exists);
+
+  SVN_ERR(svn_fs_verify(fs_path, NULL, 0, SVN_INVALID_REVNUM,
+                        NULL, NULL, NULL, NULL, pool));
+
+  return SVN_NO_ERROR;
+}
+
 
 
 /* The test table.  */
@@ -435,6 +514,8 @@ static struct svn_test_descriptor_t test
                        "dump the P2L index"),
     SVN_TEST_OPTS_PASS(load_index,
                        "load the P2L index"),
+    SVN_TEST_OPTS_PASS(build_rep_cache,
+                       "build the representation cache"),
     SVN_TEST_NULL
   };
 

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_fs_x/fs-x-pack-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_fs_x/fs-x-pack-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_fs_x/fs-x-pack-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_fs_x/fs-x-pack-test.c 
Sat May 23 14:16:56 2020
@@ -680,7 +680,7 @@ recover_fully_packed(const svn_test_opts
 
 /* ------------------------------------------------------------------------ */
 /* Regression test for issue #4320 (fsfs file-hinting fails when reading a rep
-   from the transaction that is commiting rev = SHARD_SIZE). */
+   from the transaction that is committing rev = SHARD_SIZE). */
 #define REPO_NAME "test-repo-file-hint-at-shard-boundary"
 #define SHARD_SIZE 4
 #define MAX_REV (SHARD_SIZE - 1)

Propchange: subversion/branches/addremove/subversion/tests/libsvn_ra/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Sat May 23 14:16:56 2020
@@ -1,4 +1,13 @@
 *.lo
 .libs
+base_revision_above_youngest
+commit_cb_failure
+commit_empty_last_change
+delete_revision_above_youngest
+errors_from_callbacks
 ra-test
+ra_list_has_props
+ra_revision_errors
+test-get-dir
 test-repo-*
+test-run_checkout

Modified: subversion/branches/addremove/subversion/tests/libsvn_ra/ra-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_ra/ra-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_ra/ra-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_ra/ra-test.c Sat May 
23 14:16:56 2020
@@ -62,7 +62,8 @@ make_and_open_repos(svn_ra_session_t **s
                                   pool, pool));
   SVN_ERR(svn_ra_initialize(pool));
 
-  SVN_ERR(svn_ra_open4(session, NULL, url, NULL, cbtable, NULL, NULL, pool));
+  SVN_ERR(svn_ra_open5(session, NULL, NULL, url, NULL, cbtable, NULL, NULL,
+                       pool));
 
   return SVN_NO_ERROR;
 }
@@ -94,6 +95,41 @@ commit_changes(svn_ra_session_t *session
   return SVN_NO_ERROR;
 }
 
+/* Commit two revisions: add 'B', then delete 'A' */
+static svn_error_t *
+commit_two_changes(svn_ra_session_t *session,
+                   apr_pool_t *pool)
+{
+  apr_hash_t *revprop_table = apr_hash_make(pool);
+  const svn_delta_editor_t *editor;
+  void *edit_baton;
+  void *root_baton, *dir_baton;
+
+  /* mkdir B */
+  SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
+                                    revprop_table,
+                                    NULL, NULL, NULL, TRUE, pool));
+  SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
+                            pool, &root_baton));
+  SVN_ERR(editor->add_directory("B", root_baton, NULL, SVN_INVALID_REVNUM,
+                               pool, &dir_baton));
+  SVN_ERR(editor->close_directory(dir_baton, pool));
+  SVN_ERR(editor->close_directory(root_baton, pool));
+  SVN_ERR(editor->close_edit(edit_baton, pool));
+
+  /* delete A */
+  SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
+                                    revprop_table,
+                                    NULL, NULL, NULL, TRUE, pool));
+  SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
+                            pool, &root_baton));
+  SVN_ERR(editor->delete_entry("A", SVN_INVALID_REVNUM, root_baton, pool));
+  SVN_ERR(editor->close_directory(root_baton, pool));
+  SVN_ERR(editor->close_edit(edit_baton, pool));
+
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 commit_tree(svn_ra_session_t *session,
             apr_pool_t *pool)
@@ -352,7 +388,7 @@ check_tunnel_callback_test(const svn_tes
                                          NULL, NULL, NULL, pool));
 
   b->last_check = TRUE;
-  SVN_TEST_ASSERT_ERROR(svn_ra_open4(&session, NULL,
+  SVN_TEST_ASSERT_ERROR(svn_ra_open5(&session, NULL, NULL,
                                      "svn+foo://localhost/no-repo",
                                      NULL, cbtable, NULL, NULL, pool),
                         SVN_ERR_RA_CANNOT_CREATE_SESSION);
@@ -395,7 +431,7 @@ tunnel_callback_test(const svn_test_opts
                                          NULL, NULL, NULL, pool));
 
   b->last_check = FALSE;
-  SVN_ERR(svn_ra_open4(&session, NULL, url, NULL, cbtable, NULL, NULL,
+  SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, NULL, NULL,
                         scratch_pool));
   SVN_TEST_ASSERT(b->last_check);
   SVN_TEST_ASSERT(b->open_count > 0);
@@ -427,17 +463,7 @@ lock_cb(void *baton,
   struct lock_result_t *result = apr_palloc(b->pool,
                                             sizeof(struct lock_result_t));
 
-  if (lock)
-    {
-      result->lock = apr_palloc(b->pool, sizeof(svn_lock_t));
-      *result->lock = *lock;
-      result->lock->path = apr_pstrdup(b->pool, lock->path);
-      result->lock->token = apr_pstrdup(b->pool, lock->token);
-      result->lock->owner = apr_pstrdup(b->pool, lock->owner);
-      result->lock->comment = apr_pstrdup(b->pool, lock->comment);
-    }
-  else
-    result->lock = NULL;
+  result->lock = svn_lock_dup(lock, b->pool);
   result->err = ra_err;
 
   svn_hash_sets(b->results, apr_pstrdup(b->pool, path), result);
@@ -1566,7 +1592,7 @@ tunnel_run_checkout(const svn_test_opts_
 
   b->last_check = FALSE;
 
-  SVN_ERR(svn_ra_open4(&session, NULL, url, NULL, cbtable, NULL, NULL,
+  SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, NULL, NULL,
                        scratch_pool));
 
   SVN_ERR(commit_changes(session, pool));
@@ -1636,16 +1662,16 @@ commit_empty_last_change(const svn_test_
       SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
                                         revprop_table,
                                         NULL, NULL, NULL, TRUE, tmp_pool));
-      
+
       SVN_ERR(editor->open_root(edit_baton, 1, tmp_pool, &root_baton));
       SVN_ERR(editor->close_directory(root_baton, tmp_pool));
       SVN_ERR(editor->close_edit(edit_baton, tmp_pool));
-      
+
       SVN_ERR(svn_ra_stat(session, "", 2+i, &dirent, tmp_pool));
-      
+
       SVN_TEST_ASSERT(dirent != NULL);
       SVN_TEST_STRING_ASSERT(dirent->last_author, "jrandom");
-      
+
       /* BDB used to only updates last_changed on the repos_root when there
          was an actual change. Now all filesystems behave in the same way */
       SVN_TEST_INT_ASSERT(dirent->created_rev, 2+i);
@@ -1682,6 +1708,175 @@ commit_empty_last_change(const svn_test_
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+commit_locked_file(const svn_test_opts_t *opts, apr_pool_t *pool)
+{
+  const char *url;
+  svn_ra_callbacks2_t *cbtable;
+  svn_ra_session_t *session;
+  const svn_delta_editor_t *editor;
+  void *edit_baton;
+  void *root_baton;
+  void *file_baton;
+  struct lock_result_t *lock_result;
+  apr_hash_t *lock_tokens;
+  svn_txdelta_window_handler_t handler;
+  void *handler_baton;
+  svn_revnum_t fetched_rev;
+  apr_hash_t *fetched_props;
+  const svn_string_t *propval;
+
+  SVN_ERR(svn_test__create_repos2(NULL, &url, NULL,
+                                  "test-repo-commit-locked-file-test",
+                                  opts, pool, pool));
+
+  SVN_ERR(svn_ra_initialize(pool));
+  SVN_ERR(svn_ra_create_callbacks(&cbtable, pool));
+  SVN_ERR(svn_test__init_auth_baton(&cbtable->auth_baton, pool));
+
+  SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable,
+                       NULL, NULL, pool));
+  SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
+                                    apr_hash_make(pool),
+                                    NULL, NULL, NULL, TRUE, pool));
+  /* Add a file. */
+  SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
+                            pool, &root_baton));
+  SVN_ERR(editor->add_file("file", root_baton, NULL, SVN_INVALID_REVNUM,
+                           pool, &file_baton));
+  SVN_ERR(editor->close_file(file_baton, NULL, pool));
+  SVN_ERR(editor->close_directory(root_baton, pool));
+  SVN_ERR(editor->close_edit(edit_baton, pool));
+
+  /* Acquire a lock on this file. */
+  {
+    struct lock_baton_t baton = {0};
+    svn_revnum_t rev = 1;
+    apr_hash_t *lock_targets;
+
+    baton.results = apr_hash_make(pool);
+    baton.pool = pool;
+
+    lock_targets = apr_hash_make(pool);
+    svn_hash_sets(lock_targets, "file", &rev);
+    SVN_ERR(svn_ra_lock(session, lock_targets, "comment", FALSE,
+                        lock_cb, &baton, pool));
+
+    SVN_ERR(expect_lock("file", baton.results, session, pool));
+    lock_result = svn_hash_gets(baton.results, "file");
+  }
+
+  /* Open a new session using the file parent's URL. */
+  SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable,
+                       NULL, NULL, pool));
+
+  /* Create a new commit editor supplying our lock token. */
+  lock_tokens = apr_hash_make(pool);
+  svn_hash_sets(lock_tokens, "file", lock_result->lock->token);
+  SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
+                                    apr_hash_make(pool), NULL, NULL,
+                                    lock_tokens, TRUE, pool));
+  /* Edit the locked file. */
+  SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
+                            pool, &root_baton));
+  SVN_ERR(editor->open_file("file", root_baton, SVN_INVALID_REVNUM, pool,
+                            &file_baton));
+  SVN_ERR(editor->apply_textdelta(file_baton, NULL, pool, &handler,
+                                  &handler_baton));
+  SVN_ERR(svn_txdelta_send_string(svn_string_create("A", pool),
+                                  handler, handler_baton, pool));
+  SVN_ERR(editor->close_file(file_baton, NULL, pool));
+  SVN_ERR(editor->close_directory(root_baton, pool));
+  SVN_ERR(editor->close_edit(edit_baton, pool));
+
+  /* Check the result. */
+  SVN_ERR(svn_ra_get_file(session, "file", SVN_INVALID_REVNUM, NULL,
+                          &fetched_rev, NULL, pool));
+  SVN_TEST_INT_ASSERT((int) fetched_rev, 2);
+
+  /* Change property of the locked file. */
+  SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
+                                    apr_hash_make(pool), NULL, NULL,
+                                    lock_tokens, TRUE, pool));
+  SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
+                            pool, &root_baton));
+  SVN_ERR(editor->open_file("file", root_baton, SVN_INVALID_REVNUM, pool,
+                            &file_baton));
+  SVN_ERR(editor->change_file_prop(file_baton, "propname",
+                                   svn_string_create("propval", pool),
+                                   pool));
+  SVN_ERR(editor->close_file(file_baton, NULL, pool));
+  SVN_ERR(editor->close_directory(root_baton, pool));
+  SVN_ERR(editor->close_edit(edit_baton, pool));
+
+  /* Check the result. */
+  SVN_ERR(svn_ra_get_file(session, "file", SVN_INVALID_REVNUM, NULL,
+                          &fetched_rev, &fetched_props, pool));
+  SVN_TEST_INT_ASSERT((int) fetched_rev, 3);
+  propval = svn_hash_gets(fetched_props, "propname");
+  SVN_TEST_ASSERT(propval);
+  SVN_TEST_STRING_ASSERT(propval->data, "propval");
+
+  return SVN_NO_ERROR;
+}
+
+/* Cases of 'get-deleted-rev' that should return SVN_INVALID_REVNUM. */
+static svn_error_t *
+test_get_deleted_rev_no_delete(const svn_test_opts_t *opts,
+                               apr_pool_t *pool)
+{
+  svn_ra_session_t *ra_session;
+  svn_revnum_t revision_deleted;
+
+  SVN_ERR(make_and_open_repos(&ra_session,
+                              "test-repo-get-deleted-rev-no-delete", opts,
+                              pool));
+  SVN_ERR(commit_changes(ra_session, pool));
+  SVN_ERR(commit_two_changes(ra_session, pool));
+
+  /* expect 'no deletion' in the range up to r2, when it is deleted in r3 */
+  /* This was failing over RA-SVN where the 'get-deleted-rev' wire command's
+     prototype cannot directly represent that result. A new enough client and
+     server collaborate on a work-around implemented using an error code. */
+  SVN_ERR(svn_ra_get_deleted_rev(ra_session, "A", 1, 2,
+                                 &revision_deleted, pool));
+  SVN_TEST_INT_ASSERT(revision_deleted, SVN_INVALID_REVNUM);
+
+  /* this connection should still be open: a simple case should still work */
+  SVN_ERR(svn_ra_get_deleted_rev(ra_session, "A", 1, 3,
+                                 &revision_deleted, pool));
+  SVN_TEST_INT_ASSERT(revision_deleted, 3);
+
+  return SVN_NO_ERROR;
+}
+
+/* Cases of 'get-deleted-rev' that should return an error. */
+static svn_error_t *
+test_get_deleted_rev_errors(const svn_test_opts_t *opts,
+                               apr_pool_t *pool)
+{
+  svn_ra_session_t *ra_session;
+  svn_revnum_t revision_deleted;
+  svn_error_t *err;
+
+  SVN_ERR(make_and_open_repos(&ra_session,
+                              "test-repo-get-deleted-rev-errors", opts, pool));
+  SVN_ERR(commit_changes(ra_session, pool));
+
+  /* expect an error when searching up to r3, when repository head is r1 */
+  err = svn_ra_get_deleted_rev(ra_session, "A", 1, 3, &revision_deleted, pool);
+
+  /* mod_dav_svn returns a generic error code for "500 Internal Server Error";
+   * the other RA layers return the specific error code for "no such revision".
+   * We should make these consistent, but for now that's how it is. */
+  if (opts->repos_url && strncmp(opts->repos_url, "http", 4) == 0)
+    SVN_TEST_ASSERT_ERROR(err, SVN_ERR_RA_DAV_REQUEST_FAILED);
+  else
+    SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_NO_SUCH_REVISION);
+
+  return SVN_NO_ERROR;
+}
+
 
 /* The test table.  */
 
@@ -1716,6 +1911,12 @@ static struct svn_test_descriptor_t test
                        "verify checkout over a tunnel"),
     SVN_TEST_OPTS_PASS(commit_empty_last_change,
                        "check how last change applies to empty commit"),
+    SVN_TEST_OPTS_PASS(commit_locked_file,
+                       "check commit editor for a locked file"),
+    SVN_TEST_OPTS_PASS(test_get_deleted_rev_no_delete,
+                       "test get-deleted-rev no delete"),
+    SVN_TEST_OPTS_PASS(test_get_deleted_rev_errors,
+                       "test get-deleted-rev errors"),
     SVN_TEST_NULL
   };
 

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_repos/authz-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_repos/authz-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_repos/authz-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_repos/authz-test.c 
Sat May 23 14:16:56 2020
@@ -212,7 +212,7 @@ test_authz_parse(const svn_test_opts_t *
                            APR_READ, APR_OS_DEFAULT,
                            pool));
   groups = svn_stream_from_aprfile2(groups_file, FALSE, pool);
-  SVN_ERR(svn_authz__parse(&authz, rules, groups, pool, pool));
+  SVN_ERR(svn_authz__parse(&authz, rules, groups, NULL, NULL, pool, pool));
 
   printf("Access check for ('%s', '%s')\n", check_user, check_repo);
 
@@ -277,9 +277,9 @@ test_authz_parse(const svn_test_opts_t *
 
   printf("[users]\n");
   if (authz->has_anon_rights)
-    print_user_rights(NULL, NULL, 0, &authz->anon_rights, pool);
+    SVN_ERR(print_user_rights(NULL, NULL, 0, &authz->anon_rights, pool));
   if (authz->has_authn_rights)
-    print_user_rights(NULL, NULL, 0, &authz->authn_rights, pool);
+    SVN_ERR(print_user_rights(NULL, NULL, 0, &authz->authn_rights, pool));
   SVN_ERR(svn_iter_apr_hash(NULL, authz->user_rights,
                             print_user_rights, NULL, pool));
   printf("\n\n");
@@ -304,7 +304,7 @@ run_global_rights_tests(const char *cont
 
   svn_stringbuf_t *buffer = svn_stringbuf_create(contents, pool);
   svn_stream_t *stream = svn_stream_from_stringbuf(buffer, pool);
-  SVN_ERR(svn_repos_authz_parse(&authz, stream, NULL, pool));
+  SVN_ERR(svn_repos_authz_parse2(&authz, stream, NULL, NULL, NULL, pool, 
pool));
 
   for (; test_cases->repos; ++test_cases)
     {
@@ -444,6 +444,73 @@ test_global_rights(apr_pool_t *pool)
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+issue_4741_groups(apr_pool_t *pool)
+{
+   const char rules[] =
+     "[groups]"      NL
+     "g1 = userA"    NL
+     "g2 = userB"    NL
+     "g = @g1, @g2"  NL
+     ""              NL
+     "[/]"           NL
+     "* ="           NL
+     "@g = rw"       NL
+     ;
+
+   svn_stringbuf_t *buf = svn_stringbuf_create(rules, pool);
+   svn_stream_t *stream = svn_stream_from_stringbuf(buf, pool);
+   svn_authz_t *authz;
+   svn_boolean_t access_granted;
+
+   SVN_ERR(svn_repos_authz_parse2(&authz, stream, NULL, NULL, NULL, pool, 
pool));
+
+   SVN_ERR(svn_repos_authz_check_access(authz, "repo", "/", "userA",
+                                        svn_authz_write, &access_granted,
+                                        pool));
+   SVN_TEST_ASSERT(access_granted == TRUE);
+
+   SVN_ERR(svn_repos_authz_check_access(authz, "repo", "/", "userB",
+                                        svn_authz_write, &access_granted,
+                                        pool));
+   SVN_TEST_ASSERT(access_granted == TRUE);
+
+   return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reposful_reposless_stanzas_inherit(apr_pool_t *pool)
+{
+  const char rules[] =
+    "[groups]"                               NL
+    "company = user1, user2, user3"          NL
+    "customer = customer1, customer2"        NL
+    ""                                       NL
+    "# company can read-write on everything" NL
+    "[/]"                                    NL
+    "@company = rw"                          NL
+    ""                                       NL
+    "[project1:/]"                           NL
+    "@customer = r"                          NL
+    ""                                       NL
+    "[project2:/]"                           NL;
+
+   svn_stringbuf_t *buf = svn_stringbuf_create(rules, pool);
+   svn_stream_t *stream = svn_stream_from_stringbuf(buf, pool);
+   svn_authz_t *authz;
+   svn_boolean_t access_granted;
+
+   SVN_ERR(svn_repos_authz_parse2(&authz, stream, NULL, NULL, NULL, pool, 
pool));
+
+   SVN_ERR(svn_repos_authz_check_access(authz, "project1", "/foo", "user1",
+                                        svn_authz_write | svn_authz_recursive,
+                                        &access_granted,
+                                        pool));
+   SVN_TEST_ASSERT(access_granted == TRUE);
+
+   return SVN_NO_ERROR;
+}
+
 static int max_threads = 4;
 
 static struct svn_test_descriptor_t test_funcs[] =
@@ -453,6 +520,10 @@ static struct svn_test_descriptor_t test
                        "test svn_authz__parse"),
     SVN_TEST_PASS2(test_global_rights,
                    "test svn_authz__get_global_rights"),
+    SVN_TEST_PASS2(issue_4741_groups,
+                   "issue 4741 groups"),
+    SVN_TEST_XFAIL2(reposful_reposless_stanzas_inherit,
+                    "[foo:/] inherits [/]"),
     SVN_TEST_NULL
   };
 

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_repos/dump-load-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_repos/dump-load-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- 
subversion/branches/addremove/subversion/tests/libsvn_repos/dump-load-test.c 
(original)
+++ 
subversion/branches/addremove/subversion/tests/libsvn_repos/dump-load-test.c 
Sat May 23 14:16:56 2020
@@ -81,7 +81,7 @@ test_dump_bad_props(svn_stringbuf_t **du
                              notify_func, notify_baton,
                              NULL, NULL, NULL, NULL,
                              pool));
-  svn_stream_close(stream);
+  SVN_ERR(svn_stream_close(stream));
 
   /* Check that the property appears in the dump data */
   expected_str = apr_psprintf(pool, "K %d\n%s\n"
@@ -131,7 +131,7 @@ test_load_bad_props(svn_stringbuf_t *dum
                              notify_func, notify_baton,
                              NULL, NULL, /*cancellation*/
                              pool));
-  svn_stream_close(stream);
+  SVN_ERR(svn_stream_close(stream));
 
   /* Check the loaded property */
   fs = svn_repos_fs(repos);

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_repos/repos-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_repos/repos-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_repos/repos-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_repos/repos-test.c 
Sat May 23 14:16:56 2020
@@ -4154,7 +4154,7 @@ mkdir_delete_copy(svn_repos_t *repos,
   svn_fs_root_t *txn_root, *rev_root;
 
   SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool));
-  
+
   SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool));
   SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
   SVN_ERR(svn_fs_make_dir(txn_root, "A/T", pool));
@@ -4461,7 +4461,7 @@ test_list(const svn_test_opts_t *opts,
   SVN_ERR(svn_repos_list(rev_root, "/A", patterns, svn_depth_infinity, FALSE,
                          NULL, NULL, list_callback, &counter, NULL, NULL,
                          pool));
-  SVN_TEST_ASSERT(counter == 6);
+  SVN_TEST_ASSERT(counter == 7);
 
   return SVN_NO_ERROR;
 }

Propchange: subversion/branches/addremove/subversion/tests/libsvn_subr/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Sat May 23 14:16:56 2020
@@ -56,3 +56,7 @@ sqlite-test-*
 x509-test
 xml-test
 test_apr_trunc_workaround
+save-cleartext
+test_stream_readline_file_crlf
+test_stream_readline_file_lf
+test_stream_readline_file_nul

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.c 
Sat May 23 14:16:56 2020
@@ -70,12 +70,12 @@ get_config_file_path(const char **cfg_fi
 }
 
 static const char *config_keys[] = { "foo", "a", "b", "c", "d", "e", "f", "g",
-                                     "h", "i", NULL };
+                                     "h", "i", "m", NULL };
 static const char *config_values[] = { "bar", "Aa", "100", "bar",
                                        "a %(bogus)s oyster bar",
                                        "%(bogus)s shmoo %(",
                                        "%Aa", "lyrical bard", "%(unterminated",
-                                       "Aa 100", NULL };
+                                       "Aa 100", "foo bar baz", NULL };
 
 static svn_error_t *
 test_text_retrieval(const svn_test_opts_t *opts,

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.cfg
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.cfg?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.cfg 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_subr/config-test.cfg 
Sat May 23 14:16:56 2020
@@ -45,6 +45,10 @@ j=some %(k)scle
 k=c%(j)sy
 # Depends on a cyclic definition
 l=depends on a %(j)scycle!
+# line continuation
+m = foo
+ bar
+  baz
 
 [UpperCaseSection]
 a=Aa

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_subr/dirent_uri-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/dirent_uri-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- 
subversion/branches/addremove/subversion/tests/libsvn_subr/dirent_uri-test.c 
(original)
+++ 
subversion/branches/addremove/subversion/tests/libsvn_subr/dirent_uri-test.c 
Sat May 23 14:16:56 2020
@@ -36,6 +36,7 @@
 
 #include "svn_pools.h"
 #include "svn_dirent_uri.h"
+#include "private/svn_dirent_uri_private.h"
 #include "private/svn_fspath.h"
 #include "private/svn_cert.h"
 
@@ -2331,11 +2332,12 @@ test_relpath_internal_style(apr_pool_t *
 
   for (i = 0; i < COUNT_OF(tests); i++)
     {
-      const char *internal = svn_relpath__internal_style(tests[i].path, pool);
+      const char *internal;
+      SVN_ERR(svn_relpath__make_internal(&internal, tests[i].path, pool, 
pool));
 
       if (strcmp(internal, tests[i].result))
         return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
-                                 "svn_relpath__internal_style(\"%s\") returned 
"
+                                 "svn_relpath__make_internal(\"%s\") returned "
                                  "\"%s\" expected \"%s\"",
                                  tests[i].path, internal, tests[i].result);
     }

Modified: subversion/branches/addremove/subversion/tests/libsvn_subr/io-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/io-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_subr/io-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_subr/io-test.c Sat 
May 23 14:16:56 2020
@@ -211,6 +211,38 @@ create_comparison_candidates(struct test
   return err;
 }
 
+/* Create an on-disk tree with optional read-only attributes on some
+   files and/or directories. */
+static svn_error_t *
+create_dir_tree(const char **dir_path,
+                const char *testname,
+                svn_boolean_t dir_readonly,
+                svn_boolean_t file_readonly,
+                apr_pool_t *pool)
+{
+  const char *test_dir_path;
+  const char *sub_dir_path;
+  const char *file_path;
+
+  SVN_ERR(svn_test_make_sandbox_dir(&test_dir_path, testname, pool));
+
+  sub_dir_path = svn_dirent_join(test_dir_path, "dir", pool);
+  SVN_ERR(svn_io_dir_make(sub_dir_path, APR_OS_DEFAULT, pool));
+
+  file_path = svn_dirent_join(sub_dir_path, "file", pool);
+  SVN_ERR(svn_io_file_create_empty(file_path, pool));
+
+  if (file_readonly)
+    SVN_ERR(svn_io_set_file_read_only(file_path, FALSE, pool));
+
+  if (dir_readonly)
+    SVN_ERR(svn_io_set_file_read_only(sub_dir_path, FALSE, pool));
+
+  *dir_path = sub_dir_path;
+  return SVN_NO_ERROR;
+}
+
+
 
 /* Functions to check the 2-way and 3-way file comparison functions.  */
 
@@ -1144,9 +1176,59 @@ test_apr_trunc_workaround(apr_pool_t *po
   SVN_ERR(svn_io_file_seek(f, APR_CUR, &offset, pool));
   SVN_TEST_ASSERT(offset == (int)len);
 
-  return SVN_NO_ERROR;  
+  return SVN_NO_ERROR;
+}
+
+
+/* Issue #4806 */
+static svn_error_t *
+test_rmtree_all_writable(apr_pool_t *pool)
+{
+  const char *dir_path = NULL;
+
+  SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_all_writable",
+                          FALSE, FALSE, pool));
+  SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool));
+  return SVN_NO_ERROR;
+}
+
+/* Issue #4806 */
+static svn_error_t *
+test_rmtree_file_readonly(apr_pool_t *pool)
+{
+  const char *dir_path = NULL;
+
+  SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_file_readonly",
+                          FALSE, TRUE, pool));
+  SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool));
+  return SVN_NO_ERROR;
+}
+
+/* Issue #4806 */
+static svn_error_t *
+test_rmtree_dir_readonly(apr_pool_t *pool)
+{
+  const char *dir_path = NULL;
+
+  SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_dir_readonly",
+                          TRUE, FALSE, pool));
+  SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool));
+  return SVN_NO_ERROR;
+}
+
+/* Issue #4806 */
+static svn_error_t *
+test_rmtree_all_readonly(apr_pool_t *pool)
+{
+  const char *dir_path = NULL;
+
+  SVN_ERR(create_dir_tree(&dir_path, "test_rmtree_all_readonly",
+                          TRUE, TRUE, pool));
+  SVN_ERR(svn_io_remove_dir2(dir_path, FALSE, NULL, NULL, pool));
+  return SVN_NO_ERROR;
 }
 
+
 /* The test table.  */
 
 static int max_threads = 3;
@@ -1184,6 +1266,14 @@ static struct svn_test_descriptor_t test
                    "test svn_io_open_uniquely_named()"),
     SVN_TEST_PASS2(test_apr_trunc_workaround,
                    "test workaround for APR in svn_io_file_trunc"),
+    SVN_TEST_PASS2(test_rmtree_all_writable,
+                   "test svn_io_remove_dir2() with writable tree"),
+    SVN_TEST_PASS2(test_rmtree_file_readonly,
+                   "test svn_io_remove_dir2() with read-only file"),
+    SVN_TEST_PASS2(test_rmtree_dir_readonly,
+                   "test svn_io_remove_dir2() with read-only directory"),
+    SVN_TEST_PASS2(test_rmtree_all_readonly,
+                   "test svn_io_remove_dir2() with read-only tree"),
     SVN_TEST_NULL
   };
 

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_subr/mergeinfo-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/mergeinfo-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_subr/mergeinfo-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_subr/mergeinfo-test.c 
Sat May 23 14:16:56 2020
@@ -33,6 +33,8 @@
 #include "svn_types.h"
 #include "svn_mergeinfo.h"
 #include "private/svn_mergeinfo_private.h"
+#include "private/svn_sorts_private.h"
+#include "private/svn_error_private.h"
 #include "../svn_test.h"
 
 /* A quick way to create error messages.  */
@@ -1673,100 +1675,27 @@ test_remove_prefix_from_catalog(apr_pool
 static svn_error_t *
 test_rangelist_merge_overlap(apr_pool_t *pool)
 {
-  svn_rangelist_t * changes;
-  /* 
15014-19472,19473-19612*,19613-19614,19615-19630*,19631-19634,19635-20055* */
-  svn_rangelist_t * rangelist = apr_array_make(pool, 1, 
sizeof(svn_merge_range_t *));
-  svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
-
-  /* This range is optional for reproducing issue #4686 */
-  mrange->start = 15013;
-  mrange->end = 19472;
-  mrange->inheritable = TRUE;
-  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
-
-  mrange = apr_pcalloc(pool, sizeof(*mrange));
-  mrange->start = 19472;
-  mrange->end = 19612;
-  mrange->inheritable = FALSE;
-  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
-
-  /* This range is optional for reproducing issue #4686 */
-  mrange = apr_pcalloc(pool, sizeof(*mrange));
-  mrange->start = 19612;
-  mrange->end = 19614;
-  mrange->inheritable = TRUE;
-  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
-
-  mrange = apr_pcalloc(pool, sizeof(*mrange));
-  mrange->start = 19614;
-  mrange->end = 19630;
-  mrange->inheritable = FALSE;
-  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
-
-  mrange = apr_pcalloc(pool, sizeof(*mrange));
-  mrange->start = 19630;
-  mrange->end = 19634;
-  mrange->inheritable = TRUE;
-  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
-
-  /* This range is optional for reproducing issue #4686 */
-  mrange = apr_pcalloc(pool, sizeof(*mrange));
-  mrange->start = 19634;
-  mrange->end = 20055;
-  mrange->inheritable = FALSE;
-  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
-
-  /* 15014-20515* */
-  changes = apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
-  mrange = apr_pcalloc(pool, sizeof(*mrange));
-  mrange->start = 15013;
-  mrange->end = 20515;
-  mrange->inheritable = FALSE;
-  APR_ARRAY_PUSH(changes, svn_merge_range_t *) = mrange;
-#if 0
-  {
-    svn_string_t * tmpString;
-
-    svn_rangelist_to_string(&tmpString, rangelist, pool);
-    printf("rangelist %s\n", tmpString->data);
-  }
-  {
-    svn_string_t * tmpString;
-
-    svn_rangelist_to_string(&tmpString, changes, pool);
-    printf("changes %s\n", tmpString->data);
-  }
-#endif
-
+  const char *rangelist_str = "19473-19612*,19615-19630*,19631-19634";
+  const char *changes_str = "15014-20515*";
+  const char *expected_str = "15014-19630*,19631-19634,19635-20515*";
+  /* wrong result: "15014-19630*,19634-19631*,19631-19634,19635-20515*" */
+  svn_rangelist_t *rangelist, *changes;
+  svn_string_t *result_string;
+
+  /* prepare the inputs */
+  SVN_ERR(svn_rangelist__parse(&rangelist, rangelist_str, pool));
+  SVN_ERR(svn_rangelist__parse(&changes, changes_str, pool));
   SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist));
   SVN_TEST_ASSERT(svn_rangelist__is_canonical(changes));
 
+  /* perform the merge */
   SVN_ERR(svn_rangelist_merge2(rangelist, changes, pool, pool));
 
+  /* check the output */
   SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist));
+  SVN_ERR(svn_rangelist_to_string(&result_string, rangelist, pool));
+  SVN_TEST_STRING_ASSERT(result_string->data, expected_str);
 
-#if 0
-  {
-    svn_string_t * tmpString;
-
-    svn_rangelist_to_string(&tmpString, rangelist, pool);
-    printf("result %s\n", tmpString->data);
-  }
-#endif
-
-  /* wrong result
-    result 
15014-19472,19473-19612*,19613-19614,19615-19630*,19634-19631*,19631-19634,19635-20515*
-  */
-
-  {
-     svn_string_t * tmp_string;
-     svn_rangelist_t *range_list;
-
-     svn_rangelist_to_string(&tmp_string, rangelist, pool);
-
-     SVN_ERR(svn_rangelist__parse(&range_list, tmp_string->data, pool));
-  }
-  
   return SVN_NO_ERROR;
 }
 
@@ -1856,6 +1785,701 @@ test_rangelist_loop(apr_pool_t *pool)
 
   return SVN_NO_ERROR;
 }
+
+/* A specific case where result was non-canonical, around svn 1.10 ~ 1.13. */
+static svn_error_t *
+test_rangelist_merge_canonical_result(apr_pool_t *pool)
+{
+  const char *rangelist_str = "8-10";
+  const char *changes_str = "5-10*,11-24";
+  const char *expected_str = "5-7*,8-24";
+  /* wrong result: "5-7*,8-10,11-24" */
+  svn_rangelist_t *rangelist, *changes;
+  svn_string_t *result_string;
+
+  /* prepare the inputs */
+  SVN_ERR(svn_rangelist__parse(&rangelist, rangelist_str, pool));
+  SVN_ERR(svn_rangelist__parse(&changes, changes_str, pool));
+  SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist));
+  SVN_TEST_ASSERT(svn_rangelist__is_canonical(changes));
+
+  /* perform the merge */
+  SVN_ERR(svn_rangelist_merge2(rangelist, changes, pool, pool));
+
+  /* check the output */
+  SVN_TEST_ASSERT(svn_rangelist__is_canonical(rangelist));
+  SVN_ERR(svn_rangelist_to_string(&result_string, rangelist, pool));
+  SVN_TEST_STRING_ASSERT(result_string->data, expected_str);
+
+  return SVN_NO_ERROR;
+}
+
+/* Test svn_rangelist_merge2() with specific inputs derived from an
+ * occurrence of issue #4840 "in the wild", that triggered a hard
+ * assertion failure (abort) in a 1.10.6 release-mode build.
+ */
+static svn_error_t *
+test_rangelist_merge_array_insert_failure(apr_pool_t *pool)
+{
+  svn_rangelist_t *rx, *ry;
+  svn_string_t *rxs;
+
+  /* Simplified case with same failure mode as reported case: error
+   * "E200004: svn_sort__array_insert2:
+   *  Attempted insert at index 4 in array length 3" */
+  SVN_ERR(svn_rangelist__parse(&rx, "2*,4*,6*,8", pool));
+  SVN_ERR(svn_rangelist__parse(&ry, "1-9*,11", pool));
+  SVN_ERR(svn_rangelist_merge2(rx, ry, pool, pool));
+  SVN_ERR(svn_rangelist_to_string(&rxs, rx, pool));
+  SVN_TEST_STRING_ASSERT(rxs->data, "1-7*,8,9*,11");
+
+  /* Actual reported case: in v1.10.6, aborted; after r1872118, error
+   * "E200004: svn_sort__array_insert2:
+   *  Attempted insert at index 57 in array length 55".  The actual "index"
+   *  and "array length" numbers vary with changes such as r1823728. */
+  SVN_ERR(svn_rangelist__parse(&rx, 
"997347-997597*,997884-1000223*,1000542-1000551*,1001389-1001516,1002139-1002268*,1002896-1003064*,1003320-1003468,1005939-1006089*,1006443-1006630*,1006631-1006857,1007028-1007116*,1009467-1009629,1009630-1010007*,1010774-1010860,1011036-1011502,1011672-1014004*,1014023-1014197,1014484-1014542*,1015077-1015568,1016219-1016365,1016698-1016845,1017331-1018616,1027032-1027180,1027855-1028051,1028261-1028395,1028553-1028663,1028674-1028708,1028773-1028891*,1029223-1030557,1032239-1032284*,1032801-1032959,1032960-1033074*,1033745-1033810,1034990-1035104,1035435-1036108*,1036109-1036395,1036396-1036865*,1036866-1036951,1036952-1037647*,1037648-1037750,1037751-1038548*,1038549-1038700,1038701-1042103*,1042104-1042305,1042306-1046626*,1046627-1046910,1046911-1047676*,1047677-1047818,1047819-1047914*,1047915-1048025,1048026-1048616*,1048617-1048993,1048994-1050066*,1054605-1054739,1054854-1055021",
 pool));
+  SVN_ERR(svn_rangelist__parse(&ry, "1035435-1050066*,1052459-1054617", pool));
+  SVN_ERR(svn_rangelist_merge2(rx, ry, pool, pool));
+  /* Here we don't care to check the result; just that it returns "success". */
+  return SVN_NO_ERROR;
+}
+
+
+/* Random testing parameters and coverage
+ *
+ * The parameters for testing random inputs, in conjunction with the
+ * specific test case generation code, were adjusted so as to observe the
+ * tests generating each of the known failure modes.  The aim is also to
+ * have sufficient coverage of inputs to discover other failure modes in
+ * future if the code is changed.
+ *
+ * There are neither theoretic nor empirical guarantees on the coverage.
+ */
+
+/* Randomize revision numbers over this small range.
+ * (With a larger range, we would find edge cases more rarely.)
+ * See comment "Random testing parameters and coverage" */
+#define RANGELIST_TESTS_MAX_REV 15
+
+/* A representation of svn_rangelist_t in which
+ *   root[R]    := (revision R is in the rangelist)
+ *   inherit[R] := (revision R is in the rangelist and inheritable)
+ *
+ * Assuming all forward ranges.
+ */
+typedef struct rl_array_t {
+    svn_boolean_t root[RANGELIST_TESTS_MAX_REV + 1];
+    svn_boolean_t inherit[RANGELIST_TESTS_MAX_REV + 1];
+} rl_array_t;
+
+static void
+rangelist_to_array(rl_array_t *a,
+                   const svn_rangelist_t *rl)
+{
+  int i;
+
+  memset(a, 0, sizeof(*a));
+  for (i = 0; i < rl->nelts; i++)
+    {
+      svn_merge_range_t *range = APR_ARRAY_IDX(rl, i, svn_merge_range_t *);
+      svn_revnum_t r;
+
+      for (r = range->start + 1; r <= range->end; r++)
+        {
+          a->root[r] = TRUE;
+          a->inherit[r] = range->inheritable;
+        }
+    }
+}
+
+/* Compute the union of two rangelists arrays.
+ * Let MA := union(BA, CA)
+ */
+static void
+rangelist_array_union(rl_array_t *ma,
+                      const rl_array_t *ba,
+                      const rl_array_t *ca)
+{
+  svn_revnum_t r;
+
+  for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++)
+    {
+      ma->root[r]    = ba->root[r]    || ca->root[r];
+      ma->inherit[r] = ba->inherit[r] || ca->inherit[r];
+    }
+}
+
+/* Return TRUE iff two rangelist arrays are equal.
+ */
+static svn_boolean_t
+rangelist_array_equal(const rl_array_t *ba,
+                      const rl_array_t *ca)
+{
+  svn_revnum_t r;
+
+  for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++)
+    {
+      if (ba->root[r]    != ca->root[r]
+       || ba->inherit[r] != ca->inherit[r])
+        {
+          return FALSE;
+        }
+    }
+  return TRUE;
+}
+
+#define IS_VALID_FORWARD_RANGE(range) \
+  (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end))
+
+/* Check rangelist is sorted and contains forward ranges. */
+static svn_boolean_t
+rangelist_is_sorted(const svn_rangelist_t *rangelist)
+{
+  int i;
+
+  for (i = 0; i < rangelist->nelts; i++)
+    {
+      const svn_merge_range_t *thisrange
+        = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+
+      if (!IS_VALID_FORWARD_RANGE(thisrange))
+        return FALSE;
+    }
+  for (i = 1; i < rangelist->nelts; i++)
+    {
+      const svn_merge_range_t *lastrange
+        = APR_ARRAY_IDX(rangelist, i-1, svn_merge_range_t *);
+      const svn_merge_range_t *thisrange
+        = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+
+      if (svn_sort_compare_ranges(&lastrange, &thisrange) > 0)
+        return FALSE;
+    }
+  return TRUE;
+}
+
+/* Return a random number R, where 0 <= R < N.
+ */
+static int rand_less_than(int n, apr_uint32_t *seed)
+{
+  apr_uint32_t next = svn_test_rand(seed);
+  return ((apr_uint64_t)next * n) >> 32;
+}
+
+/* Return a random integer in a triangular (centre-weighted) distribution in
+ * the inclusive interval [MIN, MAX]. */
+static int
+rand_interval_triangular(int min, int max, apr_uint32_t *seed)
+{
+  int span = max - min + 1;
+
+  return min + rand_less_than(span/2 + 1, seed)
+             + rand_less_than((span+1)/2, seed);
+}
+
+/* Generate a rangelist with a random number of random ranges.
+ * Choose from 0 to NON_V_MAX_RANGES ranges, biased towards the middle.
+ */
+#define NON_V_MAX_RANGES 4  /* See "Random testing parameters and coverage" */
+static void
+rangelist_random_non_validated(svn_rangelist_t **rl,
+                               apr_uint32_t *seed,
+                               apr_pool_t *pool)
+{
+  svn_rangelist_t *r = apr_array_make(pool, NON_V_MAX_RANGES,
+                                      sizeof(svn_merge_range_t *));
+  int n_ranges = rand_interval_triangular(0, NON_V_MAX_RANGES, seed);
+  int i;
+
+  for (i = 0; i < n_ranges; i++)
+    {
+      svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
+
+      mrange->start = rand_less_than(RANGELIST_TESTS_MAX_REV + 1, seed);
+      mrange->end = rand_less_than(RANGELIST_TESTS_MAX_REV + 1, seed);
+      mrange->inheritable = rand_less_than(2, seed);
+      APR_ARRAY_PUSH(r, svn_merge_range_t *) = mrange;
+    }
+  *rl = r;
+}
+
+/* Compare two integers pointed to by A_P and B_P, for use with qsort(). */
+static int
+int_compare(const void *a_p, const void *b_p)
+{
+  const int *a = a_p, *b = b_p;
+
+  return (*a < *b) ? -1 : (*a > *b) ? 1 : 0;
+}
+
+/* Fill an ARRAY[ARRAY_LENGTH] with values each in the inclusive range
+ * [0, MAX].  The values are in ascending order, possibly with the same
+ * value repeated any number of times.
+ */
+static void
+ascending_values(int *array,
+                 int array_length,
+                 int max,
+                 apr_uint32_t *seed)
+{
+  int i;
+
+  for (i = 0; i < array_length; i++)
+    array[i] = rand_less_than(max + 1, seed);
+  /* Sort them. (Some values will be repeated.) */
+  qsort(array, array_length, sizeof(*array), int_compare);
+}
+
+/* Generate a random rangelist that is not necessarily canonical
+ * but is at least sorted according to svn_sort_compare_ranges()
+ * and on which svn_rangelist__canonicalize() would succeed.
+ * Choose from 0 to SEMI_C_MAX_RANGES ranges, biased towards the middle.
+ */
+#define SEMI_C_MAX_RANGES 8
+static void
+rangelist_random_semi_canonical(svn_rangelist_t **rl,
+                                apr_uint32_t *seed,
+                                apr_pool_t *pool)
+{
+  svn_rangelist_t *r = apr_array_make(pool, 4, sizeof(svn_merge_range_t *));
+  int n_ranges = rand_interval_triangular(0, SEMI_C_MAX_RANGES, seed);
+  int start_and_end_revs[SEMI_C_MAX_RANGES * 2];
+  int i;
+
+  /* Choose start and end revs of the ranges. To end up with ranges evenly
+   * distributed up to RANGELIST_TESTS_MAX_REV, we start with them evenly
+   * distributed up to RANGELIST_TESTS_MAX_REV - N_RANGES, before spreading. */
+  ascending_values(start_and_end_revs, n_ranges * 2,
+                   RANGELIST_TESTS_MAX_REV - n_ranges,
+                   seed);
+  /* Some values will be repeated. Within one range, that is not allowed,
+   * so add 1 to the length of each range, spreading the ranges out so they
+   * still don't overlap:
+   * [(6,6), (6,8)] becomes [(6,7), (7, 10)] */
+  for (i = 0; i < n_ranges; i++)
+    {
+      start_and_end_revs[i*2] += i;
+      start_and_end_revs[i*2 + 1] += i+1;
+    }
+
+  for (i = 0; i < n_ranges; i++)
+    {
+      svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
+
+      mrange->start = start_and_end_revs[i * 2];
+      mrange->end = start_and_end_revs[i * 2 + 1];
+      mrange->inheritable = rand_less_than(2, seed);
+      APR_ARRAY_PUSH(r, svn_merge_range_t *) = mrange;
+    }
+  *rl = r;
+
+  /* check postconditions */
+  {
+    svn_rangelist_t *dup;
+    svn_error_t *err;
+
+    SVN_ERR_ASSERT_NO_RETURN(rangelist_is_sorted(*rl));
+    dup = svn_rangelist_dup(*rl, pool);
+    err = svn_rangelist__canonicalize(dup, pool);
+    SVN_ERR_ASSERT_NO_RETURN(!err);
+  }
+}
+
+/* Generate a random rangelist that satisfies svn_rangelist__is_canonical().
+ */
+static void
+rangelist_random_canonical(svn_rangelist_t **rl,
+                           apr_uint32_t *seed,
+                           apr_pool_t *pool)
+{
+  svn_rangelist_t *r;
+  int i;
+
+  rangelist_random_semi_canonical(&r, seed, pool);
+  for (i = 1; i < r->nelts; i++)
+    {
+      svn_merge_range_t *prev_mrange = APR_ARRAY_IDX(r, i-1, svn_merge_range_t 
*);
+      svn_merge_range_t *mrange = APR_ARRAY_IDX(r, i, svn_merge_range_t *);
+
+      /* to be canonical: adjacent ranges need differing inheritability */
+      if (mrange->start == prev_mrange->end)
+        {
+          mrange->inheritable = !prev_mrange->inheritable;
+        }
+    }
+  *rl = r;
+
+  /* check postconditions */
+  SVN_ERR_ASSERT_NO_RETURN(svn_rangelist__is_canonical(*rl));
+}
+
+static const char *
+rangelist_to_string(const svn_rangelist_t *rl,
+                    apr_pool_t *pool)
+{
+  svn_error_t *err;
+  svn_string_t *ss;
+
+  err = svn_rangelist_to_string(&ss, rl, pool);
+  if (err)
+    {
+      const char *s
+        = apr_psprintf(pool, "<rangelist[%d ranges]: %s>",
+                       rl->nelts, svn_error_purge_tracing(err)->message);
+      svn_error_clear(err);
+      return s;
+    }
+  return ss->data;
+}
+
+/* Try svn_rangelist_merge2(rlx, rly) and check errors and result */
+static svn_error_t *
+rangelist_merge_random_inputs(svn_rangelist_t *rlx,
+                              svn_rangelist_t *rly,
+                              apr_pool_t *pool)
+{
+  rl_array_t ax, ay, a_expected, a_actual;
+  svn_rangelist_t *rlm;
+
+  rangelist_to_array(&ax, rlx);
+  rangelist_to_array(&ay, rly);
+
+  rlm = svn_rangelist_dup(rlx, pool);
+  /*printf("testcase: %s / %s\n", rangelist_to_string(rlx, pool), 
rangelist_to_string(rly, pool));*/
+  SVN_ERR(svn_rangelist_merge2(rlm, rly, pool, pool));
+
+  if (!svn_rangelist__is_canonical(rlm))
+    {
+      return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+                               "non-canonical result %s",
+                               rangelist_to_string(rlm, pool));
+    }
+
+  /*SVN_TEST_ASSERT(rangelist_equal(rlm, ...));*/
+  rangelist_array_union(&a_expected, &ax, &ay);
+  rangelist_to_array(&a_actual, rlm);
+  if (!rangelist_array_equal(&a_actual, &a_expected))
+    {
+      return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+                               "wrong result: (c? %d / %d) -> %s",
+                               svn_rangelist__is_canonical(rlx),
+                               svn_rangelist__is_canonical(rly),
+                               rangelist_to_string(rlm, pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Insert a failure mode (ERR_CHAIN) into RESPONSES, keyed by a message
+ * representing its failure mode.  The failure mode message is the lowest
+ * level error message in ERR_CHAIN, with some case-specific details
+ * removed to aid de-duplication.  If it is new failure mode (not already in
+ * RESPONSES), store the error and return the message (hash key), else
+ * clear the error and return NULL.
+ */
+static const char *
+add_failure_mode(svn_error_t *err_chain,
+                 apr_hash_t *failure_modes)
+{
+  svn_error_t *err = err_chain;
+  char buf[100];
+  const char *message;
+
+  if (!err_chain)
+    return NULL;
+
+  while (err->child)
+    err = err->child;
+  message = svn_err_best_message(err, buf, sizeof(buf));
+
+  /* For deduplication, ignore case-specific data in certain messages. */
+  if (strstr(message, "Unable to parse overlapping revision ranges '"))
+            message = "Unable to parse overlapping revision ranges '...";
+  if (strstr(message, "wrong result: (c?"))
+            message = "wrong result: (c?...";
+  if (strstr(message, "svn_sort__array_insert2: Attempted insert at index "))
+            message = "svn_sort__array_insert2: Attempted insert at index ...";
+
+  if (!svn_hash_gets(failure_modes, message))
+    {
+      svn_hash_sets(failure_modes, message, err);
+      return message;
+    }
+  else
+    {
+      svn_error_clear(err_chain);
+      return NULL;
+    }
+}
+
+static void
+clear_failure_mode_errors(apr_hash_t *failure_modes, apr_pool_t *scratch_pool)
+{
+  apr_hash_index_t *hi;
+
+  for (hi = apr_hash_first(scratch_pool, failure_modes);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      svn_error_t *err = apr_hash_this_val(hi);
+      svn_error_clear(err);
+    }
+}
+
+static svn_error_t *
+test_rangelist_merge_random_canonical_inputs(apr_pool_t *pool)
+{
+  static apr_uint32_t seed = 0;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  apr_hash_t *failure_modes = apr_hash_make(pool);
+  svn_boolean_t pass = TRUE;
+  int ix, iy;
+
+  /* "300": See comment "Random testing parameters and coverage" */
+  for (ix = 0; ix < 300; ix++)
+   {
+    svn_rangelist_t *rlx;
+
+    rangelist_random_canonical(&rlx, &seed, pool);
+
+    for (iy = 0; iy < 300; iy++)
+      {
+        svn_rangelist_t *rly;
+        svn_error_t *err;
+        const char *failure_mode;
+
+        svn_pool_clear(iterpool);
+
+        rangelist_random_canonical(&rly, &seed, iterpool);
+
+        err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, 
iterpool));
+        failure_mode = add_failure_mode(err, failure_modes);
+        if (failure_mode)
+          {
+            printf("first example of a failure mode: %s / %s\n"
+                   "  %s\n",
+                   rangelist_to_string(rlx, iterpool),
+                   rangelist_to_string(rly, iterpool),
+                   failure_mode);
+            /*svn_handle_error(err, stdout, FALSE);*/
+            pass = FALSE;
+          }
+      }
+   }
+
+  clear_failure_mode_errors(failure_modes, pool);
+
+  if (!pass)
+    return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+                             "Test failed: %d failure modes",
+                             apr_hash_count(failure_modes));
+  return SVN_NO_ERROR;
+}
+
+/* Test svn_rangelist_merge2() with inputs that confirm to its doc-string.
+ * Fail if any errors are produced.
+ */
+static svn_error_t *
+test_rangelist_merge_random_semi_c_inputs(apr_pool_t *pool)
+{
+  static apr_uint32_t seed = 0;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  apr_hash_t *failure_modes = apr_hash_make(pool);
+  svn_boolean_t pass = TRUE;
+  int ix, iy;
+
+  /* "300": See comment "Random testing parameters and coverage" */
+  for (ix = 0; ix < 300; ix++)
+   {
+    svn_rangelist_t *rlx;
+
+    rangelist_random_semi_canonical(&rlx, &seed, pool);
+
+    for (iy = 0; iy < 300; iy++)
+      {
+        svn_rangelist_t *rly;
+        svn_error_t *err;
+        const char *failure_mode;
+
+        svn_pool_clear(iterpool);
+
+        rangelist_random_semi_canonical(&rly, &seed, iterpool);
+
+        err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, 
iterpool));
+        failure_mode = add_failure_mode(err, failure_modes);
+        if (failure_mode)
+          {
+            printf("first example of a failure mode: %s / %s\n"
+                   "  %s\n",
+                   rangelist_to_string(rlx, iterpool),
+                   rangelist_to_string(rly, iterpool),
+                   failure_mode);
+            /*svn_handle_error(err, stdout, FALSE);*/
+            pass = FALSE;
+          }
+      }
+   }
+
+  clear_failure_mode_errors(failure_modes, pool);
+
+  if (!pass)
+    return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
+                             "Test failed: %d failure modes",
+                             apr_hash_count(failure_modes));
+  return SVN_NO_ERROR;
+}
+
+/* Test svn_rangelist_merge2() with random non-validated inputs.
+ *
+ * Unlike the tests with valid inputs, this test expects many assertion
+ * failures.  We don't care about those.  All we care about is that it does
+ * not crash. */
+static svn_error_t *
+test_rangelist_merge_random_non_validated_inputs(apr_pool_t *pool)
+{
+  static apr_uint32_t seed = 0;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  apr_hash_t *failure_modes = apr_hash_make(pool);
+  int ix, iy;
+
+  /* "300": See comment "Random testing parameters and coverage" */
+  for (ix = 0; ix < 300; ix++)
+   {
+    svn_rangelist_t *rlx;
+
+    rangelist_random_non_validated(&rlx, &seed, pool);
+
+    for (iy = 0; iy < 300; iy++)
+      {
+        svn_rangelist_t *rly;
+        svn_error_t *err;
+
+        svn_pool_clear(iterpool);
+
+        rangelist_random_non_validated(&rly, &seed, iterpool);
+
+        err = svn_error_trace(rangelist_merge_random_inputs(rlx, rly, 
iterpool));
+        add_failure_mode(err, failure_modes);
+      }
+   }
+
+  clear_failure_mode_errors(failure_modes, pool);
+
+  return SVN_NO_ERROR;
+}
+
+/* Generate random mergeinfo, in which the paths and rangelists are not
+ * necessarily valid. */
+static svn_error_t *
+mergeinfo_random_non_validated(svn_mergeinfo_t *mp,
+                               apr_uint32_t *seed,
+                               apr_pool_t *pool)
+{
+  svn_mergeinfo_t m = apr_hash_make(pool);
+  int n_paths = 3;  /* See comment "Random testing parameters and coverage" */
+  int i;
+
+  for (i = 0; i < n_paths; i++)
+    {
+      const char *path;
+      svn_rangelist_t *rl;
+
+      /* A manually chosen distribution of valid and invalid paths:
+         See comment "Random testing parameters and coverage" */
+      switch (rand_less_than(8, seed))
+        {
+        case 0: case 1: case 2: case 3:
+          path = apr_psprintf(pool, "/path%d", i); break;
+        case 4:
+          path = apr_psprintf(pool, "path%d", i); break;
+        case 5:
+          path = apr_psprintf(pool, "//path%d", i); break;
+        case 6:
+          path = "/"; break;
+        case 7:
+          path = ""; break;
+        }
+      rangelist_random_non_validated(&rl, seed, pool);
+      svn_hash_sets(m, path, rl);
+    }
+  *mp = m;
+  return SVN_NO_ERROR;
+}
+
+#if 0
+static const char *
+mergeinfo_to_string_debug(svn_mergeinfo_t m,
+                          apr_pool_t *pool)
+{
+  svn_string_t *s;
+  svn_error_t *err;
+
+  err = svn_mergeinfo_to_string(&s, m, pool);
+  if (err)
+    {
+      const char *s2 = err->message;
+      svn_error_clear(err);
+      return s2;
+    }
+  return s->data;
+}
+#endif
+
+/* Try a mergeinfo merge.  This does not check the result. */
+static svn_error_t *
+mergeinfo_merge_random_inputs(const svn_mergeinfo_t mx,
+                              const svn_mergeinfo_t my,
+                              apr_pool_t *pool)
+{
+  svn_mergeinfo_t mm = svn_mergeinfo_dup(mx, pool);
+
+  SVN_ERR(svn_mergeinfo_merge2(mm, my, pool, pool));
+  return SVN_NO_ERROR;
+}
+
+/* Test svn_mergeinfo_merge2() with random non-validated inputs.
+ *
+ * Unlike the tests with valid inputs, this test expects many assertion
+ * failures.  We don't care about those.  All we care about is that it does
+ * not crash. */
+static svn_error_t *
+test_mergeinfo_merge_random_non_validated_inputs(apr_pool_t *pool)
+{
+  static apr_uint32_t seed = 0;
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  int ix, iy;
+
+  for (ix = 0; ix < 300; ix++)
+   {
+    svn_mergeinfo_t mx;
+
+    SVN_ERR(mergeinfo_random_non_validated(&mx, &seed, pool));
+
+    for (iy = 0; iy < 300; iy++)
+      {
+        svn_mergeinfo_t my;
+        svn_error_t *err;
+
+        svn_pool_clear(iterpool);
+
+        SVN_ERR(mergeinfo_random_non_validated(&my, &seed, iterpool));
+
+        err = mergeinfo_merge_random_inputs(mx, my, iterpool);
+        if (err)
+          {
+            /*
+            printf("testcase FAIL: %s / %s\n",
+                   mergeinfo_to_string_debug(mx, iterpool),
+                   mergeinfo_to_string_debug(my, iterpool));
+            svn_handle_error(err, stdout, FALSE);
+            */
+            svn_error_clear(err);
+          }
+      }
+   }
+
+  return SVN_NO_ERROR;
+}
 
 /* The test table.  */
 
@@ -1900,10 +2524,22 @@ static struct svn_test_descriptor_t test
                    "diff of rangelists"),
     SVN_TEST_PASS2(test_remove_prefix_from_catalog,
                    "removal of prefix paths from catalog keys"),
-    SVN_TEST_XFAIL2(test_rangelist_merge_overlap,
+    SVN_TEST_PASS2(test_rangelist_merge_overlap,
                    "merge of rangelists with overlaps (issue 4686)"),
-    SVN_TEST_XFAIL2(test_rangelist_loop,
+    SVN_TEST_PASS2(test_rangelist_loop,
                     "test rangelist edgecases via loop"),
+    SVN_TEST_PASS2(test_rangelist_merge_canonical_result,
+                   "test rangelist merge canonical result (#4840)"),
+    SVN_TEST_PASS2(test_rangelist_merge_array_insert_failure,
+                   "test rangelist merge array insert failure (#4840)"),
+    SVN_TEST_PASS2(test_rangelist_merge_random_canonical_inputs,
+                   "test rangelist merge random canonical inputs"),
+    SVN_TEST_PASS2(test_rangelist_merge_random_semi_c_inputs,
+                   "test rangelist merge random semi-c inputs"),
+    SVN_TEST_PASS2(test_rangelist_merge_random_non_validated_inputs,
+                   "test rangelist merge random non-validated inputs"),
+    SVN_TEST_PASS2(test_mergeinfo_merge_random_non_validated_inputs,
+                   "test mergeinfo merge random non-validated inputs"),
     SVN_TEST_NULL
   };
 

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_subr/priority-queue-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/priority-queue-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- 
subversion/branches/addremove/subversion/tests/libsvn_subr/priority-queue-test.c
 (original)
+++ 
subversion/branches/addremove/subversion/tests/libsvn_subr/priority-queue-test.c
 Sat May 23 14:16:56 2020
@@ -125,7 +125,7 @@ verify_queue_order(svn_priority_queue__t
     }
 
   /* the queue should now be empty */
-  verify_empty_queue(queue);
+  SVN_ERR(verify_empty_queue(queue));
 
   return SVN_NO_ERROR;
 }
@@ -154,7 +154,7 @@ test_empty_queue(apr_pool_t *pool)
   svn_priority_queue__t *queue
     = svn_priority_queue__create(elements, compare_func);
 
-  verify_empty_queue(queue);
+  SVN_ERR(verify_empty_queue(queue));
 
   return SVN_NO_ERROR;
 }
@@ -214,7 +214,7 @@ test_update(apr_pool_t *pool)
     }
 
   /* the queue should now be empty */
-  verify_empty_queue(queue);
+  SVN_ERR(verify_empty_queue(queue));
 
   return SVN_NO_ERROR;
 }

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_subr/stream-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/stream-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_subr/stream-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_subr/stream-test.c 
Sat May 23 14:16:56 2020
@@ -1000,6 +1000,71 @@ test_stream_readline_file_crlf(apr_pool_
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_stream_readline_file_nul(apr_pool_t *pool)
+{
+  /* Test is written based on the problem report in
+       
https://lists.apache.org/thread.html/c96eb5618ac0bf6e083345e0fdcdcf834e30913f26eabe6ada7bab62@%3Cusers.subversion.apache.org%3E
+     where the user had an OOM when calling `svndumpfilter` with
+     a (most likely) invalid dump containing nul bytes.
+   */
+  const char data[] =
+    {
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n',
+      'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n', 'a', '\0', '\n'
+    };
+  const char *tmp_dir;
+  const char *tmp_file;
+  svn_stream_t *stream;
+
+  SVN_ERR(svn_dirent_get_absolute(&tmp_dir, "test_stream_readline_file_nul", 
pool));
+  SVN_ERR(svn_io_remove_dir2(tmp_dir, TRUE, NULL, NULL, pool));
+  SVN_ERR(svn_io_make_dir_recursively(tmp_dir, pool));
+  svn_test_add_dir_cleanup(tmp_dir);
+
+  tmp_file = svn_dirent_join(tmp_dir, "file", pool);
+  SVN_ERR(svn_io_file_create_bytes(tmp_file, data, sizeof(data), pool));
+  SVN_ERR(svn_stream_open_readonly(&stream, tmp_file, pool, pool));
+
+  while (1)
+    {
+      svn_boolean_t eof;
+      svn_stringbuf_t *line;
+
+      SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, pool));
+      if (eof)
+        break;
+
+      /* Don't check the contents of the `line` here, at least for now.
+
+         In other words, we just test that this case does not crash or cause
+         unexpected errors.  The reason is that currently our `readline_fn`
+         implementations have inconsistent behavior when dealing with \0 bytes,
+         handling those differently, either in strchr() or memchr() styles.
+
+         Once we make them consistent (or even decide to bail out with an error
+         for \0), this part of the test should start properly checking `data`;
+         maybe also for non-file streams.
+       */
+    }
+
+  SVN_ERR(svn_stream_close(stream));
+
+  return SVN_NO_ERROR;
+}
+
 /* The test table.  */
 
 static int max_threads = 1;
@@ -1037,6 +1102,8 @@ static struct svn_test_descriptor_t test
                    "test reading LF-terminated lines from file"),
     SVN_TEST_PASS2(test_stream_readline_file_crlf,
                    "test reading CRLF-terminated lines from file"),
+    SVN_TEST_PASS2(test_stream_readline_file_nul,
+                   "test reading line from file with nul bytes"),
     SVN_TEST_NULL
   };
 

Modified: 
subversion/branches/addremove/subversion/tests/libsvn_subr/subst_translate-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/subst_translate-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- 
subversion/branches/addremove/subversion/tests/libsvn_subr/subst_translate-test.c
 (original)
+++ 
subversion/branches/addremove/subversion/tests/libsvn_subr/subst_translate-test.c
 Sat May 23 14:16:56 2020
@@ -158,7 +158,7 @@ test_svn_subst_translate_string2_null_en
     };
   const char **other_locale;
 
-  strncpy(orig_lc_all, setlocale(LC_ALL, NULL), sizeof (orig_lc_all));
+  strncpy(orig_lc_all, setlocale(LC_ALL, NULL), sizeof (orig_lc_all) - 1);
 
   for (other_locale = other_locales; *other_locale != NULL; ++other_locale)
   {

Modified: subversion/branches/addremove/subversion/tests/libsvn_subr/utf-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/addremove/subversion/tests/libsvn_subr/utf-test.c?rev=1878061&r1=1878060&r2=1878061&view=diff
==============================================================================
--- subversion/branches/addremove/subversion/tests/libsvn_subr/utf-test.c 
(original)
+++ subversion/branches/addremove/subversion/tests/libsvn_subr/utf-test.c Sat 
May 23 14:16:56 2020
@@ -752,8 +752,10 @@ test_utf_conversions(apr_pool_t *pool)
   {
     svn_boolean_t sixteenbit;
     svn_boolean_t bigendian;
+    apr_size_t sourcelen;
     const char *source;
     const char *result;
+    svn_boolean_t counted;
   } tests[] = {
 
 #define UTF_32_LE FALSE, FALSE
@@ -762,32 +764,36 @@ test_utf_conversions(apr_pool_t *pool)
 #define UTF_16_BE TRUE, TRUE
 
     /* Normal character conversion */
-    { UTF_32_LE, "t\0\0\0" "e\0\0\0" "s\0\0\0" "t\0\0\0" "\0\0\0\0", "test" },
-    { UTF_32_BE, "\0\0\0t" "\0\0\0e" "\0\0\0s" "\0\0\0t" "\0\0\0\0", "test" },
-    { UTF_16_LE, "t\0" "e\0" "s\0" "t\0" "\0\0", "test" },
-    { UTF_16_BE, "\0t" "\0e" "\0s" "\0t" "\0\0", "test" },
+    { UTF_32_LE, 4, "t\0\0\0" "e\0\0\0" "s\0\0\0" "t\0\0\0" "\0\0\0\0", 
"test", FALSE },
+    { UTF_32_BE, 4, "\0\0\0t" "\0\0\0e" "\0\0\0s" "\0\0\0t" "\0\0\0\0", 
"test", FALSE },
+    { UTF_16_LE, 4, "t\0" "e\0" "s\0" "t\0" "\0\0", "test", FALSE },
+    { UTF_16_BE, 4, "\0t" "\0e" "\0s" "\0t" "\0\0", "test", FALSE },
 
     /* Valid surrogate pairs */
-    { UTF_16_LE, "\x00\xD8" "\x00\xDC" "\0\0", "\xf0\x90\x80\x80" }, /* 
U+010000 */
-    { UTF_16_LE, "\x34\xD8" "\x1E\xDD" "\0\0", "\xf0\x9d\x84\x9e" }, /* 
U+01D11E */
-    { UTF_16_LE, "\xFF\xDB" "\xFD\xDF" "\0\0", "\xf4\x8f\xbf\xbd" }, /* 
U+10FFFD */
-
-    { UTF_16_BE, "\xD8\x00" "\xDC\x00" "\0\0", "\xf0\x90\x80\x80" }, /* 
U+010000 */
-    { UTF_16_BE, "\xD8\x34" "\xDD\x1E" "\0\0", "\xf0\x9d\x84\x9e" }, /* 
U+01D11E */
-    { UTF_16_BE, "\xDB\xFF" "\xDF\xFD" "\0\0", "\xf4\x8f\xbf\xbd" }, /* 
U+10FFFD */
+    { UTF_16_LE, 2, "\x00\xD8" "\x00\xDC" "\0\0", "\xf0\x90\x80\x80", FALSE }, 
/* U+010000 */
+    { UTF_16_LE, 2, "\x34\xD8" "\x1E\xDD" "\0\0", "\xf0\x9d\x84\x9e", FALSE }, 
/* U+01D11E */
+    { UTF_16_LE, 2, "\xFF\xDB" "\xFD\xDF" "\0\0", "\xf4\x8f\xbf\xbd", FALSE }, 
/* U+10FFFD */
+
+    { UTF_16_BE, 2, "\xD8\x00" "\xDC\x00" "\0\0", "\xf0\x90\x80\x80", FALSE }, 
/* U+010000 */
+    { UTF_16_BE, 2, "\xD8\x34" "\xDD\x1E" "\0\0", "\xf0\x9d\x84\x9e", FALSE }, 
/* U+01D11E */
+    { UTF_16_BE, 2, "\xDB\xFF" "\xDF\xFD" "\0\0", "\xf4\x8f\xbf\xbd", FALSE }, 
/* U+10FFFD */
 
     /* Swapped, single and trailing surrogate pairs */
-    { UTF_16_LE, "*\0" "\x00\xDC" "\x00\xD8" "*\0\0\0", "*\xed\xb0\x80" 
"\xed\xa0\x80*" },
-    { UTF_16_LE, "*\0" "\x1E\xDD" "*\0\0\0", "*\xed\xb4\x9e*" },
-    { UTF_16_LE, "*\0" "\xFF\xDB" "*\0\0\0", "*\xed\xaf\xbf*" },
-    { UTF_16_LE, "\x1E\xDD" "\0\0", "\xed\xb4\x9e" },
-    { UTF_16_LE, "\xFF\xDB" "\0\0", "\xed\xaf\xbf" },
-
-    { UTF_16_BE, "\0*" "\xDC\x00" "\xD8\x00" "\0*\0\0", "*\xed\xb0\x80" 
"\xed\xa0\x80*" },
-    { UTF_16_BE, "\0*" "\xDD\x1E" "\0*\0\0", "*\xed\xb4\x9e*" },
-    { UTF_16_BE, "\0*" "\xDB\xFF" "\0*\0\0", "*\xed\xaf\xbf*" },
-    { UTF_16_BE, "\xDD\x1E" "\0\0", "\xed\xb4\x9e" },
-    { UTF_16_BE, "\xDB\xFF" "\0\0", "\xed\xaf\xbf" },
+    { UTF_16_LE, 4, "*\0" "\x00\xDC" "\x00\xD8" "*\0\0\0", "*\xed\xb0\x80" 
"\xed\xa0\x80*", FALSE },
+    { UTF_16_LE, 3, "*\0" "\x1E\xDD" "*\0\0\0", "*\xed\xb4\x9e*", FALSE },
+    { UTF_16_LE, 3, "*\0" "\xFF\xDB" "*\0\0\0", "*\xed\xaf\xbf*", FALSE },
+    { UTF_16_LE, 1, "\x1E\xDD" "\0\0", "\xed\xb4\x9e", FALSE },
+    { UTF_16_LE, 1, "\xFF\xDB" "\0\0", "\xed\xaf\xbf", FALSE },
+
+    { UTF_16_BE, 4, "\0*" "\xDC\x00" "\xD8\x00" "\0*\0\0", "*\xed\xb0\x80" 
"\xed\xa0\x80*", FALSE },
+    { UTF_16_BE, 3, "\0*" "\xDD\x1E" "\0*\0\0", "*\xed\xb4\x9e*", FALSE },
+    { UTF_16_BE, 3, "\0*" "\xDB\xFF" "\0*\0\0", "*\xed\xaf\xbf*", FALSE },
+    { UTF_16_BE, 1, "\xDD\x1E" "\0\0", "\xed\xb4\x9e", FALSE },
+    { UTF_16_BE, 1, "\xDB\xFF" "\0\0", "\xed\xaf\xbf", FALSE },
+
+    /* Counted strings with NUL characters */
+    { UTF_16_LE, 3, "x\0" "\0\0" "y\0" "*\0", "x\0y", TRUE },
+    { UTF_32_BE, 3, "\0\0\0x" "\0\0\0\0" "\0\0\0y" "\0\0\0*", "x\0y", TRUE },
 
 #undef UTF_32_LE
 #undef UTF_32_BE
@@ -799,33 +805,46 @@ test_utf_conversions(apr_pool_t *pool)
 
   const struct cvt_test_t *tc;
   const svn_string_t *result;
-  int i;
+  apr_size_t maxlen = 0;
 
-  for (i = 1, tc = tests; tc->source; ++tc, ++i)
+  /* To assure proper alignment of the source string, it needs to be copied
+     into an array of the appropriate type before calling
+     svn_utf__utf{16,32}_to_utf8. */
+  apr_uint16_t *source16;
+  apr_int32_t *source32;
+
+  for (tc = tests; tc->source; ++tc)
+    if (tc->sourcelen > maxlen)
+      maxlen = tc->sourcelen;
+  maxlen++;
+
+  source16 = apr_pcalloc(pool, maxlen * sizeof(*source16));
+  source32 = apr_pcalloc(pool, maxlen * sizeof(*source32));
+
+  for (tc = tests; tc->source; ++tc)
     {
       if (tc->sixteenbit)
-        SVN_ERR(svn_utf__utf16_to_utf8(&result, (const void*)tc->source,
-                                       SVN_UTF__UNKNOWN_LENGTH,
-                                       tc->bigendian, pool, pool));
+        {
+          memset(source16, 0, maxlen * sizeof(*source16));
+          memcpy(source16, tc->source, (tc->sourcelen + 1) * 
sizeof(*source16));
+          SVN_ERR(svn_utf__utf16_to_utf8(&result, source16,
+                                         tc->counted ? tc->sourcelen : 
SVN_UTF__UNKNOWN_LENGTH,
+                                         tc->bigendian, pool, pool));
+        }
       else
-        SVN_ERR(svn_utf__utf32_to_utf8(&result, (const void*)tc->source,
-                                       SVN_UTF__UNKNOWN_LENGTH,
-                                       tc->bigendian, pool, pool));
-      SVN_ERR_ASSERT(0 == strcmp(result->data, tc->result));
+        {
+          memset(source32, 0, maxlen * sizeof(*source32));
+          memcpy(source32, tc->source, (tc->sourcelen + 1) * 
sizeof(*source32));
+          SVN_ERR(svn_utf__utf32_to_utf8(&result, source32,
+                                         tc->counted ? tc->sourcelen : 
SVN_UTF__UNKNOWN_LENGTH,
+                                         tc->bigendian, pool, pool));
+        }
+      if (tc->counted)
+        SVN_ERR_ASSERT(0 == memcmp(result->data, tc->result, tc->sourcelen));
+      else
+        SVN_ERR_ASSERT(0 == strcmp(result->data, tc->result));
     }
 
-  /* Test counted strings with NUL characters */
-  SVN_ERR(svn_utf__utf16_to_utf8(
-              &result, (void*)("x\0" "\0\0" "y\0" "*\0"), 3,
-              FALSE, pool, pool));
-  SVN_ERR_ASSERT(0 == memcmp(result->data, "x\0y", 3));
-
-  SVN_ERR(svn_utf__utf32_to_utf8(
-              &result,
-              (void*)("\0\0\0x" "\0\0\0\0" "\0\0\0y" "\0\0\0*"), 3,
-              TRUE, pool, pool));
-  SVN_ERR_ASSERT(0 == memcmp(result->data, "x\0y", 3));
-
   return SVN_NO_ERROR;
 }
 


Reply via email to