You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/11/09 17:47:34 UTC

svn commit: r1033086 - in /subversion/trunk: ./ build/run_tests.py subversion/tests/cmdline/svntest/main.py

Author: hwright
Date: Tue Nov  9 16:47:34 2010
New Revision: 1033086

URL: http://svn.apache.org/viewvc?rev=1033086&view=rev
Log:
Merge the py-tests-as-modules branch to trunk.

This change makes the tests, when run from 'make check' and friends, load into
the same process as the test runner, rather than spawning off a separate
process.  The developer-visible change here is that the dotted leader on the
'make check' output is now a progress bar.

Modified:
    subversion/trunk/   (props changed)
    subversion/trunk/build/run_tests.py
    subversion/trunk/subversion/tests/cmdline/svntest/main.py

Propchange: subversion/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Tue Nov  9 16:47:34 2010
@@ -24,6 +24,7 @@
 /subversion/branches/merge-skips-obstructions:874525-874615
 /subversion/branches/nfc-nfd-aware-client:870276,870376
 /subversion/branches/performance:982355,983764,983766,984927,984984,985014,985037,985046,985472,985477,985482,985500,985606,985669,986453,987888,987893,995507,995603,1001413,1025660,1028092,1028094,1028104,1029038,1029042,1029090,1029092,1029335,1030763
+/subversion/branches/py-tests-as-modules:956579-1033052
 /subversion/branches/ra_serf-digest-authn:875693-876404
 /subversion/branches/reintegrate-improvements:873853-874164
 /subversion/branches/subtree-mergeinfo:876734-878766

