[Zope-Checkins] CVS: Zope3/lib/python/Zope/Testing - Builder.py:1.1.4.1

Tres Seaver tseaver@zope.com
Mon, 19 Nov 2001 19:26:17 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/Testing
In directory cvs.zope.org:/tmp/cvs-serv8568/lib/python/Zope/Testing

Added Files:
      Tag: Zope-3x-branch
	Builder.py 
Log Message:


  - Integrate test-crawler, to enable better use of 'unittestgui.py'.


=== Added File Zope3/lib/python/Zope/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.
        """
        from Zope import Testing
        zope_pkg, rest = os.path.split( Testing.__path__[0] )
        sw_home, rest = os.path.split( zope_pkg )
        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 = []
        loaded = []
        for candidate in self._candidates:
            try:
                suite.addTest( self.loadTestsFromName( '%s.test_suite'
                                                     % candidate ) )
                loaded.append( '%s (test_suite)' % candidate )
            except ValueError:
                try:
                    suite.addTest( self.loadTestsFromName( candidate ) )
                    append( '%s (implicit)' % candidate )
                except ValueError, msg:
                    self._cant_load.append( '[%s] %s' % ( candidate, msg ) )
            except Exception, msg:
                self._cant_load.append( '[%s] %s' % ( candidate, msg ) )
        return suite, loaded

    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 )
        suite, loaded = self._buildSuite()
        return suite

        

def allZopeTests():
    #suite, failed_imports = extractSuite( sw_home )
    #return suite
    return TestFinder().loadTestsFromPath()