[Zope-Checkins] CVS: Zope/utilities - testrunner.py:1.33

Fred L. Drake, Jr. fred@zope.com
Thu, 30 Jan 2003 16:19:54 -0500


Update of /cvs-repository/Zope/utilities
In directory cvs.zope.org:/tmp/cvs-serv1042

Modified Files:
	testrunner.py 
Log Message:
Lots of really small changes.


=== Zope/utilities/testrunner.py 1.32 => 1.33 ===
--- Zope/utilities/testrunner.py:1.32	Thu Jan 30 14:24:00 2003
+++ Zope/utilities/testrunner.py	Thu Jan 30 16:19:51 2003
@@ -1,9 +1,9 @@
+#! /usr/bin/env python2.2
 """testrunner - a Zope test suite utility.
 
-The testrunner utility is used to execute PyUnit test suites. You can find
-more information on PyUnit at http://pyunit.sourceforge.net. This utility
-should be run from the root of your Zope installation. It will set up the
-correct python path environment based on your installation directory so that
+The testrunner utility is used to execute PyUnit test suites. This utility
+should be run from the root of your Zope source directory. It will set up the
+correct python path environment based on your source directory so that
 test suites can import Zope modules in a way that is fairly independent of
 the location of the test suite. It does *not* import the Zope package, so
 a test thats depend on dynamic aspects of the Zope environment (such as
@@ -13,14 +13,18 @@
 Testrunner will look for and execute test suites that follow some simple
 conventions. Test modules should have a name prefixed with 'test', such as
 'testMyModule.py', and test modules are expected to define a module function
-named 'test_suite' that returns a PyUnit TestSuite object. By convention,
-we put test suites in 'tests' subdirectories of the packages they test.
+named 'test_suite' that returns a TestSuite object. By convention,
+we put test modules in a 'tests' sub-package of the package they test.
 
 Testrunner is used to run all checked in test suites before (final) releases
 are made, and can be used to quickly run a particular suite or all suites in
 a particular directory."""
 
-import sys, os, imp, string, getopt, traceback
+import getopt
+import imp
+import os
+import sys
+import traceback
 import unittest
 
 VERBOSE = 2
@@ -28,30 +32,31 @@
 class TestRunner:
     """Test suite runner"""
 
-    def __init__(self, basepath, verbosity=VERBOSE, results=[], mega_suite=0):
-        # initialize python path
-        self.basepath=path=basepath
+    def __init__(self, path, verbosity, mega_suite):
+        self.basepath = path
         self.verbosity = verbosity
-        self.results = results
+        self.results = []
         self.mega_suite = mega_suite
-        pjoin=os.path.join
+        # initialize python path
+        pjoin = os.path.join
         if sys.platform == 'win32':
-            sys.path.insert(0, pjoin(path, 'lib/python'))
-            sys.path.insert(1, pjoin(path, 'bin/lib'))
-            sys.path.insert(2, pjoin(path, 'bin/lib/plat-win'))
-            sys.path.insert(3, pjoin(path, 'bin/lib/win32'))
-            sys.path.insert(4, pjoin(path, 'bin/lib/win32/lib'))
-            sys.path.insert(5, path)
+            newpaths = [pjoin(path, 'lib', 'python'),
+                        pjoin(path, 'bin', 'lib'),
+                        pjoin(path, 'bin', 'lib', 'plat-win'),
+                        pjoin(path, 'bin', 'lib', 'win32'),
+                        pjoin(path, 'bin', 'lib', 'win32', 'lib'),
+                        path]
         else:
-            sys.path.insert(0, pjoin(path, 'lib/python'))
-            sys.path.insert(1, path)
+            newpaths = [pjoin(path, 'lib', 'python'),
+                        path]
+        sys.path[:0] = newpaths
 
     def getSuiteFromFile(self, filepath):
         if not os.path.isfile(filepath):
             raise ValueError, '%s is not a file' % filepath
-        path, filename=os.path.split(filepath)
-        name, ext=os.path.splitext(filename)
-        file, pathname, desc=imp.find_module(name, [path])
+        path, filename = os.path.split(filepath)
+        name, ext = os.path.splitext(filename)
+        file, pathname, desc = imp.find_module(name, [path])
         saved_syspath = sys.path[:]
         module = None
         try:
@@ -64,7 +69,7 @@
                 (tb_t, tb_v, tb_tb) = sys.exc_info()
                 self.report("Module %s failed to load\n%s: %s" % (pathname,
                         tb_t, tb_v))
-                self.report(string.join(traceback.format_tb(tb_tb)) + '\n')
+                self.report(''.join(traceback.format_tb(tb_tb)) + '\n')
                 del tb_tb
         finally:
             file.close()
@@ -75,76 +80,77 @@
             return None
         return function()
 
-    def smellsLikeATest(self, filepath, find=string.find):
+    def smellsLikeATest(self, filepath):
         path, name = os.path.split(filepath)
         fname, ext = os.path.splitext(name)
 
