svn commit: r1925915 - in /subversion/branches/utf8-cmdline-prototype: ./ build/ subversion/include/ subversion/libsvn_subr/ subversion/svnlook/ subversion/svnmucc/ subversion/tests/cmdline/ subversion/tests/cmdline/svntest/

[email protected]
Newsgroups gmane.comp.version-control.subversion.svn
Message-ID <[email protected]>
Author: rinrab
Date: Wed May 28 19:32:09 2025
New Revision: 1925915

URL: http://svn.apache.org/viewvc?rev=1925915&view=rev
Log:
On the 'utf8-cmdline-prototype' branch: sync with trunk@r1925914.

Modified:
    subversion/branches/utf8-cmdline-prototype/   (props changed)
    subversion/branches/utf8-cmdline-prototype/CMakeLists.txt
    subversion/branches/utf8-cmdline-prototype/build/run_tests.py
    subversion/branches/utf8-cmdline-prototype/subversion/include/svn_opt.h
    subversion/branches/utf8-cmdline-prototype/subversion/libsvn_subr/opt.c
    subversion/branches/utf8-cmdline-prototype/subversion/svnlook/svnlook.c
    subversion/branches/utf8-cmdline-prototype/subversion/svnmucc/svnmucc.c
    subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/prop_tests.py
    subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svnmucc_tests.py
    subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/actions.py
    subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/main.py
    subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/verify.py

Propchange: subversion/branches/utf8-cmdline-prototype/
------------------------------------------------------------------------------
  Merged /subversion/trunk:r1925721-1925914

Modified: subversion/branches/utf8-cmdline-prototype/CMakeLists.txt
URL: http://svn.apache.org/viewvc/subversion/branches/utf8-cmdline-prototype/CMakeLists.txt?rev=1925915&r1=1925914&r2=1925915&view=diff
==============================================================================
--- subversion/branches/utf8-cmdline-prototype/CMakeLists.txt (original)
+++ subversion/branches/utf8-cmdline-prototype/CMakeLists.txt Wed May 28 19:32:09 2025
@@ -83,6 +83,7 @@ cmake_dependent_option(SVN_ENABLE_TOOLS
 option(SVN_ENABLE_TESTS "Build Subversion test-suite" OFF)
 option(SVN_TEST_EXPAND "Expand tests; This will slow-down configuration, but you will have an ability to run any subtest" OFF)
 option(SVN_TEST_CONFIGURE_FOR_PARALLEL "Configures tests for parallel run execution" OFF)
+set(SVN_TEST_COMMAND_ARGUMENTS "" CACHE STRING "Additional command line options to be passed to run_tests.py")
 option(SVN_ENABLE_APACHE_MODULES "Build modules for Apache HTTPD" OFF)
 
 option(SVN_ENABLE_SWIG_PERL "Enable Subversion SWIG bindings for Perl" OFF)
@@ -778,12 +779,28 @@ if(SVN_ENABLE_TESTS)
   find_package(Python3 COMPONENTS Interpreter REQUIRED)
   set(run_tests_script "${CMAKE_CURRENT_SOURCE_DIR}/build/run_tests.py")
   set(list_tests_script "${CMAKE_CURRENT_SOURCE_DIR}/build/list_tests.py")
+  set(test_base_dir "${CMAKE_CURRENT_BINARY_DIR}/Testing")
+
+  # Create the virtual environment for Python tests.
+  execute_process(
+    COMMAND
+      "${Python3_EXECUTABLE}" "${run_tests_script}"
+      --create-python-venv "${test_base_dir}"
+      ${CMAKE_CURRENT_SOURCE_DIR}
+      OUTPUT_VARIABLE command_output
+      WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+      RESULT_VARIABLE command_result
+  )
+  if (command_result)
+    message(FATAL_ERROR "run_tests.py --create-python-venv failed.")
+  endif()
+  string(STRIP "${command_output}" python3_test_executable)
 
   function(add_py_test name prog)
     if(SVN_TEST_CONFIGURE_FOR_PARALLEL)
-      set(test_root "${CMAKE_CURRENT_BINARY_DIR}/Testing/${name}")
+      set(test_root "${test_base_dir}/${name}")
     else()
-      set(test_root "${CMAKE_CURRENT_BINARY_DIR}/Testing")
+      set(test_root "${test_base_dir}")
     endif()
 
     file(MAKE_DIRECTORY "${test_root}/subversion/tests/cmdline")
@@ -792,12 +809,13 @@ if(SVN_ENABLE_TESTS)
       NAME
         "${name}"
       COMMAND
-        "${Python3_EXECUTABLE}" "${run_tests_script}"
+        "${python3_test_executable}" "${run_tests_script}"
         --bin ${binary_dir}
         --tools-bin ${binary_dir}
         --verbose
         --log-to-stdout
         --set-log-level=WARNING
+        ${SVN_TEST_COMMAND_ARGUMENTS}
         ${CMAKE_CURRENT_SOURCE_DIR}
         ${test_root}
         "${prog}"

Modified: subversion/branches/utf8-cmdline-prototype/build/run_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/utf8-cmdline-prototype/build/run_tests.py?rev=1925915&r1=1925914&r2=1925915&view=diff
==============================================================================
--- subversion/branches/utf8-cmdline-prototype/build/run_tests.py (original)
+++ subversion/branches/utf8-cmdline-prototype/build/run_tests.py Wed May 28 19:32:09 2025
@@ -259,6 +259,8 @@ class TestHarness:
       cmdline.append('--tools-bin=%s' % self.opts.tools_bin)
     if self.opts.svn_bin is not None:
       cmdline.append('--bin=%s' % self.opts.svn_bin)
+    if self.opts.venv_base is not None:
+      cmdline.append('--python-venv=%s' % self.opts.venv_base)
     if self.opts.url is not None:
       cmdline.append('--url=%s' % self.opts.url)
     if self.opts.fs_type is not None:
@@ -326,16 +328,17 @@ class TestHarness:
 
       global svntest
       svntest = importlib.import_module('svntest')
-      extra_packages = svntest.main.ensure_dependencies()
       svntest.main.parse_options(cmdline, optparse.SUPPRESS_USAGE)
       svntest.testcase.TextColors.disable()
+      dependency_path = svntest.main.ensure_dependencies()
 
       # We have to update PYTHONPATH, otherwise the whole setting up of a
       # virtualenv and installing dependencies will happen for every test case.
-      python_path = os.environ.get("PYTHONPATH")
-      python_path = (extra_packages if not python_path
-                     else "%s:%s" % (extra_packages, python_path))
-      os.environ["PYTHONPATH"] = python_path
+      if dependency_path:
+        python_path = os.environ.get("PYTHONPATH")
+        python_path = (dependency_path if not python_path
+                       else "%s:%s" % (dependency_path, python_path))
+        os.environ["PYTHONPATH"] = python_path
     finally:
       os.chdir(old_cwd)
 
@@ -674,6 +677,12 @@ class TestHarness:
       for x in failed_list:
         sys.stdout.write(x)
 
+    xml_error_list = [x for x in log_lines if x[:8] == 'E: XML: ']
+    if xml_error_list:
+      print('There were some XML validation errors, checking' + self.logfile)
+      for x in sorted(set(xml_error_list)):
+        sys.stdout.write(x[3:])
+
     # Print summaries, from least interesting to most interesting.
     if self.opts.list_tests:
       print('Summary of test listing:')
@@ -1032,6 +1041,13 @@ def create_parser():
                     help='Use the svn binaries installed in this path')
   parser.add_option('--tools-bin', action='store', dest='tools_bin',
                     help='Use the svn tools installed in this path')