Modified: subversion/trunk/build/run_tests.py
URL: http://svn.apache.org/viewvc/subversion/trunk/build/run_tests.py?rev=1033086&r1=1033085&r2=1033086&view=diff
==============================================================================
--- subversion/trunk/build/run_tests.py (original)
+++ subversion/trunk/build/run_tests.py Tue Nov  9 16:47:34 2010
@@ -42,9 +42,9 @@ separated list of test numbers; the defa
 '''
 
 # A few useful constants
-LINE_LENGTH = 40
+LINE_LENGTH = 45
 
-import os, re, subprocess, sys
+import os, re, subprocess, sys, imp
 from datetime import datetime
 
 import getopt
@@ -237,43 +237,14 @@ class TestHarness:
       self.log.close()
       self.log = None
 
-  def _run_test(self, prog, test_nr, total_tests):
-    "Run a single test. Return the test's exit code."
-
-    if self.log:
-      log = self.log
-    else:
-      log = sys.stdout
-
-    test_nums = None
-    if '#' in prog:
-      prog, test_nums = prog.split('#')
-
+  def _run_c_test(self, prog, test_nums, dot_count):
+    'Run a c test, escaping parameters as required.'
     progdir, progbase = os.path.split(prog)
-    if self.log:
-      # Using write here because we don't want even a trailing space
-      test_info = '%s [%d/%d]' % (progbase, test_nr + 1, total_tests)
-      sys.stdout.write('Running tests in %s' % (test_info, ))
-      sys.stdout.write('.'*(LINE_LENGTH - len(test_info)))
-      sys.stdout.flush()
 
-    log.write('START: %s\n' % progbase)
-    log.flush()
+    sys.stdout.write('.' * dot_count)
+    sys.stdout.flush()
 
-    start_time = datetime.now()
-    if progbase[-3:] == '.py':
-      progname = sys.executable
-      cmdline = [progname,
-                 os.path.join(self.srcdir, prog)]
-      if self.base_url is not None:
-        cmdline.append('--url=' + self.base_url)
-      if self.enable_sasl is not None:
-        cmdline.append('--enable-sasl')
-      if self.parallel is not None:
-        cmdline.append('--parallel')
-      if self.config_file is not None:
-        cmdline.append('--config-file=' + self.config_file)
-    elif os.access(prog, os.X_OK):
+    if os.access(progbase, os.X_OK):
       progname = './' + progbase
       cmdline = [progname,
                  '--srcdir=' + os.path.join(self.srcdir, progdir)]
@@ -307,10 +278,129 @@ class TestHarness:
       test_nums = test_nums.split(',')
       cmdline.extend(test_nums)
 
+    return self._run_prog(progname, cmdline)
+
+  def _run_py_test(self, prog, test_nums, dot_count):
+    'Run a python test, passing parameters as needed.'
+    progdir, progbase = os.path.split(prog)
+
+    old_path = sys.path[:]
+    sys.path = [progdir] + sys.path
+
+    try:
+      prog_mod = imp.load_module(progbase[:-3], open(prog, 'r'), prog,
+                                 ('.py', 'U', imp.PY_SOURCE))
+    except:
+      print('Don\'t know what to do about ' + progbase)
+      raise
+
+    import svntest.main
+
+    # set up our options
+    svntest.main.create_default_options()
+    if self.base_url is not None:
+      svntest.main.options.test_area_url = self.base_url
+    if self.enable_sasl is not None:
+      svntest.main.options.enable_sasl = True
+    if self.parallel is not None:
+      svntest.main.options.parallel = True
+    if self.config_file is not None:
+      svntest.main.options.config_file = self.config_file
+    if self.verbose is not None:
+      svntest.main.options.verbose = True
+    if self.cleanup is not None:
+      svntest.main.options.cleanup = True
+    if self.fs_type is not None:
+      svntest.main.options.fs_type = self.fs_type
+    if self.http_library is not None:
+      svntest.main.options.http_library = self.http_library
+    if self.server_minor_version is not None:
+      svntest.main.options.server_minor_version = self.server_minor_version
+    if self.list_tests is not None:
+      svntest.main.options.list_tests = True
+    if self.svn_bin is not None:
+      svntest.main.options.svn_bin = self.svn_bin
+    if self.fsfs_sharding is not None:
+      svntest.main.options.fsfs_sharding = self.fsfs_sharding
+    if self.fsfs_packing is not None:
+      svntest.main.options.fsfs_packing = self.fsfs_packing
+
+    svntest.main.options.srcdir = self.srcdir
+
+    # setup the output pipes
+    if self.log:
+      sys.stdout.flush()
+      sys.stderr.flush()
+      self.log.flush()
+      old_stdout = os.dup(1)
+      old_stderr = os.dup(2)
+      os.dup2(self.log.fileno(), 1)
+      os.dup2(self.log.fileno(), 2)
+
+    # This has to be class-scoped for use in the progress_func()
+    self.dots_written = 0
+    def progress_func(completed, total):
+      dots = (completed * dot_count) / total
+
+      dots_to_write = dots - self.dots_written
+      if self.log:
+        os.write(old_stdout, '.' * dots_to_write)
+      else:
+        sys.stdout.write(old_stdout, '.' * dots_to_write)
+        sys.stdout.flush()
+
+      self.dots_written = dots
+
+    # run the tests
+    svntest.testcase.TextColors.disable()
+    failed = svntest.main.execute_tests(prog_mod.test_list,
+                                        test_name=progbase,
+                                        progress_func=progress_func)
+
+    # restore some values
+    sys.path = old_path
+    if self.log:
+      os.dup2(old_stdout, 1)
+      os.dup2(old_stderr, 2)
+      os.close(old_stdout)
+      os.close(old_stderr)
+
+    return failed
+
+  def _run_test(self, prog, test_nr, total_tests):
+    "Run a single test. Return the test's exit code."
+
+    if self.log:
+      log = self.log
+    else:
+      log = sys.stdout
+
+    test_nums = None
+    if '#' in prog:
+      prog, test_nums = prog.split('#')
+
+    progdir, progbase = os.path.split(prog)
+    if self.log:
+      # Using write here because we don't want even a trailing space
+      test_info = '%s [%d/%d]' % (progbase, test_nr + 1, total_tests)
+      sys.stdout.write('Running tests in %s' % (test_info, ))
+      sys.stdout.flush()
+
+    log.write('START: %s\n' % progbase)
+    log.flush()
+
+    start_time = datetime.now()
+
+    progabs = os.path.abspath(os.path.join(self.srcdir, prog))
     old_cwd = os.getcwd()
     try:
       os.chdir(progdir)
-      failed = self._run_prog(progname, cmdline)
+      if progbase[-3:] == '.py':
+        failed = self._run_py_test(progabs, test_nums,
+                                   (LINE_LENGTH - len(test_info)))
+      else:
+        failed = self._run_c_test(prog, test_nums,
+                                  (LINE_LENGTH - len(test_info)))
     except:
       os.chdir(old_cwd)
       raise

Modified: subversion/trunk/subversion/tests/cmdline/svntest/main.py
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/svntest/main.py?rev=1033086&r1=1033085&r2=1033086&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/svntest/main.py (original)
+++ subversion/trunk/subversion/tests/cmdline/svntest/main.py Tue Nov  9 16:47:34 2010
@@ -445,6 +445,7 @@ def wait_on_pipe(waiter, binary_mode, st
     if exit_code and options.verbose:
       sys.stderr.write("CMD: %s exited with %d\n"
                        % (command_string, exit_code))
+      sys.stderr.flush()
     return stdout_lines, stderr_lines, exit_code
 
 def spawn_process(command, bufsize=0, binary_mode=0, stdin_lines=None,
@@ -1098,8 +1099,9 @@ class TestSpawningThread(threading.Threa
   """A thread that runs test cases in their own processes.
   Receives test numbers to run from the queue, and saves results into
   the results field."""
-  def __init__(self, queue):
+  def __init__(self, srcdir, queue):
     threading.Thread.__init__(self)
+    self.srcdir = srcdir
     self.queue = queue
     self.results = []
 
@@ -1113,8 +1115,8 @@ class TestSpawningThread(threading.Threa
       self.run_one(next_index)
 
   def run_one(self, index):
-    command = sys.argv[0]
-
+    command = os.path.join(self.srcdir, 'subversion/tests/cmdline',
+                           sys.argv[0])
     args = []
     args.append(str(index))
     args.append('-c')
@@ -1283,7 +1285,7 @@ def run_one_test(n, test_list, finished_
   exit_code = TestRunner(test_list[n], n).run()
   return exit_code
 
-def _internal_run_tests(test_list, testnums, parallel):
+def _internal_run_tests(test_list, testnums, parallel, srcdir, progress_func):
   """Run the tests from TEST_LIST whose indices are listed in TESTNUMS.
 
   If we're running the tests in parallel spawn as much parallel processes
@@ -1296,15 +1298,19 @@ def _internal_run_tests(test_list, testn
   tests_started = 0
 
   if not parallel:
-    for testnum in testnums:
+    for i, testnum in enumerate(testnums):
       if run_one_test(testnum, test_list) == 1:
           exit_code = 1
+      # signal progress
+      if progress_func:
+        progress_func(i+1, len(testnums))
   else:
     number_queue = queue.Queue()
     for num in testnums:
       number_queue.put(num)
 
-    threads = [ TestSpawningThread(number_queue) for i in range(parallel) ]
+    threads = [ TestSpawningThread(srcdir, number_queue)
+                for i in range(parallel) ]
     for t in threads:
       t.start()
 
@@ -1317,6 +1323,10 @@ def _internal_run_tests(test_list, testn
       results += t.results
     results.sort()
 
+    # signal some kind of progress
+    if progress_func:
+      progress_func(len(testnums), len(testnums))
+
     # terminate the line of dots
     print("")
 
@@ -1392,6 +1402,8 @@ def _create_parser():
                          'test output and ignores all exceptions in the ' +
                          'run_and_verify* functions. This option is only ' +
                          'useful during test development!')
+  parser.add_option('--srcdir', action='store', dest='srcdir',
+                    help='Source directory.')
 
   # most of the defaults are None, but some are other values, set them here
   parser.set_defaults(
@@ -1428,10 +1440,6 @@ def _parse_options(arglist=sys.argv[1:])
   return (parser, args)
 
 
-# Main func.  This is the "entry point" that all the test scripts call
-# to run their list of tests.
-#
-# This routine parses sys.argv to decide what to do.
 def run_tests(test_list, serial_only = False):
   """Main routine to run all tests in TEST_LIST.
 
@@ -1439,6 +1447,20 @@ def run_tests(test_list, serial_only = F
         appropriate exit code.
   """
 
+  sys.exit(execute_tests(test_list, serial_only))
+
+
+# Main func.  This is the "entry point" that all the test scripts call
+# to run their list of tests.
+#
+# This routine parses sys.argv to decide what to do.
+def execute_tests(test_list, serial_only = False, test_name = None,
+                  progress_func = None):
+  """Similar to run_tests(), but just returns the exit code, rather than
+  exiting the process.  This function can be used when a caller doesn't
+  want the process to die."""
+
+  global pristine_url
   global pristine_greek_repos_url
   global svn_binary
   global svnadmin_binary
@@ -1448,6 +1470,9 @@ def run_tests(test_list, serial_only = F
   global svnversion_binary
   global options
 
+  if test_name:
+    sys.argv[0] = test_name
+
   testnums = []
 
   if not options:
@@ -1560,7 +1585,8 @@ def run_tests(test_list, serial_only = F
     svntest.actions.setup_pristine_greek_repository()
 
   # Run the tests.
-  exit_code = _internal_run_tests(test_list, testnums, options.parallel)
+  exit_code = _internal_run_tests(test_list, testnums, options.parallel,
+                                  options.srcdir, progress_func)
 
   # Remove all scratchwork: the 'pristine' repository, greek tree, etc.
   # This ensures that an 'import' will happen the next time we run.
@@ -1571,4 +1597,4 @@ def run_tests(test_list, serial_only = F
   svntest.sandbox.cleanup_deferred_test_paths()
 
   # Return the appropriate exit code from the tests.
-  sys.exit(exit_code)
+  return exit_code