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)) _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org http://mail.zope.org/mailman/listinfo/zope-checkins