+  parser.add_option('--create-python-venv', action='store', dest='create_venv',
+                    help=('Create the Python virtual environment inside this'
+                          ' path and install the dependencies used by the'
+                          ' test suite, then exit. Do not run any tests.'))
+  parser.add_option('--python-venv', action='store', dest='venv_base',
+                    help=('Use the virtual environment inside this path to'
+                          ' find the dependencies used by the test suite.'))
   parser.add_option('--fsfs-sharding', action='store', type='int',
                     help='Default shard size (for fsfs)')
   parser.add_option('--fsfs-packing', action='store_true',
@@ -1097,12 +1113,22 @@ def create_parser():
 
 def main():
   (opts, args) = create_parser().parse_args(sys.argv[1:])
+  if opts.create_venv:
+    main_create_venv(opts, args)
+    sys.exit(0)
+
+  # Normal mode: don't create a virtual environment, run tests or whatever
+  # else was requested instead. Create the virtual environment on demand.
+  assert not opts.create_venv
 
   if len(args) < 3:
     print("{}: at least three positional arguments required; got {!r}".format(
       os.path.basename(sys.argv[0]), args
     ))
     sys.exit(2)
+  abs_srcdir = args[0]
+  abs_builddir = args[1]
+  programs = args[2:]
 
   if opts.log_to_stdout:
     logfile = None
@@ -1111,11 +1137,30 @@ def main():
     logfile = os.path.abspath('tests.log')
     faillogfile = os.path.abspath('fails.log')
 
-  th = TestHarness(args[0], args[1], logfile, faillogfile, opts)
-  failed = th.run(args[2:])
+  th = TestHarness(abs_srcdir, abs_builddir, logfile, faillogfile, opts)
+  failed = th.run(programs)
   if failed:
     sys.exit(1)
 
+def main_create_venv(opts, args):
+  # Environment creation mode: create the requested virtual environment,
+  # install required dependencies and exit.
+  assert opts.create_venv
+
+  if len(args) < 1:
+    print("{}: at least one positional argument required; got {!r}".format(
+      os.path.basename(sys.argv[0]), args
+    ))
+    sys.exit(2)
+  abs_srcdir = args[0]
+
+  sys.path.insert(0, os.path.join(abs_srcdir, "subversion", "tests", "cmdline"))
+  svntest = importlib.import_module("svntest")
+  svntest.main.venv_base = opts.create_venv
+  venv_dir = svntest.main.venv_path()
+  python_prog, _ = svntest.main.create_python_venv(venv_dir, quiet=True)
+  print(python_prog)
+
 
 # Run main if not imported as a module
 if __name__ == '__main__':

Modified: subversion/branches/utf8-cmdline-prototype/subversion/include/svn_opt.h
URL: http://svn.apache.org/viewvc/subversion/branches/utf8-cmdline-prototype/subversion/include/svn_opt.h?rev=1925915&r1=1925914&r2=1925915&view=diff
==============================================================================
--- subversion/branches/utf8-cmdline-prototype/subversion/include/svn_opt.h (original)
+++ subversion/branches/utf8-cmdline-prototype/subversion/include/svn_opt.h Wed May 28 19:32:09 2025
@@ -482,6 +482,24 @@ typedef struct svn_opt_revision_range_t
   svn_opt_revision_t end;
 } svn_opt_revision_range_t;
 
+
+/**
+ * Parse NULL-terminated C string @a str as a revision number and
+ * store its value in @a rev.
+ *
+ * If @a str is not a valid revision number, then the error
+ * #SVN_ERR_REVNUM_PARSE_FAILURE error is returned.  Negative numbers
+ * parsed from @a str are considered invalid, and result in the same error.
+ *
+ * Unlike svn_revnum_parse(), this function support our cmdline revision
+ * number format, whereas the revnum may be prefixed with an 'r' symbol.
+ *
+ * @since New in 1.15
+ * @see svn_revnum_parse()
+ */
+svn_error_t *
+svn_opt_parse_revnum(svn_revnum_t *rev, const char *str);
+
 /**
  * Set @a *start_revision and/or @a *end_revision according to @a arg,
  * where @a arg is "N" or "N:M", like so:
@@ -553,9 +571,10 @@ svn_opt_parse_revision_to_range(apr_arra
  *
  * @since New in 1.15.
  */