-        if name[:4]=='test' and name[-3:]=='.py' and \
-           name != 'testrunner.py':
-
-            file=open(filepath, 'r')
-            lines=file.readlines()
+        if (  name[:4] == 'test'
+              and name[-3:] == '.py'
+              and name != 'testrunner.py'):
+            file = open(filepath, 'r')
+            lines = file.readlines()
             file.close()
             for line in lines:
-                if (find(line, 'def test_suite(') > -1) or \
-                   (find(line, 'framework(') > -1):
-                    return 1
-        return 0
+                if (line.find('def test_suite(') > -1) or \
+                   (line.find('framework(') > -1):
+                    return True
+        return False
 
     def runSuite(self, suite):
         if suite:
-            runner=unittest.TextTestRunner(stream=sys.stderr,
-                                           verbosity=self.verbosity)
+            runner = self.getTestRunner()
             self.results.append(runner.run(suite))
         else:
-            self.report('No suitable tests found') 
+            self.report('No suitable tests found')
+
+    _runner = None
+
+    def getTestRunner(self):
+        if self._runner is None:
+            self._runner = self.createTestRunner()
+        return self._runner
+
+    def createTestRunner(self):
+        return unittest.TextTestRunner(stream=sys.stderr,
+                                       verbosity=self.verbosity)
 
     def report(self, message):
-        sys.stderr.write( '%s\n' % message )
+        print >>sys.stderr, message
 
     def runAllTests(self):
         """Run all tests found in the current working directory and
            all subdirectories."""
         self.runPath(self.basepath)
 
-    def listTestableNames( self, pathname ):
-        """
-            Return a list of the names to be traversed to build tests.
-        """
+    def listTestableNames(self, pathname):
+        """Return a list of the names to be traversed to build tests."""
         names = os.listdir(pathname)
         if "build" in names:
             # Don't recurse into build directories created by setup.py
             names.remove("build")
         if '.testinfo' in names:  # allow local control
-            f = open( os.path.join( pathname, '.testinfo' ) )
-            lines = filter( None, f.readlines() )
-            lines = map( lambda x: x[-1]=='\n' and x[:-1] or x, lines )
-            names = filter( lambda x: x and x[0] != '#', lines )
+            f = open(os.path.join(pathname, '.testinfo'))
+            lines = filter(None, f.readlines())
+            lines = map(lambda x: x[-1]=='\n' and x[:-1] or x, lines)
+            names = filter(lambda x: x and x[0] != '#', lines)
             f.close()
         return names
 
-    def extractSuite( self, pathname ):
-        """
-            Extract and return the appropriate test suite.
-        """
-        if os.path.isdir( pathname ):
-
+    def extractSuite(self, pathname):
+        """Extract and return the appropriate test suite."""
+        if os.path.isdir(pathname):
             suite = unittest.TestSuite()
-
-            for name in self.listTestableNames( pathname ):
-
-                fullpath = os.path.join( pathname, name )
-                sub_suite = self.extractSuite( fullpath )
+            for name in self.listTestableNames(pathname):
+                fullpath = os.path.join(pathname, name)
+                sub_suite = self.extractSuite(fullpath)
                 if sub_suite:
-                    suite.addTest( sub_suite )
-
+                    suite.addTest(sub_suite)
             return suite.countTestCases() and suite or None
 
-        elif self.smellsLikeATest( pathname ):
-
+        elif self.smellsLikeATest(pathname):
+            dirname, name = os.path.split(pathname)
             working_dir = os.getcwd()
             try:
-                dirname, name = os.path.split(pathname)
                 if dirname:
                     os.chdir(dirname)
                 try:
@@ -159,11 +165,10 @@
                     suite = None
             finally:
                 os.chdir(working_dir)
-
             return suite
 
-        else: # no test there!
-
+        else:
+            # no test there!
             return None
 
     def runPath(self, pathname):
@@ -173,11 +178,11 @@
             pathname = os.path.join(self.basepath, pathname)
 
         if self.mega_suite:
-            suite = self.extractSuite( pathname )
-            self.runSuite( suite )
+            suite = self.extractSuite(pathname)
+            self.runSuite(suite)
         else:
             for name in self.listTestableNames(pathname):
-                fullpath=os.path.join(pathname, name)
+                fullpath = os.path.join(pathname, name)
                 if os.path.isdir(fullpath):
                     self.runPath(fullpath)
                 elif self.smellsLikeATest(fullpath):
@@ -189,22 +194,22 @@
         dirname, name = os.path.split(filename)
         if dirname:
             if self.verbosity > 2:
-                sys.stderr.write('*** Changing directory to: %s\n' % dirname)
+                print >>sys.stderr, '*** Changing directory to:', dirname
             os.chdir(dirname)
         self.report('Running: %s' % filename)
         try:
-            suite=self.getSuiteFromFile(name)
+            suite = self.getSuiteFromFile(name)
         except KeyboardInterrupt:
             raise
         except:
             traceback.print_exc()
-            suite=None
+            suite = None
         if suite is not None:
             self.runSuite(suite)
         else:
             self.report('No test suite found in file:\n%s\n' % filename)
         if self.verbosity > 2:
-            sys.stderr.write('*** Restoring directory to: %s\n' % working_dir)
+            print >>sys.stderr, '*** Restoring directory to:', working_dir
         os.chdir(working_dir)
 
 def remove_stale_bytecode(arg, dirname, names):
