Changeset 113

Show
Ignore:
Timestamp:
Mon Nov 13 17:06:48 2006
Author:
jpellerin
Message:

Began rewriting loader and suite classes to be more clear.

Files:

Legend:

Unmodified
Added
Removed
Modified
  • trunk/unit_tests/test_loader.py

    r93 r113  
    248 248          
    249 249 if __name__ == '__main__':  
    250       #import logging  
    251       #logging.basicConfig()  
    252       #logging.getLogger('').setLevel(0)  
    253       unittest.main(testLoader=loader.TestLoader())  
      250     import logging  
      251     logging.basicConfig()  
      252     logging.getLogger('').setLevel(0)  
      253     #logging.getLogger('nose.importer').setLevel(0)  
      254     unittest.main() #testLoader=loader.TestLoader())  
  • trunk/nose/core.py

    r96 r113  
    15 15 from nose.proxy import ResultProxySuite  
    16 16 from nose.result import Result  
    17   from nose.suite import LazySuite, TestModule  
      17 from nose.suite import LazySuite  
    17 17 from nose.util import absdir, tolist  
    18 18 from nose.importer import add_path  
     
    469 469     testLoader = defaultTestLoader(conf)  
    470 470     def collector(conf, loader):  
    471           return TestModule(loader.loadTestsFromModule, conf, name)  
      471         return loader.loadTestsFromModule(name=name)  
    471 471     main(defaultTest=collector, testLoader=testLoader)     
    472 472  
  • trunk/nose/suite.py

    r86 r113  
    4 4 """  
    5 5 import logging  
      6 import os  
    6 7 import sys  
    7 8 import unittest  
     
    13 14 log = logging.getLogger('nose.suite')  
    14 15  
    15    
    16   class LazySuite(unittest.TestSuite):  
    17       """Generator-based test suite. Pass a callable that returns an iterable of  
    18       tests, and a nose.config.Config. Also provides hooks (setUp and tearDown)  
    19       for suite-level fixtures.  
      16 class TestSuite(unittest.TestSuite):  
      17     """A test suite with setup and teardown methods.  
    20 18     """  
    21       # _exc_info_to_string needs this property  
    22       failureException = unittest.TestCase.failureException  
    23        
    24       def __init__(self, loadtests, conf=None):  
    25           self._loadtests = loadtests  
    26           if conf is None:  
    27               conf = Config()  
    28           self.conf = conf  
    29    
    30       def loadtests(self):  
    31           for test in self._loadtests():  
    32               yield test  
    33    
    34       # lazy property so subclasses can override loadtests()  
    35       _tests = property(lambda self: self.loadtests(),  
    36                         None, None,  
    37                         'Tests in this suite (iter)')  
    38    
    39 19     def __call__(self, *arg, **kw):  
    40 20         self.run(*arg, **kw)  
      21  
      22     def id(self):  
      23         return self.__str__()  
    41 24          
    42 25     def run(self, result):  
     
    64 47         finally:  
    65 48             result.stopTest(self)  
    66                
      49  
    66 49     def setUp(self):  
    67 50         pass  
     
    75 58  
    76 59  
      60 class ModuleSuite(TestSuite):  
      61     """Suite of tests in a module.  
      62     """  
      63     def __init__(self, tests, module=None, error=None, **kw):  
      64         super(ModuleSuite, self).__init__(tests=tests, **kw)  
      65         self.module = module  
      66         self.error = error  
      67  
      68     def __repr__(self):  
      69         try:  
      70             name = self.module.__name__  
      71             if hasattr(self.module, '__path__') and self.module.__path__:  
      72                 path = self.module.__path__[0]  
      73             else:                 
      74                 path = self.module.__file__  
      75             for i in xrange(0, len(name.split('.'))):  
      76                 path = os.path.dirname(path)  
      77             return "test module %s in %s" % (name, path)  
      78         except AttributeError:  
      79             from traceback import format_exception  
      80             if self.error:  
      81                 ef = ''.join(format_exception(*self.error))  
      82             else:  
      83                 ef = ''  
      84             return "test module (%s, %s)" % (self.module, ef)  
      85     __str__ = __repr__  
      86  
      87     def run(self, result):  
      88         if self.error:  
      89             result.addError(self.error)  
      90             return  
      91         # Don't bother running setup and teardown if we don't  
      92         # have any tests to run.  
      93         if not self._tests:  
      94             return  
      95         super(ModuleSuite, self).run(result)  
      96  
      97     def setUp(self):  
      98         """Run any package or module setup function found. For packages, setup  
      99         functions may be named 'setupPackage', 'setup_package', 'setUp',  
      100         or 'setup'. For modules, setup functions may be named  
      101         'setupModule', 'setup_module', 'setUp', or 'setup'. The setup  
      102         function may optionally accept a single argument; in that case,  
      103         the test package or module will be passed to the setup function.  
      104         """  
      105         log.debug('TestModule.setUp')  
      106         if hasattr(self.module, '__path__'):  
      107             names = ['setupPackage', 'setUpPackage', 'setup_package']  
      108         else:  
      109             names = ['setupModule', 'setUpModule', 'setup_module']  
      110         names += ['setUp', 'setup']  
      111         try_run(self.module, names)  
      112              
      113     def tearDown(self):  
      114         """Run any package or module teardown function found. For packages,  
      115         teardown functions may be named 'teardownPackage',  
      116         'teardown_package' or 'teardown'. For modules, teardown functions  
      117         may be named 'teardownModule', 'teardown_module' or  
      118         'teardown'. The teardown function may optionally accept a single  
      119         argument; in that case, the test package or module will be passed  
      120         to the teardown function.  
      121  
      122         The teardown function will be run only if any package or module  
      123         setup function completed successfully.  
      124         """  
      125         if hasattr(self.module, '__path__'):  
      126             names = ['teardownPackage', 'teardown_package']  
      127         else:  
      128             names = ['teardownModule', 'teardown_module']  
      129         names += ['tearDown', 'teardown']         
      130         try_run(self.module, names)  
      131  
      132 # backwards compatibility  
      133 TestModule = ModuleSuite  
      134  
      135  
      136 class LazySuite(TestSuite):  
      137     """Generator-based test suite. Pass a callable that returns an iterable of  
      138     tests, and a nose.config.Config. Also provides hooks (setUp and tearDown)  
      139     for suite-level fixtures.  
      140     """  
      141     # _exc_info_to_string needs this property  
      142     failureException = unittest.TestCase.failureException  
      143      
      144     def __init__(self, loadtests, conf=None, **kw):  
      145         self._loadtests = loadtests  
      146         if conf is None:  
      147             conf = Config()  
      148         self.conf = conf  
      149  
      150     def loadtests(self):  
      151         for test in self._loadtests():  
      152             yield test  
      153  
      154     # lazy property so subclasses can override loadtests()  
      155     _tests = property(lambda self: self.loadtests(),  
      156                       None, None,  
      157                       'Tests in this suite (iter)')  
      158  
      159  
    77 160 class GeneratorMethodTestSuite(LazySuite):  
    78 161     """Test suite for test methods that are generators.  
     
    134 217                                     self.importPath):  
    135 218             yield test  
    136    
    137                
    138   class TestModule(LazySuite):  
    139       """Lazy suite that collects tests from modules and packages.  
    140    
    141       This suite collects module members that match the testMatch  
    142       regular expression. For packages, it also collects any modules or  
    143       packages found in the package __path__ that match testMatch. For  
    144       modules that themselves do not match testMatch, the collector collects  
    145       doctests instead of test functions.  
    146    
    147       Before returning the first collected test, any defined setup method  
    148       will be run. Packages may define setup, setUp, setup_package or  
    149       setUpPackage, modules setup, setUp, setup_module, setupModule or  
    150       setUpModule. Likewise, teardown will be run if defined and if setup  
    151       ran successfully; teardown methods follow the same naming rules as  
    152       setup methods.  
    153       """  
    154       fromDirectory = None  
    155        
    156       def __init__(self, loadtests, conf, moduleName=None, path=None,  
    157                    module=None):  
    158           self.moduleName = moduleName  
    159           self.path = path  
    160           self.module = module  
    161           if module and moduleName is None:  
    162               self.moduleName = module.__name__         
    163           LazySuite.__init__(self, loadtests, conf)  
    164            
    165       def __repr__(self):  
    166           return "test module %s in %s" % (self.moduleName, self.path)  
    167       __str__ = __repr__  
    168    
    169       def loadtests(self):  
    170           tests = self._loadtests(self.module, self.path)  
    171           try:  
    172               for test in tests:  
    173                   yield test  
    174           except TypeError:  
    175               # python 2.3: TestSuite not iterable  
    176               for test in tests._tests:  
    177                   yield test  
    178    
    179       def id(self):  
    180           return self.__str__()  
    181            
    182       def setUp(self):  
    183           """Run any package or module setup function found. For packages, setup  
    184           functions may be named 'setupPackage', 'setup_package', 'setUp',  
    185           or 'setup'. For modules, setup functions may be named  
    186           'setupModule', 'setup_module', 'setUp', or 'setup'. The setup  
    187           function may optionally accept a single argument; in that case,  
    188           the test package or module will be passed to the setup function.  
    189           """  
    190           log.debug('TestModule.setUp')  
    191           if self.module is None:  
    192               self.module = _import(self.moduleName, [self.path], self.conf)  
    193               log.debug('Imported %s from %s on %s', self.module,  
    194                         self.moduleName, self.path)  
    195           if hasattr(self.module, '__path__'):  
    196               names = ['setupPackage', 'setUpPackage', 'setup_package']  
    197           else:  
    198               names = ['setupModule', 'setUpModule', 'setup_module']  
    199           names += ['setUp', 'setup']  
    200           try_run(self.module, names)  
    201                
    202       def tearDown(self):  
    203           """Run any package or module teardown function found. For packages,  
    204           teardown functions may be named 'teardownPackage',  
    205           'teardown_package' or 'teardown'. For modules, teardown functions  
    206           may be named 'teardownModule', 'teardown_module' or  
    207           'teardown'. The teardown function may optionally accept a single  
    208           argument; in that case, the test package or module will be passed  
    209           to the teardown function.  
    210    
    211           The teardown function will be run only if any package or module  
    212           setup function completed successfully.  
    213           """  
    214           if hasattr(self.module, '__path__'):  
    215               names = ['teardownPackage', 'teardown_package']  
    216           else:  
    217               names = ['teardownModule', 'teardown_module']  
    218           names += ['tearDown', 'teardown']         
    219           try_run(self.module, names)  
  • trunk/nose/importer.py

    r86 r113  
    43 43     if conf.addPaths:  
    44 44         for p in path:  
    45               add_path(p)  
      45             if p is not None:  
      46                 add_path(p)  
    46 47  
    47 48     path = [ p for p in path if p is not None ]  
  • trunk/nose/loader.py

    r93 r113  
    10 10 from nose.case import *  
    11 11 from nose.config import Config  
    12   from nose.importer import add_path  
      12 from nose.importer import add_path, _import  
    12 12 from nose.plugins import call_plugins  
    13 13 from nose.selector import defaultSelector  
    14   from nose.suite import LazySuite, TestClass, TestDir, TestModule, \  
      14 from nose.suite import ModuleSuite, TestClass, TestDir, \  
    14 14     GeneratorMethodTestSuite  
    15 15 from nose.util import is_generator, split_test_name, try_run  
     
    19 19 log = logging.getLogger(__name__)  
    20 20  
      21 class LoaderException(Exception):  
      22     pass  
    21 23      
    22 24 class TestLoader(unittest.TestLoader):  
     
    30 32     long as possible.  
    31 33     """  
      34     suiteClass = ModuleSuite  
      35      
    32 36     def __init__(self, conf=None, selector=None):  
    33 37         if conf is None:  
     
    52 56                              "absolute paths (%s)" % dirname)  
    53 57         self.conf.working_dir = dirname  
    54    
      58         if importPath is None:  
      59             importPath = dirname  
      60          
    55 61         # Ensure that any directory we examine is on sys.path  
    56 62         if self.conf.addPaths:  
     
    78 84                 yield test  
    79 85          
    80       def loadTestsFromModule(self, module, importPath=None):  
    81           """Load tests from module at (optional) import path.  
      86     def loadTestsFromModule(self, module=None, name=None,  
      87                             package=None, importPath=None):  
      88         """Load tests from module at (optional) import path. One of module or  
      89         name must be supplied. If name is supplied, the module name (prepended  
      90         with package if that is not empty) will be imported.  
    82 91         """  
      92         if module is None:  
      93             if name is None:  
      94                 raise LoaderException("loadTestsFromModule: one of module or "  
      95                                       "name must be supplied")  
      96             if package is not None:  
      97                 modname = "%s.%s" % (package, name)  
      98             else:  
      99                 modname = name  
      100             try:  
      101                 log.debug("Importing %s from %s", modname, importPath)  
      102                 module = _import(modname, [importPath], self.conf)  
      103             except KeyboardInterrupt:  
      104                 raise  
      105             except:  
      106                 error = sys.exc_info()  
      107                 return self.suiteClass(tests=[], error=error)  
      108          
    83 109         log.debug("load from module %s (%s)", module, importPath)  
    84 110         tests = []  
     
    105 131                                  module.__name__, importPath))  
    106 132         # compat w/unittest  
    107           return self.suiteClass(tests)  
    108    
    109       def loadTestsFromModuleName(self, module_name, package=None,  
    110                                   importPath=None):  
    111           """Load tests from module_name. Specifiy module (name) to load module  
    112           from that module. Specify import path  
    113           to import from that path.  
    114           """  
    115           if package is not None:  
    116               module_name = "%s.%s" % (package, module_name)  
    117           if importPath is None:  
    118               importPath = self.conf.working_dir  
    119           yield TestModule(self.loadTestsFromModule,  
    120                            self.conf, module_name, importPath)  
      133         return self.suiteClass(tests, module=module)  
    121 134  
    122 135     def loadTestsFromName(self, name, module=None, importPath=None):  
     
    131 144         except AttributeError:  
    132 145             pass  
      146  
      147         if importPath is None:  
      148             importPath = self.conf.working_dir  
    133 149          
    134 150         tests = None  
     
    161 177         elif mod_name:  
    162 178             # handle module-like names  
    163               tests = self.loadTestsFromModuleName(name,  
    164                                                    package=module,  
    165                                                    importPath=importPath)  
      179             log.debug("%s is a module name", name)  
      180             yield self.loadTestsFromModule(name=name,  
      181                                            package=module,  
      182                                            importPath=importPath)  
    166 183         elif module:  
    167 184             # handle func-like names in a module             
     
    169 186         if tests:  
    170 187             for test in tests:  
    171                   yield test                 
      188                 yield test  
    171 188         # give plugins a chance  
    172 189         for plug in self.plugins:  
     
    194 211                 self.conf.tests.extend([ rel(n, module.__name__)  
    195 212                                          for n in names ])  
    196    
    197 213             try:  
    198 214                 mpath = os.path.dirname(module.__path__[0])  
     
    200 216                 mpath = os.path.dirname(module.__file__)  
    201 217                  
    202               #return self.loadTestsFromModule(module)  
    203               return TestModule(self.loadTestsFromModule,  
    204                                 self.conf,  
    205                                 module=module,  
    206                                 path=mpath)  
      218             return self.loadTestsFromModule(module, importPath=mpath)  
    207 219         else:  
    208 220             tests = []  
    209 221             for name in names:  
    210 222                 for test in self.loadTestsFromName(name):  
    211                       tests.append(test)         
      223                     tests.append(test)  
    211 223         return self.suiteClass(tests)  
    212 224  
     
    230 242             ispymod = False  
    231 243         if ispymod:  
    232               if module is not None:  
    233                   test = "%s.%s" % (module, test)  
    234               yield TestModule(self.loadTestsFromModule,  
    235                                self.conf, test, importPath)  
      244             yield self.loadTestsFromModule(name=test, package=module,  
      245                                            importPath=importPath)  
    236 246         # give plugins a chance  
    237 247         for plug in self.plugins:  
  • trunk/nose/selector.py

    r104 r113  
    210 210         """Is the directory a wanted test directory?  
    211 211  
    212           All package directories match, so long as they do not match exclude. All  
    213           other directories must match test requirements.  
      212         All package directories match, so long as they do not match exclude.  
      213         All other directories must match test requirements.  
    214 214         """  
    215 215         init = os.path.join(dirname, '__init__.py')  
  • trunk/nose/proxy.py

    r62 r113  
    10 10 import unittest  
    11 11 from nose.result import Result, ln  
      12 from nose.suite import TestSuite  
    12 13  
    13 14 log = logging.getLogger(__name__)  
     
    69 70  
    70 71      
    71   class ResultProxySuite(unittest.TestSuite):  
      72 class ResultProxySuite(TestSuite):  
    71 72     """Test suite that supports output capture, etc, by wrapping each test in  
    72 73     a TestProxy.