-int svn_opt_parse_change_to_range(apr_array_header_t *opt_ranges,
-                                  const char *arg,
-                                  apr_pool_t *result_pool);
+int
+svn_opt_parse_change_to_range(apr_array_header_t *opt_ranges,
+                              const char *arg,
+                              apr_pool_t *result_pool);
 
 /**
  * Resolve peg revisions and operational revisions in the following way:

Modified: subversion/branches/utf8-cmdline-prototype/subversion/libsvn_subr/opt.c
URL: http://svn.apache.org/viewvc/subversion/branches/utf8-cmdline-prototype/subversion/libsvn_subr/opt.c?rev=1925915&r1=1925914&r2=1925915&view=diff
==============================================================================
--- subversion/branches/utf8-cmdline-prototype/subversion/libsvn_subr/opt.c (original)
+++ subversion/branches/utf8-cmdline-prototype/subversion/libsvn_subr/opt.c Wed May 28 19:32:09 2025
@@ -710,6 +710,16 @@ int svn_opt_parse_change_to_range(apr_ar
 }
 
 svn_error_t *
+svn_opt_parse_revnum(svn_revnum_t *rev, const char *str)
+{
+  /* Allow any number of 'r's to prefix a revision number. */
+  while (*str == 'r')
+    str++;
+
+  return svn_error_trace(svn_revnum_parse(rev, str, NULL));
+}
+
+svn_error_t *
 svn_opt_resolve_revisions(svn_opt_revision_t *peg_rev,
                           svn_opt_revision_t *op_rev,
                           svn_boolean_t is_url,

Modified: subversion/branches/utf8-cmdline-prototype/subversion/svnlook/svnlook.c
URL: http://svn.apache.org/viewvc/subversion/branches/utf8-cmdline-prototype/subversion/svnlook/svnlook.c?rev=1925915&r1=1925914&r2=1925915&view=diff
==============================================================================
--- subversion/branches/utf8-cmdline-prototype/subversion/svnlook/svnlook.c (original)
+++ subversion/branches/utf8-cmdline-prototype/subversion/svnlook/svnlook.c Wed May 28 19:32:09 2025
@@ -2528,7 +2528,7 @@ sub_main(int *exit_code,
       switch (opt_id)
         {
         case 'r':
-          SVN_ERR(svn_revnum_parse(&opt_state.rev, utf8_opt_arg, NULL));
+          SVN_ERR(svn_opt_parse_revnum(&opt_state.rev, utf8_opt_arg));
           break;
 
         case 't':

Modified: subversion/branches/utf8-cmdline-prototype/subversion/svnmucc/svnmucc.c
URL: http://svn.apache.org/viewvc/subversion/branches/utf8-cmdline-prototype/subversion/svnmucc/svnmucc.c?rev=1925915&r1=1925914&r2=1925915&view=diff
==============================================================================
--- subversion/branches/utf8-cmdline-prototype/subversion/svnmucc/svnmucc.c (original)
+++ subversion/branches/utf8-cmdline-prototype/subversion/svnmucc/svnmucc.c Wed May 28 19:32:09 2025
@@ -429,7 +429,7 @@ log_message_func(const char **log_msg,
       svn_string_t *message = svn_string_create(lmb->log_message, pool);
 
       SVN_ERR_W(svn_subst_translate_string2(&message, NULL, NULL,
-                                            message, NULL, FALSE,
+                                            message, "UTF-8", FALSE,
                                             pool, pool),
                 _("Error normalizing log message to internal format"));
 
@@ -576,6 +576,7 @@ sub_main(int *exit_code,
             const char *filename;
             SVN_ERR(svn_utf_cstring_to_utf8(&filename, arg, pool));
             SVN_ERR(svn_stringbuf_from_file2(&filedata, filename, pool));
+            SVN_ERR(svn_utf_stringbuf_to_utf8(&filedata, filedata, pool));
           }
           break;
         case 'u':
@@ -595,19 +596,7 @@ sub_main(int *exit_code,
           root_url = sanitize_url(root_url, pool);
           break;
         case 'r':
-          {
-            const char *saved_arg = arg;
-            char *digits_end = NULL;
-            while (*arg == 'r')
-              arg++;
-            base_revision = strtol(arg, &digits_end, 10);
-            if ((! SVN_IS_VALID_REVNUM(base_revision))
-                || (! digits_end)
-                || *digits_end)
-              return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-                                       _("Invalid revision number '%s'"),
-                                       saved_arg);
-          }
+          SVN_ERR(svn_opt_parse_revnum(&base_revision, arg));
           break;
         case with_revprop_opt:
           SVN_ERR(svn_utf_cstring_to_utf8(&opt_arg, arg, pool));
@@ -774,7 +763,7 @@ sub_main(int *exit_code,
 
   lmb.non_interactive = non_interactive;
   lmb.ctx = ctx;
-    /* Make sure we have a log message to use. */
+  /* Make sure we have a log message to use. */
   SVN_ERR(sanitize_log_sources(&lmb.log_message, message, revprops, filedata,
                                pool, pool));
 
@@ -822,23 +811,11 @@ sub_main(int *exit_code,
       if (action->action == ACTION_CP)
         {
           const char *rev_str = APR_ARRAY_IDX(action_args, i, const char *);
-          if (strcmp(rev_str, "head") == 0)
-            action->rev = SVN_INVALID_REVNUM;
-          else if (strcmp(rev_str, "HEAD") == 0)
+          if (svn_cstring_casecmp(rev_str, "head") == 0)
             action->rev = SVN_INVALID_REVNUM;
           else
-            {
-              char *end;
-
-              while (*rev_str == 'r')
-                ++rev_str;
+            SVN_ERR(svn_opt_parse_revnum(&action->rev, rev_str));
 
-              action->rev = strtol(rev_str, &end, 0);
-              if (*end)
-                return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
-                                         "'%s' is not a revision\n",
-                                         rev_str);
-            }
           if (++i == action_args->nelts)
             return insufficient();
         }

Modified: subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/prop_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/prop_tests.py?rev=1925915&r1=1925914&r2=1925915&view=diff
==============================================================================
--- subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/prop_tests.py (original)
+++ subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/prop_tests.py Wed May 28 19:32:09 2025
@@ -2640,6 +2640,9 @@ def xml_unsafe_author(sbox):
                                      wc_dir)
 
 @Issue(4415)
+@Issue(4919)
+@XFail(lambda: (svntest.main.is_bad_xml_fatal()
+                and not svntest.main.is_ra_type_dav()))
 def xml_unsafe_author2(sbox):
   "svn:author with XML unsafe chars 2"
 
@@ -2666,6 +2669,13 @@ def xml_unsafe_author2(sbox):
     expected_author = 'foo\bbar'
 
   # Use svn ls in --xml mode to test locale independent output.
+  # FIXME: Theat literal \b in the author field is invalid XML.
+  #        Should be encoded as a character entity and enclosed
+  #        in a CDATA section, like this:
+  #
+  #            <[CDATA[foo@#08;bar]]>
+  #        or
+  #            foo<[CDATA[@#08;]]>bar
   expected_output = [
     '<?xml version="1.0" encoding="UTF-8"?>\n',
     '<lists>\n',
@@ -2694,8 +2704,8 @@ def xml_unsafe_author2(sbox):
     '</lists>\n'
     ]
 
-  svntest.actions.run_and_verify_svn(expected_output, [],
-                                     'ls', '--xml', repo_url)
+  svntest.actions.run_and_verify_svn_xml(expected_output, [],
+                                         'list', '--xml', repo_url)
 
   expected_info = [{
       'Repository Root' : sbox.repo_url,

Modified: subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svnmucc_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svnmucc_tests.py?rev=1925915&r1=1925914&r2=1925915&view=diff
==============================================================================
--- subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svnmucc_tests.py (original)
+++ subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svnmucc_tests.py Wed May 28 19:32:09 2025
@@ -313,7 +313,7 @@ def basic_svnmucc(sbox):
 
   # Expected missing revision error
   xtest_svnmucc(sbox.repo_url,
-                ["svnmucc: E200004: 'a' is not a revision"
+                ["svnmucc: E200022: Invalid revision number found parsing 'a'"
                  ], #---------
                 '-m', 'log msg',
                 'cp', 'a', 'b')

Modified: subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/actions.py
URL: http://svn.apache.org/viewvc/subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/actions.py?rev=1925915&r1=1925914&r2=1925915&view=diff
==============================================================================
--- subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/actions.py (original)
+++ subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/actions.py Wed May 28 19:32:09 2025
@@ -797,11 +797,11 @@ def run_and_verify_log_xml(expected_log_
   # We'll parse the output unless the caller specifies expected_stderr or
   # expected_stdout for run_and_verify_svn.
   parse = True
-  if expected_stderr == None:
+  if expected_stderr is None:
     expected_stderr = []
   else:
     parse = False
-  if expected_stdout != None:
+  if expected_stdout is not None:
     parse = False
 
   log_args = list(args)
@@ -813,6 +813,7 @@ def run_and_verify_log_xml(expected_log_
     'log', '--xml', *log_args)
   if not parse:
     return
+  verify.validate_xml_schema('log', stdout)
 
   entries = LogParser().parse(stdout)
   for index in range(len(entries)):
@@ -1647,9 +1648,9 @@ def run_and_verify_status_xml(expected_e
 
   exit_code, output, errput = run_and_verify_svn(None, [],
                                                  'status', '--xml', *args)
-
   if len(errput) > 0:
     raise Failure
+  verify.validate_xml_schema('status', output)
 
   doc = parseString(''.join(output))
   entries = doc.getElementsByTagName('entry')
@@ -1726,6 +1727,7 @@ def run_and_verify_inherited_prop_xml(pa
 
   if len(errput) > 0:
     raise Failure
+  ## FIXME: Need XML schema: verify.validate_xml_schema('props', output)
 
   # Props inherited from within the WC are keyed on absolute paths.
   expected_iprops = {}
@@ -1798,10 +1800,10 @@ def run_and_verify_diff_summarize_xml(er
                                                  'diff', '--summarize',
                                                  '--xml', *args)
 
-
   # Return if errors are present since they were expected
   if len(errput) > 0:
     return
+  verify.validate_xml_schema('diff', output)
 
   doc = parseString(''.join(output))
   paths = doc.getElementsByTagName("path")

Modified: subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/main.py
URL: http://svn.apache.org/viewvc/subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/main.py?rev=1925915&r1=1925914&r2=1925915&view=diff
==============================================================================
--- subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/main.py (original)
+++ subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/main.py Wed May 28 19:32:09 2025
@@ -36,6 +36,7 @@ import xml
 import urllib
 import logging
 import hashlib
+import importlib
 import zipfile
 import codecs
 import queue
@@ -222,11 +223,13 @@ work_dir = "svn-test-work"
 
 # Directory for the Python virtual environment where we install
 # external dependencies of the test environment
-venv_dir = os.path.join(work_dir, "__venv__")
+venv_base = work_dir
+venv_path = lambda: os.path.join(venv_base, "__venv__")
+venv_create = True
 
 # List of dependencies
+found_dependencies = set()
 SVN_TESTS_REQUIRE = ["lxml", "rnc2rng"]
-dependencies_ensured = False
 
 # Constant for the merge info property.
 SVN_PROP_MERGEINFO = "svn:mergeinfo"
@@ -1753,6 +1756,13 @@ def is_httpd_authz_provider_enabled():
 def is_remote_http_connection_allowed():
   return options.allow_remote_http_connection
 
+# XML schema validation
+def is_bad_xml_fatal():
+  """Are we treating invalid XML output as a fatal error?"""
+  # Only if we have all the necessary dependencies.
+  return {'lxml', 'rnc2rnd'} & found_dependencies
+
+
 def wc_format(ver=None):
   """Return the WC format number used by Subversion version VER.
 
@@ -1856,6 +1866,8 @@ class TestSpawningThread(threading.Threa
       args.append('--allow-remote-http-connection')
     if options.svn_bin:
       args.append('--bin=' + options.svn_bin)
+    if options.venv_base:
+      args.append('--python-venv=' + options.venv_base)
     if options.store_pristine:
       args.append('--store-pristine=' + options.store_pristine)
     if options.valgrind:
@@ -2230,6 +2242,9 @@ def _create_parser(usage=None):
                     help='Whether to clean up')
   parser.add_option('--enable-sasl', action='store_true',
                     help='Whether to enable SASL authentication')
+  parser.add_option('--python-venv', action='store', dest='venv_base',
+                    help=('Use the virtual environment inside this path to'
+                          ' find the dependencies used by the test suite.'))
   parser.add_option('--bin', action='store', dest='svn_bin',
                     help='Use the svn binaries installed in this path')
   parser.add_option('--use-jsvn', action='store_true',
@@ -2336,6 +2351,8 @@ def parse_options(arglist=sys.argv[1:],
   """Parse the arguments in arg_list, and set the global options object with
      the results"""
 
+  global venv_base
+  global venv_create
   global options
 
   parser = _create_parser(usage)
@@ -2385,7 +2402,9 @@ def parse_options(arglist=sys.argv[1:],
                     svn_wc__max_supported_format_version(),
                     options.wc_format_version))
 
-  pass
+  if options.venv_base:
+    venv_base = options.venv_base
+    venv_create = False
 
   return (parser, args)
 
@@ -2417,7 +2436,6 @@ def run_tests(test_list, serial_only = F
         appropriate exit code.
   """
 
-  ensure_dependencies()
   sys.exit(execute_tests(test_list, serial_only))
 
 def ensure_dependencies():
@@ -2428,18 +2446,42 @@ def ensure_dependencies():
         upgrade the venv in that case. In practice, we won't.
   """
 
-  global dependencies_ensured
-  if dependencies_ensured:
-    return
-
+  venv_dir = os.path.abspath(venv_path())
   package_path = os.path.join(venv_dir, "lib",
                               "python%d.%d" % sys.version_info[:2],
                               "site-packages")
-  package_path = os.path.abspath(package_path)
-  if package_path in sys.path:
-    dependencies_ensured = True
-    return
 
+  # Check if all our dependencies are installed. It doesn't matter if
+  # they're installed in our venv, as long as they're available.
+  found_dependencies.clear()
+  saved_sys_path = sys.path[:]
+  try:
+    sys.path.insert(0, package_path)
+    for package in SVN_TESTS_REQUIRE:
+      importlib.import_module(package)
+      found_dependencies.add(package)
+    have_required = True
+  except ImportError:
+    have_required = False
+  finally:
+    sys.path[:] = saved_sys_path
+
+  if have_required:
+    if package_path not in sys.path:
+      sys.path.append(package_path)
+    return package_path
+
+  if venv_create:
+    python_prog, python_path = create_python_venv(venv_dir)
+    if python_prog is not None:
+      assert python_path == package_path
+      if package_path not in sys.path:
+        sys.path.append(package_path)
+      found_dependencies.update(set(SVN_TESTS_REQUIRE))
+      return package_path
+  return None
+
+def create_python_venv(venv_dir, quiet=False):
   try:
     # Create the virtual environment
     if not os.path.isdir(venv_dir):
@@ -2447,19 +2489,22 @@ def ensure_dependencies():
         safe_rmtree(venv_dir)
       venv.create(venv_dir, with_pip=True)
 
-    # Install any (new) dependencies
-    pip = os.path.join(venv_dir, venv_bin, "pip"+_exe)
+    # Install the dependencies
+    pip = os.path.join(venv_dir, venv_bin, "pip" + _exe)
     pip_options = ("--disable-pip-version-check", "--require-virtualenv")
     subprocess.run([pip, *pip_options, "install", *SVN_TESTS_REQUIRE],
-                   check=True)
+                   check=True, stdout=subprocess.PIPE if quiet else None)
+    importlib.invalidate_caches()
 
-    sys.path.append(package_path)
-    dependencies_ensured = True
-    return package_path
-  except Exception as ex:
-    print("WARNING: Could not install test dependencies,"
-          " some tests will be skipped", file=sys.stderr)
-    print(ex, file=sys.stderr)
+    python_prog = os.path.join(venv_dir, venv_bin, "python" + _exe)
+    python_path = os.path.join(venv_dir, "lib",
+                               "python%d.%d" % sys.version_info[:2],
+                               "site-packages")
+    return python_prog, python_path
+  except Exception:
+    if logger:
+      logger.warning('Could not install test dependencies', exc_info=True)
+    return None, None
 
 def get_issue_details(issue_numbers):
   """For each issue number in ISSUE_NUMBERS query the issue
@@ -2659,6 +2704,10 @@ def execute_tests(test_list, serial_only
     wc_incomplete_tester_binary = os.path.join(options.tools_bin,
                                                'wc-incomplete-tester' + _exe)
 
+  assert options.venv_base is None or venv_base == options.venv_base, \
+    'venv_base=%s options.venv_base=%s' % (venv_base, options.venv_base)
+  ensure_dependencies()
+
   ######################################################################
 
   # Cleanup: if a previous run crashed or interrupted the python

Modified: subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/verify.py
URL: http://svn.apache.org/viewvc/subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/verify.py?rev=1925915&r1=1925914&r2=1925915&view=diff
==============================================================================
--- subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/verify.py (original)
+++ subversion/branches/utf8-cmdline-prototype/subversion/tests/cmdline/svntest/verify.py Wed May 28 19:32:09 2025
@@ -1062,8 +1062,14 @@ def validate_xml_schema(name: str, lines
     source = ''.join(lines)
     document = etree.parse(BytesIO(source.encode("utf-8")))
     if not schema.validate(document):
-      raise SVNXMLSchemaValidationError("schema %s" % schema_name)
-  except Exception:
-    print("ERROR: XML output does not conform to schema", schema_name)
-    print(source)
-    raise
+      raise SVNXMLSchemaValidationError(schema.error_log)
+  except ImportError:
+    logger.error("XML: Module lxml.etree not found")
+    return
+  except Exception as ex:
+    logger.error("XML: " + str(ex))
+    logger.warning("XML:\n" + "\n".join(repr(line) for line in lines))
+    if svntest.main.is_bad_xml_fatal():
+      raise
+    else:
+      logger.warning("XML:", exc_info=True)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.