1""" 2Test Loader 3----------- 4 5nose's test loader implements the same basic functionality as its 6superclass, unittest.TestLoader, but extends it by more liberal 7interpretations of what may be a test and how a test may be named. 8""" 9from __future__ import generators 10 11import logging 12import os 13import sys 14import unittest 15import types 16from inspect import isfunction 17from nose.pyversion import unbound_method, ismethod 18from nose.case import FunctionTestCase, MethodTestCase 19from nose.failure import Failure 20from nose.config import Config 21from nose.importer import Importer, add_path, remove_path 22from nose.selector import defaultSelector, TestAddress 23from nose.util import func_lineno, getpackage, isclass, isgenerator, \ 24 ispackage, regex_last_key, resolve_name, transplant_func, \ 25 transplant_class, test_address 26from nose.suite import ContextSuiteFactory, ContextList, LazySuite 27from nose.pyversion import sort_list, cmp_to_key 28 29 30log = logging.getLogger(__name__) 31#log.setLevel(logging.DEBUG) 32 33# for efficiency and easier mocking 34op_normpath = os.path.normpath 35op_abspath = os.path.abspath 36op_join = os.path.join 37op_isdir = os.path.isdir 38op_isfile = os.path.isfile 39 40 41__all__ = ['TestLoader', 'defaultTestLoader'] 42 43 44class TestLoader(unittest.TestLoader): 45 """Test loader that extends unittest.TestLoader to: 46 47 * Load tests from test-like functions and classes that are not 48 unittest.TestCase subclasses 49 * Find and load test modules in a directory 50 * Support tests that are generators 51 * Support easy extensions of or changes to that behavior through plugins 52 """ 53 config = None 54 importer = None 55 workingDir = None 56 selector = None 57 suiteClass = None 58 59 def __init__(self, config=None, importer=None, workingDir=None, 60 selector=None): 61 """Initialize a test loader. 62 63 Parameters (all optional): 64 65 * config: provide a `nose.config.Config`_ or other config class 66 instance; if not provided a `nose.config.Config`_ with 67 default values is used. 68 * importer: provide an importer instance that implements 69 `importFromPath`. If not provided, a 70 `nose.importer.Importer`_ is used. 71 * workingDir: the directory to which file and module names are 72 relative. If not provided, assumed to be the current working 73 directory. 74 * selector: a selector class or instance. If a class is 75 provided, it will be instantiated with one argument, the 76 current config. If not provided, a `nose.selector.Selector`_ 77 is used. 78 """ 79 if config is None: 80 config = Config() 81 if importer is None: 82 importer = Importer(config=config) 83 if workingDir is None: 84 workingDir = config.workingDir 85 if selector is None: 86 selector = defaultSelector(config) 87 elif isclass(selector): 88 selector = selector(config) 89 self.config = config 90 self.importer = importer 91 self.workingDir = op_normpath(op_abspath(workingDir)) 92 self.selector = selector 93 if config.addPaths: 94 add_path(workingDir, config) 95 self.suiteClass = ContextSuiteFactory(config=config) 96 97 self._visitedPaths = set([]) 98 99 unittest.TestLoader.__init__(self) 100 101 def getTestCaseNames(self, testCaseClass): 102 """Override to select with selector, unless 103 config.getTestCaseNamesCompat is True 104 """ 105 if self.config.getTestCaseNamesCompat: 106 return unittest.TestLoader.getTestCaseNames(self, testCaseClass) 107 108 def wanted(attr, cls=testCaseClass, sel=self.selector): 109 item = getattr(cls, attr, None) 110 if isfunction(item): 111 item = unbound_method(cls, item) 112 elif not ismethod(item): 113 return False 114 return sel.wantMethod(item) 115 116 cases = filter(wanted, dir(testCaseClass)) 117 118 # add runTest if nothing else picked 119 if not cases and hasattr(testCaseClass, 'runTest'): 120 cases = ['runTest'] 121 if self.sortTestMethodsUsing: 122 sort_list(cases, cmp_to_key(self.sortTestMethodsUsing)) 123 return cases 124 125 def _haveVisited(self, path): 126 # For cases where path is None, we always pretend we haven't visited 127 # them. 128 if path is None: 129 return False 130 131 return path in self._visitedPaths 132 133 def _addVisitedPath(self, path): 134 if path is not None: 135 self._visitedPaths.add(path) 136 137 def loadTestsFromDir(self, path): 138 """Load tests from the directory at path. This is a generator 139 -- each suite of tests from a module or other file is yielded 140 and is expected to be executed before the next file is 141 examined. 142 """ 143 log.debug("load from dir %s", path) 144 plugins = self.config.plugins 145 plugins.beforeDirectory(path) 146 if self.config.addPaths: 147 paths_added = add_path(path, self.config) 148 149 entries = os.listdir(path) 150 sort_list(entries, regex_last_key(self.config.testMatch)) 151 for entry in entries: 152 # this hard-coded initial-dot test will be removed: 153 # http://code.google.com/p/python-nose/issues/detail?id=82 154 if entry.startswith('.'): 155 continue 156 entry_path = op_abspath(op_join(path, entry)) 157 is_file = op_isfile(entry_path) 158 wanted = False 159 if is_file: 160 is_dir = False 161 wanted = self.selector.wantFile(entry_path) 162 else: 163 is_dir = op_isdir(entry_path) 164 if is_dir: 165 # this hard-coded initial-underscore test will be removed: 166 # http://code.google.com/p/python-nose/issues/detail?id=82 167 if entry.startswith('_'): 168 continue 169 wanted = self.selector.wantDirectory(entry_path) 170 is_package = ispackage(entry_path) 171 172 # Python 3.3 now implements PEP 420: Implicit Namespace Packages. 173 # As a result, it's now possible that parent paths that have a 174 # segment with the same basename as our package ends up 175 # in module.__path__. So we have to keep track of what we've 176 # visited, and not-revisit them again. 177 if wanted and not self._haveVisited(entry_path): 178 self._addVisitedPath(entry_path) 179 if is_file: 180 plugins.beforeContext() 181 if entry.endswith('.py'): 182 yield self.loadTestsFromName( 183 entry_path, discovered=True) 184 else: 185 yield self.loadTestsFromFile(entry_path) 186 plugins.afterContext() 187 elif is_package: 188 # Load the entry as a package: given the full path, 189 # loadTestsFromName() will figure it out 190 yield self.loadTestsFromName( 191 entry_path, discovered=True) 192 else: 193 # Another test dir in this one: recurse lazily 194 yield self.suiteClass( 195 lambda: self.loadTestsFromDir(entry_path)) 196 tests = [] 197 for test in plugins.loadTestsFromDir(path): 198 tests.append(test) 199 # TODO: is this try/except needed? 200 try: 201 if tests: 202 yield self.suiteClass(tests) 203 except (KeyboardInterrupt, SystemExit): 204 raise 205 except: 206 yield self.suiteClass([Failure(*sys.exc_info())]) 207 208 # pop paths 209 if self.config.addPaths: 210 for p in paths_added: 211 remove_path(p) 212 plugins.afterDirectory(path) 213 214 def loadTestsFromFile(self, filename): 215 """Load tests from a non-module file. Default is to raise a 216 ValueError; plugins may implement `loadTestsFromFile` to 217 provide a list of tests loaded from the file. 218 """ 219 log.debug("Load from non-module file %s", filename) 220 try: 221 tests = [test for test in 222 self.config.plugins.loadTestsFromFile(filename)] 223 if tests: 224 # Plugins can yield False to indicate that they were 225 # unable to load tests from a file, but it was not an 226 # error -- the file just had no tests to load. 227 tests = filter(None, tests) 228 return self.suiteClass(tests) 229 else: 230 # Nothing was able to even try to load from this file 231 open(filename, 'r').close() # trigger os error 232 raise ValueError("Unable to load tests from file %s" 233 % filename) 234 except (KeyboardInterrupt, SystemExit): 235 raise 236 except: 237 exc = sys.exc_info() 238 return self.suiteClass( 239 [Failure(exc[0], exc[1], exc[2], 240 address=(filename, None, None))]) 241 242 def loadTestsFromGenerator(self, generator, module): 243 """Lazy-load tests from a generator function. The generator function 244 may yield either: 245 246 * a callable, or 247 * a function name resolvable within the same module 248 """ 249 def generate(g=generator, m=module): 250 try: 251 for test in g(): 252 test_func, arg = self.parseGeneratedTest(test) 253 if not callable(test_func): 254 test_func = getattr(m, test_func) 255 yield FunctionTestCase(test_func, arg=arg, descriptor=g) 256 except KeyboardInterrupt: 257 raise 258 except: 259 exc = sys.exc_info() 260 yield Failure(exc[0], exc[1], exc[2], 261 address=test_address(generator)) 262 return self.suiteClass(generate, context=generator, can_split=False) 263 264 def loadTestsFromGeneratorMethod(self, generator, cls): 265 """Lazy-load tests from a generator method. 266 267 This is more complicated than loading from a generator function, 268 since a generator method may yield: 269 270 * a function 271 * a bound or unbound method, or 272 * a method name 273 """ 274 # convert the unbound generator method 275 # into a bound method so it can be called below 276 if hasattr(generator, 'im_class'): 277 cls = generator.im_class 278 inst = cls() 279 method = generator.__name__ 280 generator = getattr(inst, method) 281 282 def generate(g=generator, c=cls): 283 try: 284 for test in g(): 285 test_func, arg = self.parseGeneratedTest(test) 286 if not callable(test_func): 287 test_func = unbound_method(c, getattr(c, test_func)) 288 if ismethod(test_func): 289 yield MethodTestCase(test_func, arg=arg, descriptor=g) 290 elif callable(test_func): 291 # In this case we're forcing the 'MethodTestCase' 292 # to run the inline function as its test call, 293 # but using the generator method as the 'method of 294 # record' (so no need to pass it as the descriptor) 295 yield MethodTestCase(g, test=test_func, arg=arg) 296 else: 297 yield Failure( 298 TypeError, 299 "%s is not a callable or method" % test_func) 300 except KeyboardInterrupt: 301 raise 302 except: 303 exc = sys.exc_info() 304 yield Failure(exc[0], exc[1], exc[2], 305 address=test_address(generator)) 306 return self.suiteClass(generate, context=generator, can_split=False) 307 308 def loadTestsFromModule(self, module, path=None, discovered=False): 309 """Load all tests from module and return a suite containing 310 them. If the module has been discovered and is not test-like, 311 the suite will be empty by default, though plugins may add 312 their own tests. 313 """ 314 log.debug("Load from module %s", module) 315 tests = [] 316 test_classes = [] 317 test_funcs = [] 318 # For *discovered* modules, we only load tests when the module looks 319 # testlike. For modules we've been directed to load, we always 320 # look for tests. (discovered is set to True by loadTestsFromDir) 321 if not discovered or self.selector.wantModule(module): 322 for item in dir(module): 323 test = getattr(module, item, None) 324 # print "Check %s (%s) in %s" % (item, test, module.__name__) 325 if isclass(test): 326 if self.selector.wantClass(test): 327 test_classes.append(test) 328 elif isfunction(test) and self.selector.wantFunction(test): 329 test_funcs.append(test) 330 sort_list(test_classes, lambda x: x.__name__) 331 sort_list(test_funcs, func_lineno) 332 tests = map(lambda t: self.makeTest(t, parent=module), 333 test_classes + test_funcs) 334 335 # Now, descend into packages 336 # FIXME can or should this be lazy? 337 # is this syntax 2.2 compatible? 338 module_paths = getattr(module, '__path__', []) 339 340 if path: 341 path = os.path.normcase(os.path.realpath(path)) 342 343 for module_path in module_paths: 344 log.debug("Load tests from module path %s?", module_path) 345 log.debug("path: %s os.path.realpath(%s): %s", 346 path, os.path.normcase(module_path), 347 os.path.realpath(os.path.normcase(module_path))) 348 if (self.config.traverseNamespace or not path) or \ 349 os.path.realpath( 350 os.path.normcase(module_path)).startswith(path): 351 # Egg files can be on sys.path, so make sure the path is a 352 # directory before trying to load from it. 353 if os.path.isdir(module_path): 354 tests.extend(self.loadTestsFromDir(module_path)) 355 356 for test in self.config.plugins.loadTestsFromModule(module, path): 357 tests.append(test) 358 359 return self.suiteClass(ContextList(tests, context=module)) 360 361 def loadTestsFromName(self, name, module=None, discovered=False): 362 """Load tests from the entity with the given name. 363 364 The name may indicate a file, directory, module, or any object 365 within a module. See `nose.util.split_test_name` for details on 366 test name parsing. 367 """ 368 # FIXME refactor this method into little bites? 369 log.debug("load from %s (%s)", name, module) 370 371 suite = self.suiteClass 372 373 # give plugins first crack 374 plug_tests = self.config.plugins.loadTestsFromName(name, module) 375 if plug_tests: 376 return suite(plug_tests) 377 378 addr = TestAddress(name, workingDir=self.workingDir) 379 if module: 380 # Two cases: 381 # name is class.foo 382 # The addr will be incorrect, since it thinks class.foo is 383 # a dotted module name. It's actually a dotted attribute 384 # name. In this case we want to use the full submitted 385 # name as the name to load from the module. 386 # name is module:class.foo 387 # The addr will be correct. The part we want is the part after 388 # the :, which is in addr.call. 389 if addr.call: 390 name = addr.call 391 parent, obj = self.resolve(name, module) 392 if (isclass(parent) 393 and getattr(parent, '__module__', None) != module.__name__ 394 and not isinstance(obj, Failure)): 395 parent = transplant_class(parent, module.__name__) 396 obj = getattr(parent, obj.__name__) 397 log.debug("parent %s obj %s module %s", parent, obj, module) 398 if isinstance(obj, Failure): 399 return suite([obj]) 400 else: 401 return suite(ContextList([self.makeTest(obj, parent)], 402 context=parent)) 403 else: 404 if addr.module: 405 try: 406 if addr.filename is None: 407 module = resolve_name(addr.module) 408 else: 409 self.config.plugins.beforeImport( 410 addr.filename, addr.module) 411 # FIXME: to support module.name names, 412 # do what resolve-name does and keep trying to 413 # import, popping tail of module into addr.call, 414 # until we either get an import or run out of 415 # module parts 416 try: 417 module = self.importer.importFromPath( 418 addr.filename, addr.module) 419 finally: 420 self.config.plugins.afterImport( 421 addr.filename, addr.module) 422 except (KeyboardInterrupt, SystemExit): 423 raise 424 except: 425 exc = sys.exc_info() 426 return suite([Failure(exc[0], exc[1], exc[2], 427 address=addr.totuple())]) 428 if addr.call: 429 return self.loadTestsFromName(addr.call, module) 430 else: 431 return self.loadTestsFromModule( 432 module, addr.filename, 433 discovered=discovered) 434 elif addr.filename: 435 path = addr.filename 436 if addr.call: 437 package = getpackage(path) 438 if package is None: 439 return suite([ 440 Failure(ValueError, 441 "Can't find callable %s in file %s: " 442 "file is not a python module" % 443 (addr.call, path), 444 address=addr.totuple())]) 445 return self.loadTestsFromName(addr.call, module=package) 446 else: 447 if op_isdir(path): 448 # In this case we *can* be lazy since we know 449 # that each module in the dir will be fully 450 # loaded before its tests are executed; we 451 # also know that we're not going to be asked 452 # to load from . and ./some_module.py *as part 453 # of this named test load* 454 return LazySuite( 455 lambda: self.loadTestsFromDir(path)) 456 elif op_isfile(path): 457 return self.loadTestsFromFile(path) 458 else: 459 return suite([ 460 Failure(OSError, "No such file %s" % path, 461 address=addr.totuple())]) 462 else: 463 # just a function? what to do? I think it can only be 464 # handled when module is not None 465 return suite([ 466 Failure(ValueError, "Unresolvable test name %s" % name, 467 address=addr.totuple())]) 468 469 def loadTestsFromNames(self, names, module=None): 470 """Load tests from all names, returning a suite containing all 471 tests. 472 """ 473 plug_res = self.config.plugins.loadTestsFromNames(names, module) 474 if plug_res: 475 suite, names = plug_res 476 if suite: 477 return self.suiteClass([ 478 self.suiteClass(suite), 479 unittest.TestLoader.loadTestsFromNames(self, names, module) 480 ]) 481 return unittest.TestLoader.loadTestsFromNames(self, names, module) 482 483 def loadTestsFromTestCase(self, testCaseClass): 484 """Load tests from a unittest.TestCase subclass. 485 """ 486 cases = [] 487 plugins = self.config.plugins 488 for case in plugins.loadTestsFromTestCase(testCaseClass): 489 cases.append(case) 490 # For efficiency in the most common case, just call and return from 491 # super. This avoids having to extract cases and rebuild a context 492 # suite when there are no plugin-contributed cases. 493 if not cases: 494 return super(TestLoader, self).loadTestsFromTestCase(testCaseClass) 495 cases.extend( 496 [case for case in 497 super(TestLoader, self).loadTestsFromTestCase(testCaseClass)]) 498 return self.suiteClass(cases) 499 500 def loadTestsFromTestClass(self, cls): 501 """Load tests from a test class that is *not* a unittest.TestCase 502 subclass. 503 504 In this case, we can't depend on the class's `__init__` taking method 505 name arguments, so we have to compose a MethodTestCase for each 506 method in the class that looks testlike. 507 """ 508 def wanted(attr, cls=cls, sel=self.selector): 509 item = getattr(cls, attr, None) 510 if isfunction(item): 511 item = unbound_method(cls, item) 512 elif not ismethod(item): 513 return False 514 return sel.wantMethod(item) 515 cases = [self.makeTest(getattr(cls, case), cls) 516 for case in filter(wanted, dir(cls))] 517 for test in self.config.plugins.loadTestsFromTestClass(cls): 518 cases.append(test) 519 return self.suiteClass(ContextList(cases, context=cls)) 520 521 def makeTest(self, obj, parent=None): 522 try: 523 return self._makeTest(obj, parent) 524 except (KeyboardInterrupt, SystemExit): 525 raise 526 except: 527 exc = sys.exc_info() 528 try: 529 addr = test_address(obj) 530 except KeyboardInterrupt: 531 raise 532 except: 533 addr = None 534 return Failure(exc[0], exc[1], exc[2], address=addr) 535 536 def _makeTest(self, obj, parent=None): 537 """Given a test object and its parent, return a test case 538 or test suite. 539 """ 540 plug_tests = [] 541 try: 542 addr = test_address(obj) 543 except KeyboardInterrupt: 544 raise 545 except: 546 addr = None 547 for test in self.config.plugins.makeTest(obj, parent): 548 plug_tests.append(test) 549 # TODO: is this try/except needed? 550 try: 551 if plug_tests: 552 return self.suiteClass(plug_tests) 553 except (KeyboardInterrupt, SystemExit): 554 raise 555 except: 556 exc = sys.exc_info() 557 return Failure(exc[0], exc[1], exc[2], address=addr) 558 559 if isfunction(obj) and parent and not isinstance(parent, types.ModuleType): 560 # This is a Python 3.x 'unbound method'. Wrap it with its 561 # associated class.. 562 obj = unbound_method(parent, obj) 563 564 if isinstance(obj, unittest.TestCase): 565 return obj 566 elif isclass(obj): 567 if parent and obj.__module__ != parent.__name__: 568 obj = transplant_class(obj, parent.__name__) 569 if issubclass(obj, unittest.TestCase): 570 return self.loadTestsFromTestCase(obj) 571 else: 572 return self.loadTestsFromTestClass(obj) 573 elif ismethod(obj): 574 if parent is None: 575 parent = obj.__class__ 576 if issubclass(parent, unittest.TestCase): 577 return parent(obj.__name__) 578 else: 579 if isgenerator(obj): 580 return self.loadTestsFromGeneratorMethod(obj, parent) 581 else: 582 return MethodTestCase(obj) 583 elif isfunction(obj): 584 if parent and obj.__module__ != parent.__name__: 585 obj = transplant_func(obj, parent.__name__) 586 if isgenerator(obj): 587 return self.loadTestsFromGenerator(obj, parent) 588 else: 589 return FunctionTestCase(obj) 590 else: 591 return Failure(TypeError, 592 "Can't make a test from %s" % obj, 593 address=addr) 594 595 def resolve(self, name, module): 596 """Resolve name within module 597 """ 598 obj = module 599 parts = name.split('.') 600 for part in parts: 601 parent, obj = obj, getattr(obj, part, None) 602 if obj is None: 603 # no such test 604 obj = Failure(ValueError, "No such test %s" % name) 605 return parent, obj 606 607 def parseGeneratedTest(self, test): 608 """Given the yield value of a test generator, return a func and args. 609 610 This is used in the two loadTestsFromGenerator* methods. 611 612 """ 613 if not isinstance(test, tuple): # yield test 614 test_func, arg = (test, tuple()) 615 elif len(test) == 1: # yield (test,) 616 test_func, arg = (test[0], tuple()) 617 else: # yield test, foo, bar, ... 618 assert len(test) > 1 # sanity check 619 test_func, arg = (test[0], test[1:]) 620 return test_func, arg 621 622defaultTestLoader = TestLoader 623 624