@@ -218,8 +223,7 @@
                 os.unlink(fullname)
 
 def main(args):
-
-    usage_msg="""Usage: python testrunner.py options
+    usage_msg = """Usage: python testrunner.py options
 
     If run without options, testrunner will display this usage
     message. If you want to run all test suites found in all
@@ -229,41 +233,33 @@
     options:
 
        -a
-
           Run all tests found in all subdirectories of the current
           working directory.
 
        -m
-
           Run all tests in a single, giant suite (consolidates error
           reporting). [default]
 
        -M
-
           Run each test file's suite separately (noisier output, may
           help in isolating global effects later).
 
        -p
-
           Add 'lib/python' to the Python search path. [default]
 
        -P
-
           *Don't* add 'lib/python' to the Python search path.
 
        -d dirpath
-
           Run all tests found in the directory specified by dirpath,
           and recursively in all its subdirectories. The dirpath
           should be a full system path.
 
        -f filepath
-
           Run the test suite found in the file specified. The filepath
           should be a fully qualified path to the file to be run.
 
        -v level
-
           Set the Verbosity level to level.  Newer versions of
           unittest.py allow more options than older ones.  Allowed
           values are:
@@ -273,7 +269,6 @@
             2 - Verbose (default - produces a line of output for each test)
 
        -q
-
           Run tests without producing verbose output.  The tests are
           normally run in verbose mode, which produces a line of
           output for each test that includes the name of the test and
@@ -281,67 +276,62 @@
           running with -v1.
 
        -o filename
-
           Output test results to the specified file rather than
           to stderr.
 
        -h
-
           Display usage information.
     """
 
-    pathname=None
-    filename=None
-    test_all=None
+    pathname = None
+    filename = None
+    test_all = False
     verbosity = VERBOSE
-    mega_suite = 1
-    set_python_path = 1
+    mega_suite = True
+    set_python_path = True
 
-    options, arg=getopt.getopt(args, 'amPhd:f:v:qMo:')
+    options, arg = getopt.getopt(args, 'amPhd:f:v:qMo:')
     if not options:
         err_exit(usage_msg)
     for name, value in options:
-        name=name[1:]
-        if name == 'a':
-            test_all=1
-        elif name == 'm':
-            mega_suite = 1
-        elif name == 'M':
-            mega_suite = 0
-        elif name == 'p':
-            set_python_path = 1
-        elif name == 'P':
-            set_python_path = 0
-        elif name == 'd':
-            pathname=string.strip(value)
-        elif name == 'f':
-            filename=string.strip(value)
-        elif name == 'h':
+        if name == '-a':
+            test_all = True
+        elif name == '-m':
+            mega_suite = True
+        elif name == '-M':
+            mega_suite = False
+        elif name == '-p':
+            set_python_path = True
+        elif name == '-P':
+            set_python_path = False
+        elif name == '-d':
+            pathname = value.strip()
+        elif name == '-f':
+            filename = value.strip()
+        elif name == '-h':
             err_exit(usage_msg, 0)
-        elif name == 'v':
+        elif name == '-v':
             verbosity = int(value)
-        elif name == 'q':
+        elif name == '-q':
             verbosity = 1
-        elif name == 'o':
-            f = open(value,'w')
+        elif name == '-o':
+            f = open(value, 'w')
             sys.stderr = f
         else:
             err_exit(usage_msg)
 
     os.path.walk(os.curdir, remove_stale_bytecode, None)
 
-    testrunner = TestRunner( os.getcwd()
-                           , verbosity=verbosity
-                           , mega_suite=mega_suite)
+    testrunner = TestRunner(os.getcwd(), verbosity, mega_suite)
 
     if set_python_path:
         script = sys.argv[0]
-        script_dir = os.path.split( os.path.abspath( script ) )[0]
-        zope_dir = os.path.abspath( os.path.join( script_dir, '..' ) )
-        sw_home = os.path.join( zope_dir, 'lib', 'python' )
+        script_dir = os.path.dirname(os.path.abspath(script))
+        zope_dir = os.path.dirname(script_dir)
+        sw_home = os.path.join(zope_dir, 'lib', 'python')
         if verbosity > 1:
-            testrunner.report( "Adding %s to sys.path." % sw_home )
-        sys.path.insert( 0, sw_home )
+            testrunner.report("Adding %s to sys.path." % sw_home)
+        sys.path.insert(0, sw_home)
         os.environ['SOFTWARE_HOME'] = sw_home
 
     try:
@@ -358,7 +348,6 @@
     elif filename:
         testrunner.runFile(filename)
 
-
     ## Report overall errors / failures if there were any
     fails = reduce(lambda x, y: x + len(y.failures), testrunner.results, 0)
     errs  = reduce(lambda x, y: x + len(y.errors), testrunner.results, 0)
@@ -368,7 +357,8 @@
         if fails:
             msg += "total failures=%d" % fails
         if errs:
-            if fails: msg += ", "
+            if fails:
+                msg += ", "
             msg += "total errors=%d" % errs
         msg += ")"
         err_exit(msg, 1)