[Zope-Checkins] SVN: Zope/trunk/ Integrated the new test runner. test.py is just a script that imports

Jim Fulton jim at zope.com
Tue Oct 25 15:20:24 EDT 2005


Log message for revision 39618:
  Integrated the new test runner.  test.py is just a script that imports
  and uses zope.testing.testrunner as a module.
  

Changed:
  U   Zope/trunk/lib/python/Zope2/Startup/zopectl.py
  _U  Zope/trunk/lib/python/zope/
  U   Zope/trunk/test.py

-=-
Modified: Zope/trunk/lib/python/Zope2/Startup/zopectl.py
===================================================================
--- Zope/trunk/lib/python/Zope2/Startup/zopectl.py	2005-10-25 15:55:40 UTC (rev 39617)
+++ Zope/trunk/lib/python/Zope2/Startup/zopectl.py	2005-10-25 19:20:23 UTC (rev 39618)
@@ -244,7 +244,7 @@
         # (rather than $INSTANCE_HOME/lib/python)
         if '--libdir' not in args:
             args.insert(0, 'Products')
-            args.insert(0, '--libdir')
+            args.insert(0, '--package')
 
         # Supply our config file by default.
         if '--config-file' not in args and '-C' not in args:


Property changes on: Zope/trunk/lib/python/zope
___________________________________________________________________
Name: svn:externals
   - app              svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/app
cachedescriptors svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/cachedescriptors
component        svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/component
configuration    svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/configuration
documenttemplate svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/documenttemplate
event            svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/event
exceptions       svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/exceptions
hookable         svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/hookable
i18n             svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/i18n
i18nmessageid    svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/i18nmessageid
interface        svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/interface
modulealias      svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/modulealias
pagetemplate     svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/pagetemplate
proxy            svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/proxy
publisher        svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/publisher
schema           svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/schema
security         svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/security
server           svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/server
structuredtext   svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/structuredtext
tal              svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/tal
tales            svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/tales
testing          svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/testing
thread           svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/thread


   + app              svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/app
cachedescriptors svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/cachedescriptors
component        svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/component
configuration    svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/configuration
documenttemplate svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/documenttemplate
event            svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/event
exceptions       svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/exceptions
hookable         svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/hookable
i18n             svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/i18n
i18nmessageid    svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/i18nmessageid
interface        svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/interface
modulealias      svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/modulealias
pagetemplate     svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/pagetemplate
proxy            svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/proxy
publisher        svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/publisher
schema           svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/schema
security         svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/security
server           svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/server
structuredtext   svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/structuredtext
tal              svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/tal
tales            svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/tales
testing          -r39604 svn://svn.zope.org/repos/main/zope.testing/trunk/src/zope/testing
thread           svn://svn.zope.org/repos/main/Zope3/tags/ZopeX3-3.0.1-Zope-2.8/src/zope/thread



Modified: Zope/trunk/test.py
===================================================================
--- Zope/trunk/test.py	2005-10-25 15:55:40 UTC (rev 39617)
+++ Zope/trunk/test.py	2005-10-25 19:20:23 UTC (rev 39618)
@@ -1,8 +1,7 @@
-#!/usr/bin/env python2.3
-
+#!/usr/bin/env python
 ##############################################################################
 #
-# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# Copyright (c) 2004 Zope Corporation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -13,905 +12,64 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""
-test.py [-abcCdDfgGhLmprtTuv] [modfilter [testfilter]]
+"""Zope 2 test script
 
-Find and run tests written using the unittest module.
+see zope.testing testrunner.txt
 
-The test runner searches for Python modules that contain test suites.
-It collects those suites, and runs the tests.  There are many options
-for controlling how the tests are run.  There are options for using
-the debugger, reporting code coverage, and checking for refcount problems.
-
-The test runner uses the following rules for finding tests to run.  It
-searches for packages and modules that contain "tests" as a component
-of the name, e.g. "frob.tests.nitz" matches this rule because tests is
-a sub-package of frob.  Within each "tests" package, it looks for
-modules that begin with the name "test."  For each test module, it
-imports the module and calls the test_suite() method, which must
-return a unittest TestSuite object.  (If a package contains a file
-named .testinfo, it will not be searched for tests.  Really.)
-
--a level
---all
-    Run the tests at the given level.  Any test at a level at or below
-    this is run, any test at a level above this is not run.  Level 0
-    runs all tests.  The default is to run tests at level 1.  --all is
-    a shortcut for -a 0.
-
--b
-    Run "python setup.py build_ext -i" before running tests, where
-    "python" is the version of python used to run test.py.  Highly
-    recommended.  Tests will be run from the build directory.  (Note:
-    In Python < 2.3 the -q flag is added to the setup.py command
-    line.)
-
--c  
-    Use pychecker
-
---config-file filename
-    Configure Zope by loading the specified configuration file (zope.conf).
-
--C filename
-    Shortcut for --config-file filename.
-
--d
-    Instead of the normal test harness, run a debug version which
-    doesn't catch any exceptions.  This is occasionally handy when the
-    unittest code catching the exception doesn't work right.
-    Unfortunately, the debug harness doesn't print the name of the
-    test, so Use With Care.
-
---dir directory 
-    Option to limit where tests are searched for. This is
-    important when you *really* want to limit the code that gets run.
-    For example, if refactoring interfaces, you don't want to see the way
-    you have broken setups for tests in other packages. You *just* want to
-    run the interface tests.
-
--D
-    Works like -d, except that it loads pdb when an exception occurs.
-
--f
-    Run functional tests instead of unit tests.
-
--g threshold
-    Set the garbage collector generation0 threshold.  This can be used
-    to stress memory and gc correctness.  Some crashes are only
-    reproducible when the threshold is set to 1 (agressive garbage
-    collection).  Do "-g 0" to disable garbage collection altogether.
-
--G gc_option
-    Set the garbage collection debugging flags.  The argument must be one
-    of the DEBUG_ flags defined bythe Python gc module.  Multiple options
-    can be specified by using "-G OPTION1 -G OPTION2."
-
---import-testing
-    Import the Testing package to setup the test ZODB.  Useful for running
-    tests that forgot to "import Testing".
-
---libdir test_root
-    Search for tests starting in the specified start directory
-    (useful for testing components being developed outside the main
-    "src" or "build" trees).
-
-    Note: This directory will be prepended to sys.path!
-
---keepbytecode
-    Do not delete all stale bytecode before running tests
-
--L
-    Keep running the selected tests in a loop.  You may experience
-    memory leakage.
-
--t
-    Time the individual tests and print a list of the top 50, sorted from
-    longest to shortest.
-
--p
-    Show running progress.  It can be combined with -v or -vv.
-
--r
-    Look for refcount problems.
-    This requires that Python was built --with-pydebug.
-
--T
-    Use the trace module from Python for code coverage.  XXX This only
-    works if trace.py is explicitly added to PYTHONPATH.  The current
-    utility writes coverage files to a directory named `coverage' that
-    is parallel to `build'.  It also prints a summary to stdout.
-
--v
-    Verbose output.  With one -v, unittest prints a dot (".") for each
-    test run.  With -vv, unittest prints the name of each test (for
-    some definition of "name" ...).  With no -v, unittest is silent
-    until the end of the run, except when errors occur.
-
--u
--m
-    Use the PyUnit GUI instead of output to the command line.  The GUI
-    imports tests on its own, taking care to reload all dependencies
-    on each run.  The debug (-d), verbose (-v), and Loop (-L) options
-    will be ignored.  The testfilter filter is also not applied.
-
-    -m starts the gui minimized.  Double-clicking the progress bar
-    will start the import and run all tests.
-
-
-modfilter
-testfilter
-    Case-sensitive regexps to limit which tests are run, used in search
-    (not match) mode.
-    In an extension of Python regexp notation, a leading "!" is stripped
-    and causes the sense of the remaining regexp to be negated (so "!bc"
-    matches any string that does not match "bc", and vice versa).
-    By default these act like ".", i.e. nothing is excluded.
-
-    modfilter is applied to a test file's path, starting at "build" and
-    including (OS-dependent) path separators.
-
-    testfilter is applied to the (method) name of the unittest methods
-    contained in the test files whose paths modfilter matched.
-
-Extreme (yet useful) examples:
-
-    test.py -vvb . "^checkWriteClient$"
-
-    Builds the project silently, then runs unittest in verbose mode on all
-    tests whose names are precisely "checkWriteClient".  Useful when
-    debugging a specific test.
-
-    test.py -vvb . "!^checkWriteClient$"
-
-    As before, but runs all tests whose names aren't precisely
-    "checkWriteClient".  Useful to avoid a specific failing test you don't
-    want to deal with just yet.
-
-    test.py -m . "!^checkWriteClient$"
-
-    As before, but now opens up a minimized PyUnit GUI window (only showing
-    the progress bar).  Useful for refactoring runs where you continually want
-    to make sure all tests still pass.
+$Id: test.py 33303 2005-07-13 22:28:33Z jim $
 """
 
-import gc
-import os
-import re
-import pdb
-import sys
-import time
-import traceback
-import unittest
+import os.path, sys
 
-from distutils.util import get_platform
+shome = os.environ.get('SOFTWARE_HOME')
+zhome = os.environ.get('ZOPE_HOME')
+ihome = os.environ.get('INSTANCE_HOME')
 
-PLAT_SPEC = "%s-%s" % (get_platform(), sys.version[0:3])
-
-def callers(n):
-    callers = []
-    f = sys._getframe(2)
-    while f:
-        co = f.f_code
-        callers.append((co.co_filename, co.co_name))
-        f = f.f_back
-        n -= 1
-        if not n:
-            break
-    return callers
-
-class ImmediateTestResult(unittest._TextTestResult):
-
-    __super_init = unittest._TextTestResult.__init__
-    __super_startTest = unittest._TextTestResult.startTest
-    __super_printErrors = unittest._TextTestResult.printErrors
-
-    def __init__(self, stream, descriptions, verbosity, debug=False,
-                 count=None, progress=False):
-        self.__super_init(stream, descriptions, verbosity)
-        self._debug = debug
-        self._progress = progress
-        self._progressWithNames = False
-        self.count = count
-        self._testtimes = {}
-        if progress and verbosity == 1:
-            self.dots = False
-            self._progressWithNames = True
-            self._lastWidth = 0
-            self._maxWidth = 80
-            try:
-                import curses
-            except ImportError:
-                pass
-            else:
-                import curses.wrapper
-                def get_max_width(scr, self=self):
-                    self._maxWidth = scr.getmaxyx()[1]
-                try:
-                    curses.wrapper(get_max_width)
-                except curses.error:
-                    pass
-            self._maxWidth -= len("xxxx/xxxx (xxx.x%): ") + 1
-
-    def stopTest(self, test):
-        self._testtimes[test] = time.time() - self._testtimes[test]
-        if gc.garbage:
-            print "The following test left garbage:"
-            print test
-            print gc.garbage
-            # XXX Perhaps eat the garbage here, so that the garbage isn't
-            #     printed for every subsequent test.
-
-    def print_times(self, stream, count=None):
-        results = self._testtimes.items()
-        results.sort(lambda x, y: cmp(y[1], x[1]))
-        if count:
-            n = min(count, len(results))
-            if n:
-                print >>stream, "Top %d longest tests:" % n
-        else:
-            n = len(results)
-        if not n:
-            return
-        for i in range(n):
-            print >>stream, "%6dms" % int(results[i][1] * 1000), results[i][0]
-
-    def _handle_problem(self, err, test, errlist):
-        
-        if self._debug:
-            raise err[0], err[1], err[2]
-        
-        if errlist is self.errors:
-            prefix = 'Error'
-        else:
-            prefix = 'Failure'
-            
-        tb = "".join(traceback.format_exception(*err))
-        
-        if self._progress:
-            self.stream.writeln("\r")
-            self.stream.writeln("%s in test %s" % (prefix,test))
-            self.stream.writeln(tb)
-            self._lastWidth = 0
-        elif self.showAll:
-            self._lastWidth = 0
-            self.stream.writeln(prefix.upper())
-        elif self.dots:
-            self.stream.write(prefix[0])
-            
-        errlist.append((test, tb))
-
-    def startTest(self, test):
-        if self._progress:
-            self.stream.write("\r%4d" % (self.testsRun + 1))
-            if self.count:
-                self.stream.write("/%d (%5.1f%%)" % (self.count,
-                                  (self.testsRun + 1) * 100.0 / self.count))
-            if self.showAll:
-                self.stream.write(": ")
-            elif self._progressWithNames:
-                # XXX will break with multibyte strings
-                name = self.getShortDescription(test)
-                width = len(name)
-                if width < self._lastWidth:
-                    name += " " * (self._lastWidth - width)
-                self.stream.write(": %s" % name)
-                self._lastWidth = width
-            self.stream.flush()
-        self.__super_startTest(test)
-        self._testtimes[test] = time.time()
-
-    def getShortDescription(self, test):
-        s = self.getDescription(test)
-        if len(s) > self._maxWidth:
-            pos = s.find(" (")
-            if pos >= 0:
-                w = self._maxWidth - (pos + 5)
-                if w < 1:
-                    # first portion (test method name) is too long
-                    s = s[:self._maxWidth-3] + "..."
-                else:
-                    pre = s[:pos+2]
-                    post = s[-w:]
-                    s = "%s...%s" % (pre, post)
-        return s[:self._maxWidth]
-
-    def addError(self, test, err):
-        self._handle_problem(err, test, self.errors)
-
-    def addFailure(self, test, err):
-        self._handle_problem(err, test, self.failures)
-
-    def printErrors(self):
-        if self._progress and not (self.dots or self.showAll):
-            self.stream.writeln()
-        self.__super_printErrors()
-
-    def printErrorList(self, flavor, errors):
-        for test, err in errors:
-            self.stream.writeln(self.separator1)
-            self.stream.writeln("%s: %s" % (flavor, self.getDescription(test)))
-            self.stream.writeln(self.separator2)
-            self.stream.writeln(err)
-
-
-class ImmediateTestRunner(unittest.TextTestRunner):
-
-    __super_init = unittest.TextTestRunner.__init__
-
-    def __init__(self, **kwarg):
-        debug = kwarg.get("debug")
-        if debug is not None:
-            del kwarg["debug"]
-        progress = kwarg.get("progress")
-        if progress is not None:
-            del kwarg["progress"]
-        self.__super_init(**kwarg)
-        self._debug = debug
-        self._progress = progress
-        # Create the test result here, so that we can add errors if
-        # the test suite search process has problems.  The count
-        # attribute must be set in run(), because we won't know the
-        # count until all test suites have been found.
-        self.result = ImmediateTestResult(
-            self.stream, self.descriptions, self.verbosity, debug=self._debug,
-            progress=self._progress)
-
-    def _makeResult(self):
-        # Needed base class run method.
-        return self.result
-
-    def run(self, test):
-        self.result.count = test.countTestCases()
-        return unittest.TextTestRunner.run(self, test)
-
-# setup list of directories to put on the path
-class PathInit:
-    def __init__(self, build, libdir=None):
-        # Calculate which directories we're going to add to sys.path.
-        self.libdir = os.path.join('lib', 'python')
-        # Hack sys.path
-        self.home = os.path.dirname(os.path.realpath(sys.argv[0]))
-        # test.py lives in $ZOPE_HOME/bin when installed ...
-        dir, file = os.path.split(self.home)
-        if file == 'bin': self.home = dir
-        sys.path.insert(0, os.path.join(self.home, self.libdir))
-        self.cwd = os.path.realpath(os.getcwd())
-        # Hack again for external products.
-        if libdir:
-            self.libdir = os.path.realpath(os.path.join(self.cwd, libdir))
-        else:
-            self.libdir = os.path.realpath(os.path.join(self.cwd, self.libdir))
-        if self.libdir not in sys.path:
-            sys.path.insert(0, self.libdir)
-        # Determine where to look for tests
-        if test_dir:
-            self.testdir = os.path.abspath(os.path.join(self.cwd, test_dir))
-        else:
-            self.testdir = self.libdir
-        kind = functional and "functional" or "unit"
-        print "Running %s tests from %s" % (kind, self.testdir)
-
-def match(rx, s):
-    if not rx:
-        return True
-    if rx[0] == "!":
-        return re.search(rx[1:], s) is None
+if zhome:
+    zhome = os.path.abspath(zhome)
+    if shome:
+        shome = os.path.abspath(shome)
     else:
-        return re.search(rx, s) is not None
-
-class TestFileFinder:
-    def __init__(self, prefix):
-        self.files = []
-        self._plen = len(prefix)
-        if not prefix.endswith(os.sep):
-            self._plen += 1
-        global functional
-        if functional:
-            self.dirname = "ftests"
-        else:
-            self.dirname = "tests"
-        # dirs maps directories to a boolean indicating whether
-        # the directory is a package.  Bootstrap dirs with prefix;
-        # it isn't actually a package, but it contains packages.
-        self.dirs = {prefix: True}
-
-    def is_package(self, dir):
-        # Return true if dir contains a testable package.
-        bool = self.dirs.get(dir)
-        if bool is not None:
-            return bool
-        files = os.listdir(dir)
-        if ".testinfo" in files or "__init__.py" not in files:
-            self.dirs[dir] = False
-            return False
-        parent, dir = os.path.split(dir)
-        bool = self.is_package(parent)
-        self.dirs[dir] = bool
-        return bool
-
-    def visit(self, rx, dir, files):
-        if os.path.split(dir)[1] != self.dirname:
-            # Allow tests module rather than package.
-            if "tests.py" in files:
-                path = os.path.join(dir, "tests.py")
-                if match(rx, path):
-                    self.files.append(path)
-                    return 
-            return
-        if not self.is_package(dir):
-            return
-
-        # Put matching files in matches.  If matches is non-empty,
-        # then make sure that the package is importable.
-        matches = []
-        for file in files:
-            if file.startswith('test') and os.path.splitext(file)[-1] == '.py':
-                path = os.path.join(dir, file)
-                if match(rx, path):
-                    matches.append(path)
-
-        # ignore tests when the package can't be imported, possibly due to
-        # dependency failures.
-        pkg = dir[self._plen:].replace(os.sep, '.')
-        try:
-            __import__(pkg)
-        # We specifically do not want to catch ImportError since that's useful
-        # information to know when running the tests.
-        except RuntimeError, e:
-            if VERBOSE:
-                print "skipping %s because: %s" % (pkg, e)
-            return
-        else:
-            self.files.extend(matches)
-
-    def module_from_path(self, path):
-        """Return the Python package name indicated by the filesystem path."""
-        assert path.endswith(".py")
-        path = path[self._plen:-3]
-        mod = path.replace(os.sep, ".")
-        return mod
-
-def find_tests(rx):
-    global finder
-    finder = TestFileFinder(pathinit.libdir)
-    walk_with_symlinks(pathinit.testdir, finder.visit, rx)
-    return finder.files
-
-def package_import(modname):
-    mod = __import__(modname)
-    for part in modname.split(".")[1:]:
-        mod = getattr(mod, part)
-    return mod
-
-class PseudoTestCase:
-    """Minimal test case objects to create error reports.
-
-    If test.py finds something that looks like it should be a test but
-    can't load it or find its test suite, it will report an error
-    using a PseudoTestCase.
-    """
-
-    def __init__(self, name, descr=None):
-        self.name = name
-        self.descr = descr
-
-    def shortDescription(self):
-        return self.descr
-
-    def __str__(self):
-        return "Invalid Test (%s)" % self.name
-
-def get_suite(file, result):
-    modname = finder.module_from_path(file)
-    try:
-        mod = package_import(modname)
-        return mod.test_suite()
-    except AttributeError:
-        result.addError(PseudoTestCase(modname), sys.exc_info())
-        return None
-
-def filter_testcases(s, rx):
-    new = unittest.TestSuite()
-    for test in s._tests:
-        # See if the levels match
-        dolevel = (level == 0) or level >= getattr(test, "level", 0)
-        if not dolevel:
-            continue
-        if isinstance(test, unittest.TestCase):
-            name = test.id() # Full test name: package.module.class.method
-            name = name[1 + name.rfind("."):] # extract method name
-            if not rx or match(rx, name):
-                new.addTest(test)
-        else:
-            filtered = filter_testcases(test, rx)
-            if filtered:
-                new.addTest(filtered)
-    return new
-
-def gui_runner(files, test_filter):
-    utildir = os.path.join(os.getcwd(), "utilities")
-    sys.path.append(utildir)
-    import unittestgui
-    suites = []
-    for file in files:
-        suites.append(finder.module_from_path(file) + ".test_suite")
-
-    suites = ", ".join(suites)
-    minimal = (GUI == "minimal")
-    unittestgui.main(suites, minimal)
-
-class TrackRefs:
-    """Object to track reference counts across test runs."""
-
-    def __init__(self):
-        self.type2count = {}
-        self.type2all = {}
-
-    def update(self):
-        import types
-        obs = sys.getobjects(0)
-        type2count = {}
-        type2all = {}
-        classes = []
-        for o in obs:
-            all = sys.getrefcount(o)
-            t = type(o)
-            if t is types.ClassType:
-                classes.append((all, o))
-            if t in type2count:
-                type2count[t] += 1
-                type2all[t] += all
-            else:
-                type2count[t] = 1
-                type2all[t] = all
-
-        ct = [(type2count[t] - self.type2count.get(t, 0),
-               type2all[t] - self.type2all.get(t, 0),
-               t)
-              for t in type2count.iterkeys()]
-        ct.sort()
-        ct.reverse()
-        for delta1, delta2, t in ct:
-            if delta1 or delta2:
-                print "%-55s %8d %8d" % (t, delta1, delta2)
-
-        classes.sort()
-        classes.reverse()
-        for n, c in classes[:10]:
-            print n, c
-
-        self.type2count = type2count
-        self.type2all = type2all
-
-def runner(files, test_filter, debug):
-    runner = ImmediateTestRunner(verbosity=VERBOSE, debug=debug,
-                                 progress=progress)
-    suite = unittest.TestSuite()
-    for file in files:
-        s = get_suite(file, runner.result)
-        # See if the levels match
-        dolevel = (level == 0) or level >= getattr(s, "level", 0)
-        if s is not None and dolevel:
-            s = filter_testcases(s, test_filter)
-            suite.addTest(s)
-    try:
-        r = runner.run(suite)
-        if timesfn:
-            r.print_times(open(timesfn, "w"))
-            if VERBOSE:
-                print "Wrote timing data to", timesfn
-        if timetests:
-            r.print_times(sys.stdout, timetests)
-    except:
-        if debugger:
-            pdb.post_mortem(sys.exc_info()[2])
-        else:
-            raise
-
-def walk_with_symlinks(path, visit, arg):
-    """Like os.path.walk, but follows symlinks on POSIX systems.
-
-    This could theoretically result in an infinite loop, if you create symlink
-    cycles in your Zope sandbox, so don't do that.
-    """
-    try:
-        names = os.listdir(path)
-    except os.error:
-        return
-    visit(arg, path, names)
-    exceptions = (os.curdir, os.pardir, 'var')
-    for name in names:
-        if name not in exceptions:
-            name = os.path.join(path, name)
-            if os.path.isdir(name):
-                walk_with_symlinks(name, visit, arg)
-
-def remove_stale_bytecode(arg, dirname, names):
-    names = map(os.path.normcase, names)
-    for name in names:
-        if name.endswith(".pyc") or name.endswith(".pyo"):
-            srcname = name[:-1]
-            if srcname not in names:
-                fullname = os.path.join(dirname, name)
-                print "Removing stale bytecode file", fullname,
-                try:
-                    os.unlink(fullname)
-                except (OSError, IOError), e:
-                    print ' -->  %s (errno %d)' % (e.strerror, e.errno)
-                else:
-                    print
-
-
-def main(module_filter, test_filter, libdir):
-    global pathinit
-    global config_file
-
-    configure_logging()
-
-    # Initialize the path and cwd
-    pathinit = PathInit(build, libdir)
-
-    if not keepStaleBytecode:
-        walk_with_symlinks(pathinit.home, remove_stale_bytecode, None)
-    
-    # Load configuration
-    if config_file:
-        config_file = os.path.realpath(config_file)
-        print "Parsing %s" % config_file
-        import Zope2
-        Zope2.configure(config_file)
-
-    if not keepStaleBytecode:
-        from App.config import getConfiguration
-        softwarehome = os.path.realpath(getConfiguration().softwarehome)
-        instancehome = os.path.realpath(getConfiguration().instancehome)
-        softwarehome = os.path.normcase(softwarehome)
-        if not softwarehome.startswith(os.path.normcase(instancehome)):
-            walk_with_symlinks(instancehome, remove_stale_bytecode, None)
-
-    # Import Testing package to setup the test ZODB
-    if import_testing:
-        import Testing
-
-    files = find_tests(module_filter)
-    files.sort()
-
-    if GUI:
-        gui_runner(files, test_filter)
-    elif LOOP:
-        if REFCOUNT:
-            rc = sys.gettotalrefcount()
-            track = TrackRefs()
-        while True:
-            runner(files, test_filter, debug)
-            gc.collect()
-            if gc.garbage:
-                print "GARBAGE:", len(gc.garbage), gc.garbage
-                return
-            if REFCOUNT:
-                prev = rc
-                rc = sys.gettotalrefcount()
-                print "totalrefcount=%-8d change=%-6d" % (rc, rc - prev)
-                track.update()
+        shome = os.path.join(zhome, 'lib/python')
+elif shome:
+    shome = os.path.abspath(shome)
+    if zhome:
+        zhome = os.path.abspath(zhome)
     else:
-        runner(files, test_filter, debug)
+        zhome = os.path.dirname(os.path.dirname(shome))
+elif ihome:
+    print >> sys.stderr, '''
+    If INSTANCE_HOME is set, then at least one of SOFTWARE_HOME or ZOPE_HOME
+    must be set
+    '''
+else:
+    # No zope home, assume that it is the script directory
+    zhome = os.path.abspath(os.path.dirname(sys.argv[0]))
+    shome = os.path.join(zhome, 'lib/python')
 
+sys.path.insert(0, shome)
 
-def configure_logging():
-    """Initialize the logging module."""
-    import logging.config
+defaults = '--tests-pattern ^tests$ -v'.split()
+if ihome:
+    ihome = os.path.abspath(ihome)
+    sys.path.insert(0, os.path.join(ihome, 'lib', 'python'))
+    defaults += ['--test-path', ihome, '--package', 'Products']
+else:
+    defaults += ['--test-path', shome]
 
-    # Get the log.ini file from the current directory instead of possibly                                      
-    # buried in the build directory.  XXX This isn't perfect because if
-    # log.ini specifies a log file, it'll be relative to the build directory.
-    # Hmm...
-    logini = os.path.abspath("log.ini")
+from zope.testing import testrunner
 
-    if os.path.exists(logini):
-        logging.config.fileConfig(logini)
-    else:
-        logging.basicConfig()
+def load_config_file(option, opt, config_file, *ignored):
+    config_file = os.path.abspath(config_file)
+    print "Parsing %s" % config_file
+    import Zope2
+    Zope2.configure(config_file)
 
