You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2022/01/14 14:01:51 UTC

svn commit: r1897034 [33/37] - in /subversion/branches/multi-wc-format: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/ contrib/client-side/svn_load_dirs/ contrib/hook-scripts/ contrib/s...

Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/authz-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/authz-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/authz-test.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/authz-test.c Fri Jan 14 14:01:45 2022
@@ -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);
 
@@ -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)
     {
@@ -463,7 +463,7 @@ issue_4741_groups(apr_pool_t *pool)
    svn_authz_t *authz;
    svn_boolean_t access_granted;
 
-   SVN_ERR(svn_repos_authz_parse(&authz, stream, NULL, pool));
+   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,
@@ -481,7 +481,7 @@ issue_4741_groups(apr_pool_t *pool)
 static svn_error_t *
 reposful_reposless_stanzas_inherit(apr_pool_t *pool)
 {
-  const char rules[] = 
+  const char rules[] =
     "[groups]"                               NL
     "company = user1, user2, user3"          NL
     "customer = customer1, customer2"        NL
@@ -500,7 +500,7 @@ reposful_reposless_stanzas_inherit(apr_p
    svn_authz_t *authz;
    svn_boolean_t access_granted;
 
-   SVN_ERR(svn_repos_authz_parse(&authz, stream, NULL, pool));
+   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,
@@ -522,7 +522,7 @@ static struct svn_test_descriptor_t test
                    "test svn_authz__get_global_rights"),
     SVN_TEST_PASS2(issue_4741_groups,
                    "issue 4741 groups"),
-    SVN_TEST_XFAIL2(reposful_reposless_stanzas_inherit,
+    SVN_TEST_PASS2(reposful_reposless_stanzas_inherit,
                     "[foo:/] inherits [/]"),
     SVN_TEST_NULL
   };

Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/repos-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/repos-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/repos-test.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/libsvn_repos/repos-test.c Fri Jan 14 14:01:45 2022
@@ -1181,6 +1181,7 @@ struct check_access_tests {
  * as defined in TESTS. */
 static svn_error_t *
 authz_check_access(svn_authz_t *authz_cfg,
+                   const char *authz_contents,
                    const struct check_access_tests *tests,
                    apr_pool_t *pool)
 {
@@ -1202,7 +1203,7 @@ authz_check_access(svn_authz_t *authz_cf
         {
           return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
                                    "Authz incorrectly %s %s%s access "
-                                   "to %s%s%s for user %s",
+                                   "to %s%s%s for user %s\n%s",
                                    access_granted ?
                                    "grants" : "denies",
                                    tests[i].required
@@ -1217,7 +1218,8 @@ authz_check_access(svn_authz_t *authz_cf
                                    ":" : "",
                                    tests[i].path,
                                    tests[i].user ?
-                                   tests[i].user : "-");
+                                   tests[i].user : "-",
+                                   authz_contents);
         }
     }
 
@@ -1312,11 +1314,11 @@ authz(apr_pool_t *pool)
   SVN_ERR(authz_get_handle(&authz_cfg, contents, FALSE, subpool));
 
   /* Loop over the test array and test each case. */
-  SVN_ERR(authz_check_access(authz_cfg, test_set, subpool));
+  SVN_ERR(authz_check_access(authz_cfg, contents, test_set, subpool));
 
   /* Repeat the previous test on disk */
   SVN_ERR(authz_get_handle(&authz_cfg, contents, TRUE, subpool));
-  SVN_ERR(authz_check_access(authz_cfg, test_set, subpool));
+  SVN_ERR(authz_check_access(authz_cfg, contents, test_set, subpool));
 
   /* The authz rules for the phase 2 tests, first case (cyclic
      dependency). */
@@ -1496,7 +1498,7 @@ test_authz_wildcards(apr_pool_t *pool)
   SVN_ERR(authz_get_handle(&authz_cfg, contents, FALSE, pool));
 
   /* Loop over the test array and test each case. */
-  SVN_ERR(authz_check_access(authz_cfg, test_set, pool));
+  SVN_ERR(authz_check_access(authz_cfg, contents, test_set, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1652,7 +1654,7 @@ test_authz_prefixes(apr_pool_t *pool)
             test->path = test_paths[i];
 
           /* Loop over the test array and test each case. */
-          SVN_ERR(authz_check_access(authz_cfg, test_set, iterpool));
+          SVN_ERR(authz_check_access(authz_cfg, contents, test_set, iterpool));
         }
     }
 
@@ -1719,7 +1721,7 @@ test_authz_recursive_override(apr_pool_t
   SVN_ERR(authz_get_handle(&authz_cfg, contents, FALSE, pool));
 
   /* Loop over the test array and test each case. */
-  SVN_ERR(authz_check_access(authz_cfg, test_set, pool));
+  SVN_ERR(authz_check_access(authz_cfg, contents, test_set, pool));
 
   /* That's a wrap! */
   return SVN_NO_ERROR;
@@ -1898,16 +1900,16 @@ test_authz_pattern_tests(apr_pool_t *poo
 
   /* Verify that the rules are applies as expected. */
   SVN_ERR(authz_get_handle(&authz_cfg, contents, FALSE, pool));
-  SVN_ERR(authz_check_access(authz_cfg, test_set, pool));
+  SVN_ERR(authz_check_access(authz_cfg, contents, test_set, pool));
 
   SVN_ERR(authz_get_handle(&authz_cfg, contents2, FALSE, pool));
-  SVN_ERR(authz_check_access(authz_cfg, test_set2, pool));
+  SVN_ERR(authz_check_access(authz_cfg, contents2, test_set2, pool));
 
   SVN_ERR(authz_get_handle(&authz_cfg, contents3, FALSE, pool));
-  SVN_ERR(authz_check_access(authz_cfg, test_set3, pool));
+  SVN_ERR(authz_check_access(authz_cfg, contents3, test_set3, pool));
 
   SVN_ERR(authz_get_handle(&authz_cfg, contents4, FALSE, pool));
-  SVN_ERR(authz_check_access(authz_cfg, test_set4, pool));
+  SVN_ERR(authz_check_access(authz_cfg, contents4, test_set4, pool));
 
   /* That's a wrap! */
   return SVN_NO_ERROR;
@@ -1984,7 +1986,7 @@ in_repo_authz(const svn_test_opts_t *opt
 
   /* absolute file URL. */
   SVN_ERR(svn_repos_authz_read2(&authz_cfg, authz_url, NULL, TRUE, pool));
-  SVN_ERR(authz_check_access(authz_cfg, test_set, pool));
+  SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set, pool));
 
   /* Non-existent path in the repo with must_exist set to FALSE */
   SVN_ERR(svn_repos_authz_read2(&authz_cfg, noent_authz_url, NULL,
@@ -2127,7 +2129,7 @@ in_repo_groups_authz(const svn_test_opts
 
   /* absolute file URLs. */
   SVN_ERR(svn_repos_authz_read2(&authz_cfg, authz_url, groups_url, TRUE, pool));
-  SVN_ERR(authz_check_access(authz_cfg, test_set, pool));
+  SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set, pool));
 
   /* Non-existent path for the groups file with must_exist
    * set to TRUE */
@@ -2318,12 +2320,12 @@ groups_authz(const svn_test_opts_t *opts
   SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents,
                                   groups_contents, TRUE, pool));
 
-  SVN_ERR(authz_check_access(authz_cfg, test_set1, pool));
+  SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set1, pool));
 
   SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents,
                                   groups_contents, FALSE, pool));
 
-  SVN_ERR(authz_check_access(authz_cfg, test_set1, pool));
+  SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set1, pool));
 
   /* Access rights in the global groups file are forbidden. */
   groups_contents =
@@ -2355,12 +2357,12 @@ groups_authz(const svn_test_opts_t *opts
   SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents,
                                   groups_contents, TRUE, pool));
 
-  SVN_ERR(authz_check_access(authz_cfg, test_set2, pool));
+  SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set2, pool));
 
   SVN_ERR(authz_groups_get_handle(&authz_cfg, authz_contents,
                                   groups_contents, FALSE, pool));
 
-  SVN_ERR(authz_check_access(authz_cfg, test_set2, pool));
+  SVN_ERR(authz_check_access(authz_cfg, authz_contents, test_set2, pool));
 
   /* Local groups cannot be used in conjunction with global groups. */
   groups_contents =
@@ -4154,7 +4156,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));

Propchange: subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Fri Jan 14 14:01:45 2022
@@ -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/multi-wc-format/subversion/tests/libsvn_subr/dirent_uri-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/dirent_uri-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/dirent_uri-test.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/dirent_uri-test.c Fri Jan 14 14:01:45 2022
@@ -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/multi-wc-format/subversion/tests/libsvn_subr/io-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/io-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/io-test.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/io-test.c Fri Jan 14 14:01:45 2022
@@ -31,6 +31,7 @@
 #include "svn_pools.h"
 #include "svn_string.h"
 #include "svn_io.h"
+#include "svn_time.h"
 #include "private/svn_skel.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_io_private.h"
@@ -211,6 +212,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.  */
 
@@ -939,7 +972,11 @@ test_install_stream_to_longpath(apr_pool
   const char *final_abspath;
   const char *deep_dir;
   svn_stream_t *stream;
+  apr_time_t mtime;
+  apr_off_t size;
+  svn_boolean_t is_read_only;
   svn_stringbuf_t *actual_content;
+  apr_finfo_t finfo;
   int i;
 
   /* Create an empty directory. */
@@ -960,10 +997,21 @@ test_install_stream_to_longpath(apr_pool
   SVN_ERR(svn_stream__create_for_install(&stream, deep_dir, pool, pool));
   SVN_ERR(svn_stream_puts(stream, "stream1 content"));
   SVN_ERR(svn_stream_close(stream));
+  SVN_ERR(svn_stream__install_finalize(&mtime, &size, stream, pool));
+  /* Ensure that we will notice a timestamp change, if it happens. */
+  svn_io_sleep_for_timestamps(NULL, pool);
   SVN_ERR(svn_stream__install_stream(stream,
                                      final_abspath,
                                      TRUE,
                                      pool));
+  SVN_ERR(svn_io_stat(&finfo, final_abspath,
+                      APR_FINFO_MTIME | APR_FINFO_SIZE | SVN__APR_FINFO_READONLY,
+                      pool));
+  /* Should see the same values as before the install. */
+  SVN_TEST_INT_ASSERT(finfo.mtime, mtime);
+  SVN_TEST_INT_ASSERT(finfo.size, size);
+  SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, pool));
+  SVN_TEST_ASSERT(!is_read_only);
 
   SVN_ERR(svn_stringbuf_from_file2(&actual_content,
                                    final_abspath,
@@ -980,7 +1028,11 @@ test_install_stream_over_readonly_file(a
   const char *tmp_dir;
   const char *final_abspath;
   svn_stream_t *stream;
+  apr_time_t mtime;
+  apr_off_t size;
+  svn_boolean_t is_read_only;
   svn_stringbuf_t *actual_content;
+  apr_finfo_t finfo;
 
   /* Create an empty directory. */
   SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir,
@@ -996,10 +1048,69 @@ test_install_stream_over_readonly_file(a
   SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, pool, pool));
   SVN_ERR(svn_stream_puts(stream, "stream1 content"));
   SVN_ERR(svn_stream_close(stream));
+  SVN_ERR(svn_stream__install_finalize(&mtime, &size, stream, pool));
+  /* Ensure that we will notice a timestamp change, if it happens. */
+  svn_io_sleep_for_timestamps(NULL, pool);
+  SVN_ERR(svn_stream__install_stream(stream,
+                                     final_abspath,
+                                     TRUE,
+                                     pool));
+  SVN_ERR(svn_io_stat(&finfo, final_abspath,
+                      APR_FINFO_MTIME | APR_FINFO_SIZE | SVN__APR_FINFO_READONLY,
+                      pool));
+  /* Should see the same values as before the install. */
+  SVN_TEST_INT_ASSERT(finfo.mtime, mtime);
+  SVN_TEST_INT_ASSERT(finfo.size, size);
+  SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, pool));
+  SVN_TEST_ASSERT(!is_read_only);
+
+  SVN_ERR(svn_stringbuf_from_file2(&actual_content,
+                                   final_abspath,
+                                   pool));
+
+  SVN_TEST_STRING_ASSERT(actual_content->data, "stream1 content");
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_install_stream_set_read_only(apr_pool_t *pool)
+{
+  const char *tmp_dir;
+  const char *final_abspath;
+  svn_stream_t *stream;
+  apr_time_t mtime;
+  apr_off_t size;
+  svn_boolean_t is_read_only;
+  svn_stringbuf_t *actual_content;
+  apr_finfo_t finfo;
+
+  /* Create an empty directory. */
+  SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir,
+                                    "test_install_stream_set_read_only",
+                                    pool));
+
+  final_abspath = svn_dirent_join(tmp_dir, "stream1", pool);
+
+  SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, pool, pool));
+  SVN_ERR(svn_stream_puts(stream, "stream1 content"));
+  SVN_ERR(svn_stream_close(stream));
+  svn_stream__install_set_read_only(stream, TRUE);
+  SVN_ERR(svn_stream__install_finalize(&mtime, &size, stream, pool));
+  /* Ensure that we will notice a timestamp change, if it happens. */
+  svn_io_sleep_for_timestamps(NULL, pool);
   SVN_ERR(svn_stream__install_stream(stream,
                                      final_abspath,
                                      TRUE,
                                      pool));
