def loadTestsFromFile(self, filename): """Load tests from a non-module file. Default is to raise a ValueError; plugins may implement `loadTestsFromFile` to provide a list of tests loaded from the file. """ log.debug("Load from non-module file %s", filename) try: tests = [test for test in self.config.plugins.loadTestsFromFile(filename)] if tests: # Plugins can yield False to indicate that they were # unable to load tests from a file, but it was not an # error -- the file just had no tests to load. tests = filter(None, tests) return self.suiteClass(tests) else: # Nothing was able to even try to load from this file open(filename, 'r').close() # trigger os error raise ValueError("Unable to load tests from file %s" % filename) except (KeyboardInterrupt, SystemExit): raise except: exc = sys.exc_info() return self.suiteClass([Failure(*exc)])
def loadTestsFromTestCase(self, testCaseClass): """Load tests from a unittest.TestCase subclass. """ cases = [] plugins = self.config.plugins # Catch TypeError and AttributeError exceptions here and discard # them: the default plugin manager, NoPlugins, will raise TypeError # on generative calls. try: for case in plugins.loadTestsFromTestCase(testCaseClass): cases.append(case) except (TypeError, AttributeError): pass except (KeyboardInterrupt, SystemExit): raise except: cases.append(Failure(*sys.exc_info())) # For efficiency in the most common case, just call and return from # super. This avoids having to extract cases and rebuild a context # suite when there are no plugin-contributed cases. if not cases: return super(TestLoader, self).loadTestsFromTestCase(testCaseClass) cases.extend( [case for case in super(TestLoader, self).loadTestsFromTestCase(testCaseClass)]) return self.suiteClass(cases)
def makeTest(self, obj, parent=None): """Given a test object and its parent, return a test case or test suite. """ # Catch TypeError and AttributeError exceptions here and discard # them: the default plugin manager, NoPlugins, will raise TypeError # on generative calls. plug_tests = [] try: for test in self.config.plugins.makeTest(obj, parent): plug_tests.append(test) if plug_tests: return self.suiteClass(plug_tests) except (TypeError, AttributeError): pass except (KeyboardInterrupt, SystemExit): raise except: return Failure(*sys.exc_info()) if isinstance(obj, unittest.TestCase): return obj elif isclass(obj): if issubclass(obj, unittest.TestCase): return self.loadTestsFromTestCase(obj) else: return self.loadTestsFromTestClass(obj) elif ismethod(obj): if parent is None: parent = obj.__class__ if issubclass(parent, unittest.TestCase): return parent(obj.__name__) else: if isgenerator(obj): return self.loadTestsFromGeneratorMethod(obj, parent) else: return MethodTestCase(obj) elif isfunction(obj): if isgenerator(obj): return self.loadTestsFromGenerator(obj, parent) else: return FunctionTestCase(obj) else: return Failure(TypeError, "Can't make a test from %s" % obj)
def resolve(self, name, module): """Resolve name within module """ obj = module parts = name.split('.') for part in parts: parent, obj = obj, getattr(obj, part, None) if obj is None: # no such test obj = Failure(ValueError, "No such test %s" % name) return parent, obj
def loadTestsFromModule(self, module, discovered=False): """Load all tests from module and return a suite containing them. If the module has been discovered and is not test-like, the suite will be empty by default, though plugins may add their own tests. """ log.debug("Load from module %s", module) tests = [] test_classes = [] test_funcs = [] # For *discovered* modules, we only load tests when the module looks # testlike. For modules we've been directed to load, we always # look for tests. (discovered is set to True by loadTestsFromDir) if not discovered or self.selector.wantModule(module): for item in dir(module): test = getattr(module, item, None) # print "Check %s (%s) in %s" % (item, test, module.__name__) if isclass(test): if self.selector.wantClass(test): test_classes.append(test) elif isfunction(test) and self.selector.wantFunction(test): test_funcs.append(test) test_classes.sort(lambda a, b: cmp(a.__name__, b.__name__)) test_funcs.sort(cmp_lineno) tests = map(lambda t: self.makeTest(t, parent=module), test_classes + test_funcs) # Now, descend into packages # FIXME can or should this be lazy? # is this syntax 2.2 compatible? paths = getattr(module, '__path__', []) for path in paths: tests.extend(self.loadTestsFromDir(path)) # Catch TypeError and AttributeError exceptions here and discard # them: the default plugin manager, NoPlugins, will raise TypeError # on generative calls. try: for test in self.config.plugins.loadTestsFromModule(module): tests.append(test) except (TypeError, AttributeError): pass except (KeyboardInterrupt, SystemExit): raise except: tests.append(Failure(*sys.exc_info())) return self.suiteClass(ContextList(tests, context=module))
def generate(self, *arg, **kw): """Call all plugins, yielding each item in each non-None result. """ for p, meth in self.plugins: result = None try: result = meth(*arg, **kw) except (KeyboardInterrupt, SystemExit): raise except: exc = sys.exc_info() yield Failure(*exc) else: if result is not None: for r in result: yield r
def generate(g=generator, c=cls): for test in g(): try: test_func, arg = (test[0], test[1:]) except ValueError: test_func, arg = test[0], tuple() if not callable(test_func): test_func = getattr(c, test_func) if ismethod(test_func): yield MethodTestCase(test_func, arg=arg, descriptor=g) elif isfunction(test_func): # In this case we're forcing the 'MethodTestCase' # to run the inline function as its test call, # but using the generator method as the 'method of # record' (so no need to pass it as the descriptor) yield MethodTestCase(g, test=test_func, arg=arg) else: yield Failure(TypeError, "%s is not a function or method" % test_func)
def loadTestsFromName(self, name, module=None, discovered=False): """Load tests from the entity with the given name. The name may indicate a file, directory, module, or any object within a module. See `nose.util.split_test_name` for details on test name parsing. """ # FIXME refactor this method into little bites? log.debug("load from %s (%s)", name, module) suite = self.suiteClass # give plugins first crack plug_tests = self.config.plugins.loadTestsFromName(name, module) if plug_tests: return suite(plug_tests) addr = TestAddress(name, workingDir=self.workingDir) if module: # Two cases: # name is class.foo # The addr will be incorrect, since it thinks class.foo is # a dotted module name. It's actually a dotted attribute # name. In this case we want to use the full submitted # name as the name to load from the module. # name is module:class.foo # The addr will be correct. The part we want is the part after # the :, which is in addr.call. if addr.call: name = addr.call parent, obj = self.resolve(name, module) if isinstance(obj, Failure): return suite([obj]) else: return suite(ContextList([self.makeTest(obj, parent)], context=parent)) else: if addr.module: try: if addr.filename is None: module = resolve_name(addr.module) else: self.config.plugins.beforeImport( addr.filename, addr.module) # FIXME: to support module.name names, # do what resolve-name does and keep trying to # import, popping tail of module into addr.call, # until we either get an import or run out of # module parts try: module = self.importer.importFromPath( addr.filename, addr.module) finally: self.config.plugins.afterImport( addr.filename, addr.module) except (KeyboardInterrupt, SystemExit): raise except: exc = sys.exc_info() return suite([Failure(*exc)]) if addr.call: return self.loadTestsFromName(addr.call, module) else: return self.loadTestsFromModule( module, discovered=discovered) elif addr.filename: path = addr.filename if addr.call: package = getpackage(path) if package is None: return suite([ Failure(ValueError, "Can't find callable %s in file %s: " "file is not a python module" % (addr.call, path))]) return self.loadTestsFromName(addr.call, module=package) else: if op_isdir(path): # In this case we *can* be lazy since we know # that each module in the dir will be fully # loaded before its tests are executed; we # also know that we're not going to be asked # to load from . and ./some_module.py *as part # of this named test load* return LazySuite( lambda: self.loadTestsFromDir(path)) elif op_isfile(path): return self.loadTestsFromFile(path) else: return suite([ Failure(OSError, "No such file %s" % path)]) else: # just a function? what to do? I think it can only be # handled when module is not None return suite([ Failure(ValueError, "Unresolvable test name %s" % name)])
def loadTestsFromDir(self, path): """Load tests from the directory at path. This is a generator -- each suite of tests from a module or other file is yielded and is expected to be executed before the next file is examined. """ log.debug("load from dir %s", path) plugins = self.config.plugins plugins.beforeDirectory(path) if self.config.addPaths: paths_added = add_path(path, self.config) entries = os.listdir(path) entries.sort(lambda a, b: match_last(a, b, self.config.testMatch)) for entry in entries: if entry.startswith('.') or entry.startswith('_'): continue entry_path = op_abspath(op_join(path, entry)) is_file = op_isfile(entry_path) wanted = False if is_file: is_dir = False wanted = self.selector.wantFile(entry_path) else: is_dir = op_isdir(entry_path) if is_dir: wanted = self.selector.wantDirectory(entry_path) is_package = ispackage(entry_path) if wanted: if is_file: plugins.beforeContext() if entry.endswith('.py'): yield self.loadTestsFromName( entry_path, discovered=True) else: yield self.loadTestsFromFile(entry_path) plugins.afterContext() elif is_package: # Load the entry as a package: given the full path, # loadTestsFromName() will figure it out yield self.loadTestsFromName( entry_path, discovered=True) else: # Another test dir in this one: recurse lazily yield self.suiteClass( lambda: self.loadTestsFromDir(entry_path)) # Catch TypeError and AttributeError exceptions here and discard # them: the default plugin manager, NoPlugins, will raise TypeError # on generative calls. try: tests = [] for test in plugins.loadTestsFromDir(path): tests.append(test) yield self.suiteClass(tests) except (TypeError, AttributeError): pass except (KeyboardInterrupt, SystemExit): raise except: yield self.suiteClass([Failure(*sys.exc_info())]) # pop paths if self.config.addPaths: map(remove_path, paths_added) plugins.afterDirectory(path)
return False return sel.wantMethod(item) cases = [self.makeTest(getattr(cls, case), cls) for case in filter(wanted, dir(cls))] # Catch TypeError and AttributeError exceptions here and discard # them: the default plugin manager, NoPlugins, will raise TypeError # on generative calls. try: for test in self.config.plugins.loadTestsFromTestClass(cls): cases.append(test) except (TypeError, AttributeError): pass except (KeyboardInterrupt, SystemExit): raise except: cases.append(Failure(*sys.exc_info())) return self.suiteClass(ContextList(cases, context=cls)) def makeTest(self, obj, parent=None): """Given a test object and its parent, return a test case or test suite. """ # Catch TypeError and AttributeError exceptions here and discard # them: the default plugin manager, NoPlugins, will raise TypeError # on generative calls. plug_tests = [] try: for test in self.config.plugins.makeTest(obj, parent): plug_tests.append(test) if plug_tests: return self.suiteClass(plug_tests)