-    if os.environ.has_key("LOGGING"):
-        level = int(os.environ["LOGGING"])
-        logging.getLogger().setLevel(level)
+testrunner.setup.add_option(
+    '--config-file', action="callback", type="string",
+    callback=load_config_file,
+    help="""\
+Initialize Zope with the given config file.
+""")
 
-
-def process_args(argv=None):
-    import getopt
-    global module_filter
-    global test_filter
-    global VERBOSE
-    global LOOP
-    global GUI
-    global TRACE
-    global REFCOUNT
-    global debug
-    global debugger
-    global build
-    global level
-    global libdir
-    global timesfn
-    global timetests
-    global progress
-    global keepStaleBytecode
-    global functional
-    global test_dir
-    global config_file
-    global import_testing
-
-    if argv is None:
-        argv = sys.argv
-
-    module_filter = None
-    test_filter = None
-    VERBOSE = 0
-    LOOP = False
-    GUI = False
-    TRACE = False
-    REFCOUNT = False
-    debug = False # Don't collect test results; simply let tests crash
-    debugger = False
-    build = False
-    gcthresh = None
-    gcdebug = 0
-    gcflags = []
-    level = 1
-    libdir = None
-    progress = False
-    timesfn = None
-    timetests = 0
-    keepStaleBytecode = 0
-    functional = False
-    test_dir = None
-    config_file = None
-    import_testing = False
-
-    try:
-        opts, args = getopt.getopt(argv[1:], "a:bcC:dDfg:G:hLmprtTuv",
-                                   ["all", "help", "libdir=", "times=",
-                                    "keepbytecode", "dir=", 
-                                    "config-file=", "import-testing"])
-    except getopt.error, msg:
-        print msg
-        print "Try `python %s -h' for more information." % argv[0]
-        sys.exit(2)
-
-    for k, v in opts:
-        if k == "-a":
-            level = int(v)
-        elif k == "--all":
-            level = 0
-        elif k == "-b":
-            build = True
-        elif k == "-c":
-            # make sure you have a recent version of pychecker
-            if not os.environ.get("PYCHECKER"):
-                os.environ["PYCHECKER"] = "-q"
-            import pychecker.checker
-        elif k == "-d":
-            debug = True
-        elif k == "-D":
-            debug = True
-            debugger = True
-        elif k == "-f":
-            functional = True
-        elif k in ("-h", "--help"):
-            print __doc__
-            sys.exit(0)
-        elif k == "-g":
-            gcthresh = int(v)
-        elif k == "-G":
-            if not v.startswith("DEBUG_"):
-                print "-G argument must be DEBUG_ flag, not", repr(v)
-                sys.exit(1)
-            gcflags.append(v)
-        elif k == '--keepbytecode':
-            keepStaleBytecode = 1
-        elif k == '--libdir':
-            libdir = v
-        elif k == "-L":
-            LOOP = 1
-        elif k == "-m":
-            GUI = "minimal"
-        elif k == "-p":
-            progress = True
-        elif k == "-r":
-            if hasattr(sys, "gettotalrefcount"):
-                REFCOUNT = True
-            else:
-                print "-r ignored, because it needs a debug build of Python"
-        elif k == "-T":
-            TRACE = True
-        elif k == "-t":
-            if not timetests:
-                timetests = 50
-        elif k == "-u":
-            GUI = 1
-        elif k == "-v":
-            VERBOSE += 1
-        elif k == "--times":
-            try:
-                timetests = int(v)
-            except ValueError:
-                # must be a filename to write
-                timesfn = v
-        elif k == '--dir':
-            test_dir = v
-        elif k == '--config-file' or k == '-C':
-            config_file = v
-        elif k == '--import-testing':
-            import_testing = True
-
-    if gcthresh is not None:
-        if gcthresh == 0:
-            gc.disable()
-            print "gc disabled"
-        else:
-            gc.set_threshold(gcthresh)
-            print "gc threshold:", gc.get_threshold()
-
-    if gcflags:
-        val = 0
-        for flag in gcflags:
-            v = getattr(gc, flag, None)
-            if v is None:
-                print "Unknown gc flag", repr(flag)
-                print gc.set_debug.__doc__
-                sys.exit(1)
-            val |= v
-        gcdebug |= v
-
-    if gcdebug:
-        gc.set_debug(gcdebug)
-
-    if build:
-        # Python 2.3 is more sane in its non -q output
-        if sys.hexversion >= 0x02030000:
-            qflag = ""
-        else:
-            qflag = "-q"
-        cmd = sys.executable + " setup.py " + qflag + " build_ext -i"
-        if VERBOSE:
-            print cmd
-        sts = os.system(cmd)
-        if sts:
-            print "Build failed", hex(sts)
-            sys.exit(1)
-
-    if VERBOSE:
-        kind = functional and "functional" or "unit"
-        if level == 0:
-            print "Running %s tests at all levels" % kind
-        else:
-            print "Running %s tests at level %d" % (kind, level)
-
-    if args:
-        if len(args) > 1:
-            test_filter = args[1]
-        module_filter = args[0]
-    try:
-        if TRACE:
-            # if the trace module is used, then we don't exit with
-            # status if on a false return value from main.
-            coverdir = os.path.join(os.getcwd(), "coverage")
-            import trace
-            tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix],
-                                 trace=0, count=1)
-
-            tracer.runctx("main(module_filter, test_filter, libdir)",
-                          globals=globals(), locals=vars())
-            r = tracer.results()
-            r.write_results(show_missing=True, summary=True, coverdir=coverdir)
-        else:
-            bad = main(module_filter, test_filter, libdir)
-            if bad:
-                sys.exit(1)
-    except ImportError, err:
-        print err
-        print sys.path
-        raise
-
-
-if __name__ == "__main__":
-    process_args()
+sys.exit(testrunner.run(defaults))



More information about the Zope-Checkins mailing list