+  SVN_ERR(svn_io_stat(&finfo, final_abspath,
+                      APR_FINFO_MTIME | APR_FINFO_SIZE | SVN__APR_FINFO_READONLY,
+                      pool));
+  /* Should see the same values as before the install. */
+  SVN_TEST_INT_ASSERT(finfo.mtime, mtime);
+  SVN_TEST_INT_ASSERT(finfo.size, size);
+  SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, pool));
+  SVN_TEST_ASSERT(is_read_only);
 
   SVN_ERR(svn_stringbuf_from_file2(&actual_content,
                                    final_abspath,
@@ -1011,6 +1122,162 @@ test_install_stream_over_readonly_file(a
 }
 
 static svn_error_t *
+test_install_stream_set_affected_time(apr_pool_t *pool)
+{
+  const char *tmp_dir;
+  const char *final_abspath;
+  svn_stream_t *stream;
+  apr_time_t expected_timestamp;
+  apr_time_t mtime;
+  apr_off_t size;
+  svn_boolean_t is_read_only;
+  svn_stringbuf_t *actual_content;
+  apr_finfo_t finfo;
+
+  /* Create an empty directory. */
+  SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir,
+                                    "test_install_stream_set_affected_time",
+                                    pool));
+
+  final_abspath = svn_dirent_join(tmp_dir, "stream1", pool);
+
+  SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, pool, pool));
+  SVN_ERR(svn_stream_puts(stream, "stream1 content"));
+  SVN_ERR(svn_stream_close(stream));
+
+  SVN_ERR(svn_time_from_cstring(&expected_timestamp,
+                                "2002-05-13T19:00:50.966679Z",
+                                pool));
+  svn_stream__install_set_affected_time(stream, expected_timestamp);
+
+  SVN_ERR(svn_stream__install_finalize(&mtime, &size, stream, pool));
+  /* Ensure that we will notice a timestamp change, if it happens. */
+  svn_io_sleep_for_timestamps(NULL, pool);
+  SVN_ERR(svn_stream__install_stream(stream,
+                                     final_abspath,
+                                     TRUE,
+                                     pool));
+  SVN_ERR(svn_io_stat(&finfo, final_abspath,
+                      APR_FINFO_MTIME | APR_FINFO_SIZE | SVN__APR_FINFO_READONLY,
+                      pool));
+  /* Should see the same values as before the install. */
+  SVN_TEST_INT_ASSERT(finfo.mtime, mtime);
+  SVN_TEST_INT_ASSERT(finfo.size, size);
+  /* The actual filesystem might have a different timestamp precision,
+     so compare with proximity. */
+  SVN_TEST_TIME_ASSERT(finfo.mtime, expected_timestamp, apr_time_from_sec(10));
+  SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, pool));
+  SVN_TEST_ASSERT(!is_read_only);
+
+  SVN_ERR(svn_stringbuf_from_file2(&actual_content,
+                                   final_abspath,
+                                   pool));
+
+  SVN_TEST_STRING_ASSERT(actual_content->data, "stream1 content");
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_install_stream(apr_pool_t *pool)
+{
+  const char *tmp_dir;
+  const char *final_abspath;
+  svn_stream_t *stream;
+  apr_time_t mtime;
+  apr_off_t size;
+  svn_boolean_t is_read_only;
+  svn_stringbuf_t *actual_content;
+  apr_finfo_t finfo;
+
+  /* Create an empty directory. */
+  SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir,
+                                    "test_install_stream",
+                                    pool));
+
+  final_abspath = svn_dirent_join(tmp_dir, "stream1", pool);
+
+  SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, pool, pool));
+  SVN_ERR(svn_stream_puts(stream, "stream1 content"));
+  SVN_ERR(svn_stream_close(stream));
+  SVN_ERR(svn_stream__install_finalize(&mtime, &size, stream, pool));
+  /* Ensure that we will notice a timestamp change, if it happens. */
+  svn_io_sleep_for_timestamps(NULL, pool);
+  SVN_ERR(svn_stream__install_stream(stream,
+                                     final_abspath,
+                                     TRUE,
+                                     pool));
+  SVN_ERR(svn_io_stat(&finfo, final_abspath,
+                      APR_FINFO_MTIME | APR_FINFO_SIZE | SVN__APR_FINFO_READONLY,
+                      pool));
+  /* Should see the same values as before the install. */
+  SVN_TEST_INT_ASSERT(finfo.mtime, mtime);
+  SVN_TEST_INT_ASSERT(finfo.size, size);
+  SVN_ERR(svn_io__is_finfo_read_only(&is_read_only, &finfo, pool));
+  SVN_TEST_ASSERT(!is_read_only);
+
+  SVN_ERR(svn_stringbuf_from_file2(&actual_content,
+                                   final_abspath,
+                                   pool));
+  SVN_TEST_STRING_ASSERT(actual_content->data, "stream1 content");
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_install_stream_delete(apr_pool_t *pool)
+{
+  const char *tmp_dir;
+  apr_pool_t *subpool;
+  svn_stream_t *stream;
+  apr_hash_t *dirents;
+
+  /* Create an empty directory. */
+  SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir,
+                                    "test_install_stream_delete",
+                                    pool));
+
+  subpool = svn_pool_create(pool);
+  SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, subpool, subpool));
+  SVN_ERR(svn_stream_puts(stream, "stream1 content"));
+  SVN_ERR(svn_stream_close(stream));
+  SVN_ERR(svn_stream__install_delete(stream, subpool));
+  svn_pool_destroy(subpool);
+
+  SVN_ERR(svn_io_get_dirents3(&dirents, tmp_dir, TRUE, pool, pool));
+  SVN_TEST_INT_ASSERT(apr_hash_count(dirents), 0);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_install_stream_delete_after_finalize(apr_pool_t *pool)
+{
+  const char *tmp_dir;
+  apr_pool_t *subpool;
+  svn_stream_t *stream;
+  apr_hash_t *dirents;
+
+  /* Create an empty directory. */
+  SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir,
+                                    "test_install_stream_delete_after_finalize",
+                                    pool));
+
+  subpool = svn_pool_create(pool);
+  SVN_ERR(svn_stream__create_for_install(&stream, tmp_dir, subpool, subpool));
+  SVN_ERR(svn_stream_puts(stream, "stream1 content"));
+  SVN_ERR(svn_stream_close(stream));
+  SVN_ERR(svn_stream__install_finalize(NULL, NULL, stream, subpool));
+  SVN_ERR(svn_stream__install_delete(stream, subpool));
+  svn_pool_destroy(subpool);
+
+  SVN_ERR(svn_io_get_dirents3(&dirents, tmp_dir, TRUE, pool, pool));
+  SVN_TEST_INT_ASSERT(apr_hash_count(dirents), 0);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 test_file_size_get(apr_pool_t *pool)
 {
   const char *tmp_dir, *path;
@@ -1113,11 +1380,8 @@ test_apr_trunc_workaround(apr_pool_t *po
   char dummy;
 
   /* create a temp folder & schedule it for automatic cleanup */
-  SVN_ERR(svn_dirent_get_absolute(&tmp_dir, "test_apr_trunc_workaround",
-                                  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);
+  SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir, "test_apr_trunc_workaround",
+                                    pool));
 
   /* create an r/w file */
   tmp_file = svn_dirent_join(tmp_dir, "file", pool);
@@ -1144,9 +1408,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;
@@ -1172,6 +1486,16 @@ static struct svn_test_descriptor_t test
                    "test svn_stream__install_stream to long path"),
     SVN_TEST_PASS2(test_install_stream_over_readonly_file,
                    "test svn_stream__install_stream over RO file"),
