[Zodb-checkins] CVS: ZODB3 - test.py:1.22

Barry Warsaw barry@wooz.org
Mon, 13 Jan 2003 19:03:59 -0500


Update of /cvs-repository/ZODB3
In directory cvs.zope.org:/tmp/cvs-serv29125

Modified Files:
	test.py 
Log Message:
Two new options: -a, -t

-a sets the test level (I couldn't think of a better switch to use).
Level 0 says to run all the tests, while any other level runs all the
tests at that level and below.  --all is a synonym for "-a 0".  I'm
not 100% sure about these semantics.

Test levels can be set on either a TestSuite or a TestCase.  Set it on
the suite by setting an attribute on the instance.  Set it on a test
case by using a class attribute.  In both cases, use `level' and set
it to an integer.

By default, the test level is level 1.  If a suite or test case does
not have a `level' attribute, it assumes level 0.

-t option says to time individual tests and print a sorted list of the
top 50 longest tests (min # of tests run).


=== ZODB3/test.py 1.21 => 1.22 ===
--- ZODB3/test.py:1.21	Fri Jan  3 17:06:54 2003
+++ ZODB3/test.py	Mon Jan 13 19:03:57 2003
@@ -12,47 +12,57 @@
 # 
 ##############################################################################
 """
-test.py [-bCdgGLvv] [modfilter [testfilter]]
+test.py [-abCdgGLvvt] [modfilter [testfilter]]
 
 Test harness.
 
--b  build
+-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 -q build" before running tests, where "python"
     is the version of python used to run test.py.  Highly recommended.
 
 -C  use pychecker
 
--d  debug
+-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.
 
--D  debugger
+-D
     Works like -d, except that it loads pdb when an exception occurs.
 
--v  verbose
+-v
     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
+-L
     Keep running the selected tests in a loop.  You may experience
     memory leakage.
 
--g  threshold
+-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
+-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."
 
+-t
+    Time the individual tests and print a list of the top 50, sorted from
+    longest to shortest.
+
 modfilter
 testfilter
     Case-sensitive regexps to limit which tests are run, used in search
@@ -85,30 +95,44 @@
 
 import gc
 import os
-import pdb
 import re
+import pdb
 import sys
+import time
 import traceback
 import unittest
 
 from distutils.util import get_platform
 
-class ImmediateTestResult(unittest._TextTestResult):
 
-    __super_init = unittest._TextTestResult.__init__
+class ImmediateTestResult(unittest._TextTestResult):
 
     def __init__(self, *args, **kwarg):
         debug = kwarg.get('debug')
         if debug is not None:
             del kwarg['debug']
-        self.__super_init(*args, **kwarg)
+        unittest._TextTestResult.__init__(self, *args, **kwarg)
         self._debug = debug
+        self._testtimes = {}
+
+    def startTest(self, test):
+        self._testtimes[test] = time.time()
+        unittest._TextTestResult.startTest(self, test)
 
     def stopTest(self, test):
+        self._testtimes[test] = time.time() - self._testtimes[test]
         if gc.garbage:
             print test
             print gc.garbage
         
+    def print_times(self):
+        results = self._testtimes.items()
+        results.sort(lambda x, y: cmp(y[1], x[1]))
+        n = min(50, len(results))
+        print 'Top', n, 'longest tests:'
+        for i in range(n):
+            print results[i][0], '%sms' % (results[i][1] * 1000,)
+
     def _print_traceback(self, msg, err, test, errlist):
         if self.showAll or self.dots:
             self.stream.writeln("\n")
@@ -136,6 +160,7 @@
             self.stream.writeln("%s: %s" % (flavor, self.getDescription(test)))
             self.stream.writeln(self.separator2)
             self.stream.writeln(err)
+            
 
 class ImmediateTestRunner(unittest.TextTestRunner):
 
@@ -246,10 +271,14 @@
 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 match(rx, name):
+            if not rx or match(rx, name):
                 new.addTest(test)
         else:
             filtered = filter_testcases(test, rx)
@@ -262,12 +291,15 @@
     suite = unittest.TestSuite()
     for file in files:
         s = get_suite(file)
-        if s is not None:
-            if test_filter is not None:
-                s = filter_testcases(s, test_filter)
+        # 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 timetests:
+            r.print_times()
     except:
         if debugger:
             pdb.post_mortem(sys.exc_info()[2])
@@ -310,6 +342,8 @@
     global build
     global gcthresh
     global gcdebug
+    global level
+    global timetests
 
     module_filter = None
     test_filter = None
@@ -320,17 +354,23 @@
     build = 0
     gcthresh = None
     gcdebug = 0
+    level = 1
+    timetests = 0
 
     try:
-        opts, args = getopt.getopt(sys.argv[1:], 'DvdLbhCg:G:',
-                                   ['help'])
+        opts, args = getopt.getopt(sys.argv[1:], 'a:DvdLbhCg:G:t',
+                                   ['help', 'all'])
     except getopt.error, msg:
         print msg
         print "Try `python %s -h' for more information." % sys.argv[0]
         sys.exit(2)
 
     for k, v in opts:
-        if k == '-v':
+        if k == '-a':
+            level = int(v)
+        elif k == '--all':
+            level = 0
+        elif k == '-v':
             VERBOSE += 1
         elif k == '-d':
             debug = 1
@@ -354,6 +394,8 @@
         elif k == '-G':
             flag = getattr(gc, v)
             gcdebug |= flag
+        elif k == '-t':
+            timetests = 1
 
     if gcthresh is not None:
         if gcthresh == 0:
@@ -374,6 +416,12 @@
         if sts:
             print "Build failed", hex(sts)
             sys.exit(1)
+
+    if VERBOSE:
+        if level == 0:
+            print 'Running tests at all levels'
+        else:
+            print 'Running tests at level', level
 
     if args:
         if len(args) > 1: