[Zodb-checkins] CVS: StandaloneZODB - test.py:1.15
Jeremy Hylton
jeremy@zope.com
Mon, 10 Jun 2002 18:27:02 -0400
Update of /cvs-repository/StandaloneZODB
In directory cvs.zope.org:/tmp/cvs-serv2486
Modified Files:
test.py
Log Message:
Merge changes from Zope3 test.py
-g option
-C option
improved -d option
Add -G option to set invoke gc.set_debug()
=== StandaloneZODB/test.py 1.14 => 1.15 ===
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+test.py [-bdvvL] [modfilter [testfilter]]
+
+Test harness.
+
+-b build
+ Run "python setup.py -q build" before running tests, where "python"
+ is the version of python used to run test.py. Highly recommended.
+
+-d debug
+ 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.
+
+-v verbose
+ 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" ...). Witn no -v, unittest is silent until the end of the
+ run, except when errors occur.
+
+-L Loop
+ Keep running the selected tests in a loop. You may experience
+ memory leakage.
+
+-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 glags. 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."
+
+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.
+"""
+import gc
import os
import re
import sys
@@ -10,6 +89,20 @@
class ImmediateTestResult(unittest._TextTestResult):
+ __super_init = unittest._TextTestResult.__init__
+
+ def __init__(self, *args, **kwarg):
+ debug = kwarg.get('debug')
+ if debug is not None:
+ del kwarg['debug']
+ self.__super_init(*args, **kwarg)
+ self._debug = debug
+
+ def stopTest(self, test):
+ if gc.garbage:
+ print test
+ print gc.garbage
+
def _print_traceback(self, msg, err, test, errlist):
if self.showAll or self.dots:
self.stream.writeln("\n")
@@ -20,10 +113,14 @@
errlist.append((test, tb))
def addError(self, test, err):
+ if self._debug:
+ raise err[0], err[1], err[2]
self._print_traceback("Error in test %s" % test, err,
test, self.errors)
def addFailure(self, test, err):
+ if self._debug:
+ raise err[0], err[1], err[2]
self._print_traceback("Failure in test %s" % test, err,
test, self.failures)
@@ -33,25 +130,39 @@
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']
+ self.__super_init(**kwarg)
+ self._debug = debug
+
def _makeResult(self):
return ImmediateTestResult(self.stream, self.descriptions,
- self.verbosity)
+ self.verbosity, debug=self._debug)
# setup list of directories to put on the path
PLAT_SPEC = "%s-%s" % (get_platform(), sys.version[0:3])
def setup_path():
- DIRS = ["lib",
- "lib.%s" % PLAT_SPEC,
+ DIRS = ["lib.%s" % PLAT_SPEC,
]
for d in DIRS:
sys.path.insert(0, d)
+def match(rx, s):
+ if not rx:
+ return 1
+ if rx[0] == '!':
+ return re.search(rx[1:], s) is None
+ else:
+ return re.search(rx, s) is not None
# Find test files.
# They live under either a lib.PLAT_SPEC or plain "lib" directory.
@@ -83,9 +194,9 @@
else:
self.files.append(path)
-def find_tests(filter):
- if filter is not None:
- rx = re.compile(filter)
+def find_tests(srx):
+ if srx is not None:
+ rx = re.compile(srx)
else:
rx = None
finder = TestFileFinder()
@@ -99,41 +210,32 @@
return mod
def module_from_path(path):
- """Return the Python package name indiciated by the filesystem path.
+ """Return the Python package name indiciated by the filesystem path."""
- The path starts with build/lib or build /lib.mumble..."""
-
- assert path[-3:] == '.py'
+ assert path.endswith('.py')
path = path[:-3]
dirs = []
while path:
path, end = os.path.split(path)
dirs.insert(0, end)
- assert dirs[0] == "build"
- assert dirs[1][:3] == "lib"
return ".".join(dirs[2:])
def get_suite(file):
- assert file[:5] == "build"
- assert file[-3:] == '.py'
modname = module_from_path(file)
try:
mod = package_import(modname)
- except ImportError:
- print >> sys.stderr, 'Test skipped:', modname
+ except ImportError, err:
+ # print traceback
+ print "Error importing %s\n%s" % (modname, err)
+ if debug:
+ raise
return None
try:
- return mod.test_suite()
+ suite_func = mod.test_suite
except AttributeError:
+ print "No test_suite() in %s" % file
return None
-
-def match(rx, s):
- if not rx:
- return 1
- if rx[0] == '!':
- return re.search(rx[1:], s) is None
- else:
- return re.search(rx, s) is not None
+ return suite_func()
def filter_testcases(s, rx):
new = unittest.TestSuite()
@@ -150,7 +252,7 @@
return new
def runner(files, test_filter, debug):
- runner = ImmediateTestRunner(verbosity=VERBOSE)
+ runner = ImmediateTestRunner(verbosity=VERBOSE, debug=debug)
suite = unittest.TestSuite()
for file in files:
s = get_suite(file)
@@ -158,13 +260,20 @@
if test_filter is not None:
s = filter_testcases(s, test_filter)
suite.addTest(s)
- if debug:
- suite.debug()
- return 0
r = runner.run(suite)
- return len(r.errors) + len(r.failures)
+
+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
+ os.unlink(fullname)
def main(module_filter, test_filter):
+ os.path.walk(os.curdir, remove_stale_bytecode, None)
setup_path()
files = find_tests(module_filter)
files.sort()
@@ -177,8 +286,17 @@
else:
runner(files, test_filter, debug)
-if __name__ == "__main__":
+
+def process_args():
import getopt
+ global module_filter
+ global test_filter
+ global VERBOSE
+ global LOOP
+ global debug
+ global build
+ global gcthresh
+ global gcdebug
module_filter = None
test_filter = None
@@ -186,9 +304,12 @@
LOOP = 0
debug = 0 # Don't collect test results; simply let tests crash
build = 0
+ gcthresh = None
+ gcdebug = 0
try:
- opts, args = getopt.getopt(sys.argv[1:], 'vdLbh')
+ opts, args = getopt.getopt(sys.argv[1:], 'vdLbhCg:G:',
+ ['help'])
except getopt.error, msg:
print msg
print "Try `python %s -h' for more information." % sys.argv[0]
@@ -196,19 +317,37 @@
for k, v in opts:
if k == '-v':
- VERBOSE = VERBOSE + 1
+ VERBOSE += 1
elif k == '-d':
debug = 1
elif k == '-L':
LOOP = 1
elif k == '-b':
build = 1
- elif k == '-h':
+ elif k in ('-h', '--help'):
print __doc__
sys.exit(0)
+ elif k == '-C':
+ import pychecker.checker
+ elif k == '-g':
+ gcthresh = int(v)
+ elif k == '-G':
+ flag = getattr(gc, v)
+ gcdebug |= flag
+
+ 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 gcdebug:
+ gc.set_debug(gcdebug)
if build:
- cmd = sys.executable + " setup.py -q build"
+ cmd = sys.executable + " stupid_build.py"
if VERBOSE:
print cmd
sts = os.system(cmd)
@@ -228,3 +367,7 @@
print err
print sys.path
raise
+
+
+if __name__ == "__main__":
+ process_args()