+    SVN_TEST_PASS2(test_install_stream_set_read_only,
+                   "test svn_stream__install_set_read_only"),
+    SVN_TEST_PASS2(test_install_stream_set_affected_time,
+                   "test svn_stream__install_set_affected_time"),
+    SVN_TEST_PASS2(test_install_stream,
+                   "test svn_stream__install_stream"),
+    SVN_TEST_PASS2(test_install_stream_delete,
+                   "test svn_stream__install_delete"),
+    SVN_TEST_PASS2(test_install_stream_delete_after_finalize,
+                   "test svn_stream__install_delete after finalize"),
     SVN_TEST_PASS2(test_file_size_get,
                    "test svn_io_file_size_get"),
     SVN_TEST_PASS2(test_file_rename2,
@@ -1184,6 +1508,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/multi-wc-format/subversion/tests/libsvn_subr/mergeinfo-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/mergeinfo-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/mergeinfo-test.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/mergeinfo-test.c Fri Jan 14 14:01:45 2022
@@ -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.  */
@@ -1783,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.  */
 
@@ -1831,6 +2528,18 @@ static struct svn_test_descriptor_t test
                    "merge of rangelists with overlaps (issue 4686)"),
     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/multi-wc-format/subversion/tests/libsvn_subr/stream-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/stream-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/stream-test.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/libsvn_subr/stream-test.c Fri Jan 14 14:01:45 2022
@@ -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/multi-wc-format/subversion/tests/libsvn_wc/conflict-data-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/conflict-data-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/conflict-data-test.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/conflict-data-test.c Fri Jan 14 14:01:45 2022
@@ -911,7 +911,7 @@ test_prop_conflict_resolving(const svn_t
   SVN_TEST_STRING_ASSERT(value, "r1");
   value = svn_prop_get_value(props, "prop-3");
   SVN_TEST_STRING_ASSERT(value, "mod");
-  
+
   return SVN_NO_ERROR;
 }
 

Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/db-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/db-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/db-test.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/db-test.c Fri Jan 14 14:01:45 2022
@@ -635,7 +635,7 @@ test_inserting_nodes(apr_pool_t *pool)
             1, TIME_1a, AUTHOR_1,
             checksum,
             NULL, FALSE, FALSE, NULL, NULL, FALSE, FALSE,
-            NULL, NULL,
+            NULL, NULL, FALSE, NULL,
             pool));
 
   /* Create a new symlink node. */

Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/op-depth-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/op-depth-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/op-depth-test.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/op-depth-test.c Fri Jan 14 14:01:45 2022
@@ -1770,7 +1770,7 @@ test_db_make_copy(const svn_test_opts_t
     };
 
     SVN_ERR(insert_dirs(&b, before));
