[Zope-Checkins] CVS: Zope/lib/python/Testing - Builder.py:1.1.2.1
Tres Seaver
tseaver@zope.com
Sun, 18 Nov 2001 23:20:59 -0500
Update of /cvs-repository/Zope/lib/python/Testing
In directory cvs.zope.org:/tmp/cvs-serv31428/lib/python/Testing
Added Files:
Tag: tseaver-utxr-refactoring-branch
Builder.py
Log Message:
- Moved utilitiy functions for finding tests out into an importable
module, so that they can be run directly by an alternate runner
(e.g., the Tkinter-based 'unittestgui'). For instance, from the
top of the Zope tree, the following now works::
$ PYTHONPATH=./lib/python:. python2.1 \
/usr/local/lib/python2.1/unittestgui.py Testing.Builder.allZopeTests
It produces a GUI which does the classic "red-bar/green-bar"
feedback cycle.
'unittestgui.py' is available at:
http://pyunit.sourceforge.net
- Reactored test-finding using 'os.path.walk' and the standard
'loadTestsFromName' provided by 'unittest'; this will allow,
for instance, creation of unit test modules without the stupid
'def test_suite' boilerplate.
TODO: update 'utilities/testrunner' to use the refactored finder.
=== Added File Zope/lib/python/Testing/Builder.py ===
"""
Utilities for buliding test suites by crawling directories.
"""
import unittest
import sys, os, string, re
from fnmatch import fnmatch
import imp, traceback
def getSuiteFromFile( filepath ):
"""
Extract and return the test suite from 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 ] )
saved_syspath = sys.path[:]
try:
module=imp.load_module( name, file, pathname, desc )
finally:
file.close()
sys.path[:] = saved_syspath
function=getattr( module, 'test_suite', None )
if function is None:
return None
return function()
TEST_FILE_NAME = re.compile( r'^test([A-Za-z0-9_]+).py$' )
TEST_SUITE_DEF = re.compile( r'^def test_suite\(' )
def smellsLikeATest( filepath
):
"""
Does 'filepath' match our criteria for unit test modules?
- filename matches 'test*.py';
- defines a 'test_suite' function at module scope.
"""
path, name = os.path.split( filepath )
fname, ext = os.path.splitext( name )
match = TEST_FILE_NAME.match( name )
if match and match.group(0) != 'runner':
return len( filter( TEST_SUITE_DEF.match
, open( filepath, 'r' ).readlines() ) ) > 0
return 0
def listTestableNames( pathname ):
"""
Return a list of the names to be traversed to build tests.
"""
names = os.listdir( pathname )
if '.testinfo' in names: # allow local control
f = open( os.path.join( pathname, '.testinfo' ) )
lines = filter( None, f.readlines() )
f.close()
lines = map( lambda x: # remove trailing newlines
x[-1]=='\n' and x[:-1] or x
, lines )
names = filter( lambda x: # skip comments
x and x[0] != '#'
, lines )
return names
def extractSuite( pathname ):
"""
Extract and return the appropriate test suite, along with
a list of filed imports.
"""
suite = None
import_failures = []
if os.path.isdir( pathname ):
suite = unittest.TestSuite()
for name in listTestableNames( pathname ):
fullpath = os.path.join( pathname, name )
sub_suite, sub_failures = extractSuite( fullpath )
if sub_suite:
suite.addTest( sub_suite )
import_failures.extend( sub_failures )
if not suite.countTestCases(): # ignore empty suites
suite = None
elif smellsLikeATest( pathname ):
working_dir = os.getcwd()
try:
dirname, name = os.path.split( pathname )
if dirname:
os.chdir( dirname )
try:
suite = getSuiteFromFile( name )
except:
import_failures.append( pathname )
finally:
os.chdir( working_dir )
return suite, import_failures
class TestFinder( unittest.TestLoader ):
"""
Handle crawling the filesystem, looking for tests.
"""
def __init__( self, sw_home=None ):
if sw_home is None:
sw_home = self._guessSoftwareHome()
self._sw_home = sw_home
self._sw_home_len = len( sw_home.split( os.sep ) )
self._candiates = []
self._cant_load = []
def _guessSoftwareHome( self ):
"""
Attempt to guess where SOFTWARE_HOME is.
"""
import Testing
sw_home, rest = os.path.split( Testing.__path__[0] )
return sw_home
def _splitPath( self, path ):
"""
Return a list of path elements, relative to sw_home.
"""
return path.split( os.sep )[ self._sw_home_len : ]
def _visit( self, arg, dirname, names ):
"""
Visitor for os.path.walk.
"""
names[:] = listTestableNames( dirname )
for name in names:
if fnmatch( name, 'test*.py' ) and name != 'testrunner.py':
self._addCandidate( dirname, name )
def _addCandidate( self, dirname, name ):
"""
Append a candidate module path.
"""
elements = self._splitPath( dirname )
basename, ext = os.path.splitext( name )
elements.append( basename )
self._candidates.append( '.'.join( elements ) )
def _buildSuite( self ):
"""
Build a suite from our candidate modules.
"""
suite = unittest.TestSuite()
self._cant_load = []
for candidate in self._candidates:
try:
suite.addTest( self.loadTestsFromName( '%s.test_suite'
% candidate ) )
except ValueError:
try:
suite.addTest( self.loadTestsFromName( candidate ) )
except ValueError, msg:
self._cant_load.append( msg )
return suite
def loadTestsFromPath( self, path=None ):
self._candidates = []
self._cant_load = []
if path is None:
path = self._sw_home
os.path.walk( path, self._visit, None )
try:
return self._buildSuite()
finally:
for cl in self._cant_load:
print "Can't load: %s" % cl
def allZopeTests():
#suite, failed_imports = extractSuite( sw_home )
#return suite
return TestFinder().loadTestsFromPath()