-    SVN_ERR(svn_wc__db_op_make_copy(b.wc_ctx->db, sbox_wc_path(&b, "A"), 
+    SVN_ERR(svn_wc__db_op_make_copy(b.wc_ctx->db, sbox_wc_path(&b, "A"),
                                     NULL, NULL, pool));
 
     SVN_ERR(check_db_rows(&b, "", after));

Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-queries-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-queries-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-queries-test.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-queries-test.c Fri Jan 14 14:01:45 2022
@@ -100,6 +100,7 @@ static const int slow_statements[] =
   STMT_SELECT_UPDATE_MOVE_LIST,
   STMT_FIND_REPOS_PATH_IN_WC,
   STMT_SELECT_PRESENT_HIGHEST_WORKING_NODES_BY_BASENAME_AND_KIND,
+  STMT_SELECT_COPIES_OF_REPOS_RELPATH,
 
   /* Designed as slow to avoid penalty on other queries */
   STMT_SELECT_UNREFERENCED_PRISTINES,
@@ -266,6 +267,14 @@ struct explanation_item
   const char *compound_right;
   svn_boolean_t create_btree;
 
+  svn_boolean_t create_union;
+  svn_boolean_t union_all;
+
+  svn_boolean_t merge;
+
+  svn_boolean_t multi_index;
+  svn_boolean_t multi_index_or;
+
   int expression_vars;
   int expected_rows;
 };
@@ -312,6 +321,18 @@ parse_explanation_item(struct explanatio
           item->table = apr_psprintf(result_pool, "SUBQUERY-%s",
                                      apr_strtok(NULL, " ", &last));
         }
+      else if (MATCH_TOKEN(token, "CONSTANT"))
+        {
+          item->table = "sqlite_master"; /* not worth checking.
+                                            Just a lookup */
+          token = apr_strtok(NULL, " ", &last);
+          if (!MATCH_TOKEN(token, "ROW"))
+            {
+              printf("DBG: Expected 'ROW', got '%s' in '%s'\n",
+                     token, text);
+              return SVN_NO_ERROR;
+            }
+        }
       else
         {
           printf("DBG: Expected 'TABLE', got '%s' in '%s'\n", token, text);
@@ -433,39 +454,44 @@ parse_explanation_item(struct explanatio
       /* Handling temporary table (E.g. UNION) */
 
       token = apr_strtok(NULL, " ", &last);
-      if (!MATCH_TOKEN(token, "SUBQUERIES"))
-        {
-          printf("DBG: Expected 'SUBQUERIES', got '%s' in '%s'\n", token,
-                 text);
-          return SVN_NO_ERROR;
-        }
-
-      item->compound_left = apr_strtok(NULL, " ", &last);
-      token = apr_strtok(NULL, " ", &last);
-
-      if (!MATCH_TOKEN(token, "AND"))
-        {
-          printf("DBG: Expected 'AND', got '%s' in '%s'\n", token, text);
-          return SVN_NO_ERROR;
-        }
-
-      item->compound_right = apr_strtok(NULL, " ", &last);
-
-      token = apr_strtok(NULL, " ", &last);
-      if (MATCH_TOKEN(token, "USING"))
+      if (MATCH_TOKEN(token, "SUBQUERIES"))
         {
+          item->compound_left = apr_strtok(NULL, " ", &last);
           token = apr_strtok(NULL, " ", &last);
-          if (!MATCH_TOKEN(token, "TEMP"))
+
+          if (!MATCH_TOKEN(token, "AND"))
             {
-              printf("DBG: Expected 'TEMP', got '%s' in '%s'\n", token, text);
+              printf("DBG: Expected 'AND', got '%s' in '%s'\n", token, text);
+              return SVN_NO_ERROR;
             }
+
+          item->compound_right = apr_strtok(NULL, " ", &last);
+
           token = apr_strtok(NULL, " ", &last);
-          if (!MATCH_TOKEN(token, "B-TREE"))
+          if (MATCH_TOKEN(token, "USING"))
             {
-              printf("DBG: Expected 'B-TREE', got '%s' in '%s'\n", token,
-                     text);
+              token = apr_strtok(NULL, " ", &last);
+              if (!MATCH_TOKEN(token, "TEMP"))
+                {
+                  printf("DBG: Expected 'TEMP', got '%s' in '%s'\n", token, text);
+                }
+              token = apr_strtok(NULL, " ", &last);
+              if (!MATCH_TOKEN(token, "B-TREE"))
+                {
+                  printf("DBG: Expected 'B-TREE', got '%s' in '%s'\n", token,
+                         text);
+                }
+              item->create_btree = TRUE;
             }
-          item->create_btree = TRUE;
+        }
+      else if (MATCH_TOKEN(token, "QUERY"))
+        {
+        }
+      else
+        {
+          printf("DBG: Expected 'SUBQUERIES', got '%s' in '%s'\n", token,
+                 text);
+          return SVN_NO_ERROR;
         }
     }
   else if (MATCH_TOKEN(item->operation, "USE"))
@@ -474,6 +500,48 @@ parse_explanation_item(struct explanatio
       /* ### Need parsing */
       item->create_btree = TRUE;
     }
+  else if (MATCH_TOKEN(item->operation, "UNION"))
+    {
+      item->create_union = TRUE;
+
+      token = apr_strtok(NULL, " ", &last);
+      if (MATCH_TOKEN(token, "ALL"))
+        item->union_all = TRUE;
+      else
+        item->union_all = FALSE;
+    }
+  else if (MATCH_TOKEN(item->operation, "INDEX"))
+    {
+
+    }
+  else if (MATCH_TOKEN(item->operation, "MULTI-INDEX"))
+    {
+      item->multi_index = TRUE;
+      token = apr_strtok(NULL, " ", &last);
+      if (MATCH_TOKEN(token, "OR"))
+        item->multi_index_or = TRUE;
+    }
+  else if (MATCH_TOKEN(item->operation, "MERGE"))
+    {
+      item->merge = TRUE;
+    }
+  else if (MATCH_TOKEN(item->operation, "LEFT")
+           || MATCH_TOKEN(item->operation, "RIGHT"))
+    {
+    }
+  else if (MATCH_TOKEN(item->operation, "CORRELATED"))
+    {
+      item->merge = TRUE;
+    }
+  else if (MATCH_TOKEN(item->operation, "CO-ROUTINE"))
+    {
+    }
+  else if (MATCH_TOKEN(item->operation, "SCALAR"))
+    {
+    }
+  else if (MATCH_TOKEN(item->operation, "LEFT-MOST"))
+    {
+    }
   else
     {
       printf("DBG: Unhandled sqlite operation '%s' in explanation\n", item->operation);
@@ -670,7 +738,20 @@ test_query_expectations(apr_pool_t *scra
                        || (item->expression_vars < 1))
                    && !is_result_table(item->table))
             {
-              if (in_list(primary_key_statements, i))
+              if (MATCH_TOKEN(item->table, "sqlite_master"))
+                {
+                  /* The sqlite_master table does not have an index.
+                     Query explanations that say 'SCAN TABLE sqlite_master'
+                     will appear if SQLite was compiled with the option
+                     SQLITE_ENABLE_STMT_SCANSTATUS, for queries such
+                     as 'DROP TABLE foo', but the performance of such
+                     statements is not our concern here. */
+
+                  /* "Slow" statements do expect to see a warning, however. */
+                  if (is_slow_statement(i))
+                    warned = TRUE;
+                }
+              else if (in_list(primary_key_statements, i))
                 {
                   /* Reported as primary key index usage in Sqlite 3.7,
                      as table scan in 3.8+, while the execution plan is

Modified: subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-test.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-test.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/libsvn_wc/wc-test.c Fri Jan 14 14:01:45 2022
@@ -35,6 +35,7 @@
 #include "svn_wc.h"
 #include "svn_client.h"
 #include "svn_hash.h"
+#include "svn_props.h"
 
 #include "utils.h"
 
@@ -493,6 +494,283 @@ test_internal_file_modified(const svn_te
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_working_file_writer_simple(const svn_test_opts_t *opts,
+                                apr_pool_t *pool)
+{
+  const char *tmp_dir;
+  svn_wc__working_file_writer_t *writer;
+  svn_stream_t *stream;
+  const char *final_abspath;
+  svn_stringbuf_t *actual_content;
+
+  SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir,
+                                    "working_file_writer_simple",
+                                    pool));
+
+  SVN_ERR(svn_wc__working_file_writer_open(&writer, tmp_dir, -1,
+                                           svn_subst_eol_style_none, NULL,
+                                           FALSE, NULL, FALSE, FALSE,
+                                           FALSE,
+                                           pool, pool));
+
+  stream = svn_wc__working_file_writer_get_stream(writer);
+  SVN_ERR(svn_stream_puts(stream, "content"));
+  SVN_ERR(svn_stream_close(stream));
+
+  SVN_ERR(svn_wc__working_file_writer_finalize(NULL, NULL, writer, pool));
+  final_abspath = svn_dirent_join(tmp_dir, "file", pool);
+  SVN_ERR(svn_wc__working_file_writer_install(writer, final_abspath, pool));
+
+  SVN_ERR(svn_stringbuf_from_file2(&actual_content,
+                                   final_abspath,
+                                   pool));
+
+  SVN_TEST_STRING_ASSERT(actual_content->data, "content");
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_working_file_writer_eol_repair(const svn_test_opts_t *opts,
+                                    apr_pool_t *pool)
+{
+  const char *tmp_dir;
+  svn_wc__working_file_writer_t *writer;
+  svn_stream_t *stream;
+  const char *final_abspath;
+  svn_stringbuf_t *actual_content;
+
+  SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir,
+                                    "working_file_writer_eol_repair",
+                                    pool));
+
+  SVN_ERR(svn_wc__working_file_writer_open(&writer, tmp_dir, -1,
+                                           svn_subst_eol_style_fixed, "\r\n",
+                                           TRUE /* repair_eol */,
+                                           NULL, FALSE, FALSE,
+                                           FALSE,
+                                           pool, pool));
+
+  stream = svn_wc__working_file_writer_get_stream(writer);
+  SVN_ERR(svn_stream_puts(stream, "content\n\r\n"));
+  SVN_ERR(svn_stream_close(stream));
+
+  SVN_ERR(svn_wc__working_file_writer_finalize(NULL, NULL, writer, pool));
+  final_abspath = svn_dirent_join(tmp_dir, "file", pool);
+  SVN_ERR(svn_wc__working_file_writer_install(writer, final_abspath, pool));
+
+  SVN_ERR(svn_stringbuf_from_file2(&actual_content,
+                                   final_abspath,
+                                   pool));
+
+  SVN_TEST_STRING_ASSERT(actual_content->data, "content\r\n\r\n");
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_working_file_writer_eol_inconsistent(const svn_test_opts_t *opts,
+                                          apr_pool_t *pool)
+{
+  const char *tmp_dir;
+  svn_wc__working_file_writer_t *writer;
+  svn_stream_t *stream;
+  apr_hash_t *dirents;
+  svn_error_t *err;
+
+  SVN_ERR(svn_test_make_sandbox_dir(&tmp_dir,
+                                    "working_file_writer_eol_inconsistent",
+                                    pool));
+
+  SVN_ERR(svn_wc__working_file_writer_open(&writer, tmp_dir, -1,
+                                           svn_subst_eol_style_fixed, "\r\n",
+                                           FALSE /* repair_eol */,
+                                           NULL, FALSE, FALSE,
+                                           FALSE,
+                                           pool, pool));
+
+  /* With REPAIR_EOL disabled, expect to see an error when the line ending
+     inconsistency is detected by the stream. */
+  stream = svn_wc__working_file_writer_get_stream(writer);
+  err = svn_stream_puts(stream, "content\n\r\n");
+  SVN_TEST_ASSERT_ERROR(err, SVN_ERR_IO_INCONSISTENT_EOL);
+
+  SVN_ERR(svn_wc__working_file_writer_close(writer));
+
+  SVN_ERR(svn_io_get_dirents3(&dirents, tmp_dir, TRUE, pool, pool));
+  SVN_TEST_INT_ASSERT(apr_hash_count(dirents), 0);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_internal_file_modified_keywords(const svn_test_opts_t *opts,
+                                     apr_pool_t *pool)
+{
+  svn_test__sandbox_t b;
+  svn_boolean_t modified;
+  const char *iota_path;
+
+  SVN_ERR(svn_test__sandbox_create(&b, "internal_file_modified_keywords",
+                                   opts, pool));
+  SVN_ERR(sbox_add_and_commit_greek_tree(&b));
+
+  iota_path = sbox_wc_path(&b, "iota");
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  /* Set svn:keywords, edit the file.  The file is modified. */
+  SVN_ERR(sbox_wc_propset(&b, SVN_PROP_KEYWORDS, "Revision", iota_path));
+  SVN_ERR(sbox_file_write(&b, iota_path, "$Revision$\n"));
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(modified);
+
+  /* Commit the changes.  The file is not modified. */
+  SVN_ERR(sbox_wc_commit(&b, ""));
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  /* Manually contract the keywords.  The file is modified, but only
+     if we ask for the exact comparison. */
+  SVN_ERR(sbox_file_write(&b, iota_path, "$Revision$\n"));
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(modified);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_internal_file_modified_eol_style(const svn_test_opts_t *opts,
+                                      apr_pool_t *pool)
+{
+  svn_test__sandbox_t b;
+  svn_boolean_t modified;
+  const char *iota_path;
+
+  SVN_ERR(svn_test__sandbox_create(&b, "internal_file_modified_eol_style",
+                                   opts, pool));
+  SVN_ERR(sbox_add_and_commit_greek_tree(&b));
+
+  iota_path = sbox_wc_path(&b, "iota");
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  /* Set svn:eol-style, edit the file.  The file is modified. */
+  SVN_ERR(sbox_wc_propset(&b, SVN_PROP_EOL_STYLE, "CRLF", iota_path));
+  SVN_ERR(sbox_file_write(&b, iota_path, "contents\r\n"));
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(modified);
+
+  /* Commit the changes.  The file is not modified. */
+  SVN_ERR(sbox_wc_commit(&b, ""));
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  /* Manually change the line ending to LF.  The file is modified, but only
+     if we ask for the exact comparison. */
+  SVN_ERR(sbox_file_write(&b, iota_path, "contents\n"));
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(modified);
+
+  /* Manually change the line ending to CR.  The file is modified, but only
+     if we ask for the exact comparison. */
+  SVN_ERR(sbox_file_write(&b, iota_path, "contents\r"));
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(modified);
+
+  /* Change the line ending back to CRLF.  The file is not modified. */
+  SVN_ERR(sbox_file_write(&b, iota_path, "contents\r\n"));
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  /* Add an empty line and commit.  The file is not modified. */
+  SVN_ERR(sbox_file_write(&b, iota_path, "contents\r\n\r\n"));
+  SVN_ERR(sbox_wc_commit(&b, ""));
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  /* Change one of the line endings to LF.  The file is modified, but only
+     if we ask for the exact comparison. */
+  SVN_ERR(sbox_file_write(&b, iota_path, "contents\n\r\n"));
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, FALSE, pool));
+  SVN_TEST_ASSERT(!modified);
+
+  SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db,
+                                           iota_path, TRUE, pool));
+  SVN_TEST_ASSERT(modified);
+
+  return SVN_NO_ERROR;
+}
+
 /* ---------------------------------------------------------------------- */
 /* The list of test functions */
 
@@ -515,6 +793,16 @@ static struct svn_test_descriptor_t test
                        "test legacy commit2"),
     SVN_TEST_OPTS_PASS(test_internal_file_modified,
                        "test internal_file_modified"),
+    SVN_TEST_OPTS_PASS(test_working_file_writer_simple,
+                       "working file writer simple"),
+    SVN_TEST_OPTS_PASS(test_working_file_writer_eol_repair,
+                       "working file writer eol repair"),
+    SVN_TEST_OPTS_PASS(test_working_file_writer_eol_inconsistent,
+                       "working file writer eol inconsistent"),
+    SVN_TEST_OPTS_PASS(test_internal_file_modified_keywords,
+                       "test internal_file_modified with keywords"),
+    SVN_TEST_OPTS_PASS(test_internal_file_modified_eol_style,
+                       "test internal_file_modified with eol-style"),
     SVN_TEST_NULL
   };
 

Modified: subversion/branches/multi-wc-format/subversion/tests/svn_test.h
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/svn_test.h?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/svn_test.h (original)
+++ subversion/branches/multi-wc-format/subversion/tests/svn_test.h Fri Jan 14 14:01:45 2022
@@ -37,6 +37,7 @@
 #include "svn_error.h"
 #include "svn_string.h"
 #include "svn_auth.h"
+#include "svn_time.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -123,8 +124,17 @@ extern "C" {
                                                                     \
     if (tst_str2 == NULL && tst_str1 == NULL)                       \
       break;                                                        \
-    if ((tst_str1 == NULL) || (tst_str2 == NULL)                    \
-        || (strcmp(tst_str2, tst_str1) != 0))                       \
+    if (tst_str1 == NULL)                                           \
+      return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,           \
+          "Strings not equal\n  Expected: '%s'\n  Found:    NULL"   \
+          "\n  at %s:%d",                                           \
+          tst_str2, __FILE__, __LINE__);                            \
+    if (tst_str2 == NULL)                                           \
+      return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,           \
+          "Strings not equal\n  Expected: NULL\n  Found:    '%s'"   \
+          "\n  at %s:%d",                                           \
+          tst_str1, __FILE__, __LINE__);                            \
+    if (strcmp(tst_str2, tst_str1) != 0)                            \
       return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,           \
           "Strings not equal\n  Expected: '%s'\n  Found:    '%s'"   \
           "\n  at %s:%d",                                           \
@@ -147,6 +157,40 @@ extern "C" {
           tst_int2, tst_int1, __FILE__, __LINE__);                \
   } while(0)
 
+/** Handy macro for testing apr_time_t equality.
+ *
+ * WITHIN_EXPR specifies the proximity of the comparison.
+ */
+#define SVN_TEST_TIME_ASSERT(expr, expected_expr, within_expr)    \
+  do {                                                            \
+    apr_time_t actual_time = (expr);                              \
+    apr_time_t expected_time = (expected_expr);                   \
+    apr_interval_time_t within_time = (within_expr);              \
+                                                                  \
+    if (actual_time < expected_time - within_time ||              \
+        actual_time > expected_time + within_time)                \
+      {                                                           \
+        svn_error_t *err =                                        \
+          svn_error_create(SVN_ERR_TEST_FAILED, NULL, NULL);      \
+                                                                  \
+        err->message = apr_psprintf(                              \
+          err->pool,                                              \
+          "Time values not equal\n"                               \
+          "  Expected: %s (%" APR_TIME_T_FMT ")\n"                \
+          "     Found: %s (%" APR_TIME_T_FMT ")\n"                \
+          " Proximity: %" APR_TIME_T_FMT "ms\n"                   \
+          "  at %s:%d",                                           \
+          svn_time_to_cstring(expected_time, err->pool),          \
+          expected_time,                                          \
+          svn_time_to_cstring(actual_time, err->pool),            \
+          actual_time,                                            \
+          apr_time_as_msec(within_time),                          \
+          __FILE__, __LINE__);                                    \
+                                                                  \
+        return err;                                               \
+      }                                                           \
+  } while(0)
+
 
 /* Baton for any arguments that need to be passed from main() to svn
  * test functions.

Modified: subversion/branches/multi-wc-format/subversion/tests/svn_test_main.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/svn_test_main.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/tests/svn_test_main.c (original)
+++ subversion/branches/multi-wc-format/subversion/tests/svn_test_main.c Fri Jan 14 14:01:45 2022
@@ -880,7 +880,7 @@ svn_test_main(int argc, const char *argv
       _set_error_mode(_OUT_TO_STDERR);
 
       /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr.
-         (Ignored in releas builds) */
+         (Ignored in release builds) */
       _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR);
       _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
       _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
@@ -1089,9 +1089,9 @@ svn_test_main(int argc, const char *argv
               svn_pool_clear(cleanup_pool);
             }
         }
-#if APR_HAS_THREADS
       else
         {
+#if APR_HAS_THREADS
           got_error = do_tests_concurrently(opts.prog_name, test_funcs,
                                             array_size, max_threads,
                                             &opts, test_pool);
@@ -1099,8 +1099,11 @@ svn_test_main(int argc, const char *argv
           /* Execute all cleanups */
           svn_pool_clear(test_pool);
           svn_pool_clear(cleanup_pool);
-        }
+#else
+          /* Can't happen */
+          SVN_ERR_MALFUNCTION();
 #endif
+        }
     }
 
   /* Clean up APR */

Modified: subversion/branches/multi-wc-format/tools/backup/hot-backup.py.in
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/tools/backup/hot-backup.py.in?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/tools/backup/hot-backup.py.in (original)
+++ subversion/branches/multi-wc-format/tools/backup/hot-backup.py.in Fri Jan 14 14:01:45 2022
@@ -36,6 +36,8 @@
 ######################################################################
 
 import sys, os, getopt, stat, re, time, shutil, subprocess
+import locale
+import functools
 
 ######################################################################
 # Global Settings
@@ -193,7 +195,7 @@ def comparator(a, b):
     if not inca:
       return -1
     elif not incb:
-      return 1;
+      return 1
     elif (int(inca) < int(incb)):
       return -1
     else:
@@ -203,10 +205,18 @@ def get_youngest_revision():
   """Examine the repository REPO_DIR using the svnlook binary
   specified by SVNLOOK, and return the youngest revision."""
 
-  p = subprocess.Popen([svnlook, 'youngest', '--', repo_dir],
-                       stdin=subprocess.PIPE,
-                       stdout=subprocess.PIPE,
-                       stderr=subprocess.PIPE)
+  if b'' == '':
+    p = subprocess.Popen([svnlook, 'youngest', '--', repo_dir],
+                         stdin=subprocess.PIPE,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE)
+  else:
+    p = subprocess.Popen([svnlook, 'youngest', '--', repo_dir],
+                         stdin=subprocess.PIPE,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE,
+                         errors='backslashreplace', # foolproof
+                         encoding=locale.getpreferredencoding())
   infile, outfile, errfile = p.stdin, p.stdout, p.stderr
 
   stdout_lines = outfile.readlines()
@@ -255,7 +265,7 @@ regexp = re.compile("^" + re.escape(repo
 directory_list = os.listdir(backup_dir)
 young_list = [x for x in directory_list if regexp.search(x)]
 if young_list:
-  young_list.sort(comparator)
+  young_list.sort(key = functools.cmp_to_key(comparator))
   increment = regexp.search(young_list.pop()).groupdict()['increment']
   if increment:
     backup_subdir = os.path.join(backup_dir, repo + "-" + youngest + "-"
@@ -348,7 +358,7 @@ if num_backups > 0:
   regexp = re.compile("^" + re.escape(repo) + "-[0-9]+(-[0-9]+)?" + ext_re + "$")
   directory_list = os.listdir(backup_dir)
   old_list = [x for x in directory_list if regexp.search(x)]
-  old_list.sort(comparator)
+  old_list.sort(key = functools.cmp_to_key(comparator))
   del old_list[max(0,len(old_list)-num_backups):]
   for item in old_list:
     old_backup_item = os.path.join(backup_dir, item)

Modified: subversion/branches/multi-wc-format/tools/buildbot/slaves/README
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/tools/buildbot/slaves/README?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/tools/buildbot/slaves/README (original)
+++ subversion/branches/multi-wc-format/tools/buildbot/slaves/README Fri Jan 14 14:01:45 2022
@@ -63,7 +63,7 @@ branches/1.4.x. If the buildslave is not
 stored in the queue and started when the buildslave connects.
 
 Normally each build only includes the changes of one commit. However, when 
-multiple changes arive during a previous build, those will be combined in 
+multiple changes arrive during a previous build, those will be combined in 
 a next build.
 
 The buildmaster looks at each commit and decides if they are important enough

Modified: subversion/branches/multi-wc-format/tools/buildbot/slaves/bb-openbsd/svnbuild.sh
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/tools/buildbot/slaves/bb-openbsd/svnbuild.sh?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/tools/buildbot/slaves/bb-openbsd/svnbuild.sh (original)
+++ subversion/branches/multi-wc-format/tools/buildbot/slaves/bb-openbsd/svnbuild.sh Fri Jan 14 14:01:45 2022
@@ -22,8 +22,10 @@
 set -e
 set -x
 
+(test -h ../GNUmakefile || ln -s ../unix-build/Makefile.svn ../GNUmakefile)
+(cd .. && gmake dirs-create fetch)
 url="$(svn info --show-item url)"
 branch="${url##*/}"
-(test -h ../GNUmakefile || ln -s ../unix-build/Makefile.svn ../GNUmakefile)
+mkdir -p ../objdir/svn-${branch}
 touch ../objdir/svn-${branch}/.retrieved
-(cd .. && gmake BRANCH="$branch" THREADING="no" JAVA="no" MAKE_JOBS=8)
+(cd .. && gmake BRANCH="$branch" THREADING="no" JAVA="no" MAKE_JOBS=2)