""" Test Suites -----------
Provides a LazySuite, which is a suite whose test list is a generator function, and ContextSuite,which can run fixtures (setup/teardown functions or methods) for the context that contains its tests.
"""
if sys.version_info[:2] < (2, 6): import clr clr.AddReference("IronPython") from IronPython.Runtime.Exceptions import StringException else: class StringException(Exception): pass
#log.setLevel(logging.DEBUG)
# Singleton for default value -- see ContextSuite.__init__ below
return "%s.%s" % (cls.__module__, cls.__name__)
"""Error raised when a context suite sees tests from more than one context. """
"""A suite that may use a generator as its list of tests """ """Initialize the suite. tests may be an iterable or a generator """
return iter(self._tests)
def __repr__(self): return "<%s tests=generator (%s)>" % ( _strclass(self.__class__), id(self))
return object.__hash__(self)
# added to bypass run changes in 2.7's unittest for test in self._tests: if result.shouldStop: break test(result) return result
if self.test_generator is None: return False try: test = next(self.test_generator) if test is not None: self._precache.append(test) return True except StopIteration: pass return False
for test in self.test_generator: yield test
self.test_generator = tests() # Suites need special treatment: they must be called like # tests for their setup/teardown to run (if any) self.addTests([tests]) self.test_generator = None else:
"Access the tests in this suite. Access is through a " "generator, so iteration may not be repeatable.")
"""A suite with context.
A ContextSuite executes fixtures (setup and teardown functions or methods) for the context containing its tests.
The context may be explicitly passed. If it is not, a context (or nested set of contexts) will be constructed by examining the tests in the suite. """ 'setUpClass', 'setUpAll') 'teardownAll', 'tearDownClass', 'tearDownAll') 'setUp') 'teardown', 'tearDown') 'tearDownPackage')
config=None, resultProxy=None, can_split=True): config = Config()
def __repr__(self): return "<%s context=%s>" % ( _strclass(self.__class__), getattr(self.context, '__name__', self.context))
if self.error_context: return '%s:%s' % (repr(self), self.error_context) else: return repr(self)
# 2.3 compat -- force 2.4 call sequence
"""Hook for replacing error tuple output """ return sys.exc_info()
"""Bottleneck to fix up IronPython string exceptions """ e = self.exc_info() if sys.platform == 'cli': if isinstance(e[0], StringException): # IronPython throws these StringExceptions, but # traceback checks type(etype) == str. Make a real # string here. e = (str(e[0]), e[1], e[2])
return e
"""Run tests in suite inside of suite fixtures. """ # proxy the result for myself #import pdb #pdb.set_trace() else: result, orig = result, result except KeyboardInterrupt: raise except: self.error_context = 'setup' result.addError(self, self._exc_info()) return log.debug("stopping") break # each nose.case.Test will create its own result proxy # so the cases need the original result, to avoid proxy # chains finally: except KeyboardInterrupt: raise except: self.error_context = 'teardown' result.addError(self, self._exc_info())
context = self.context if context is None: return False if self.implementsAnyFixture(context, ctx_callback=ctx_callback): return True # My context doesn't have any, but its ancestors might factory = self.factory if factory: ancestors = factory.context.get(self, []) for ancestor in ancestors: if self.implementsAnyFixture( ancestor, ctx_callback=ctx_callback): return True return False
if isclass(context): names = self.classSetup + self.classTeardown else: names = self.moduleSetup + self.moduleTeardown if hasattr(context, '__path__'): names += self.packageSetup + self.packageTeardown # If my context has any fixture attribute, I have fixtures fixt = False for m in names: if hasattr(context, m): fixt = True break if ctx_callback is None: return fixt return ctx_callback(context, fixt)
# I have no tests log.debug("suite %s has no tests", id(self)) return log.debug("suite %s already set up", id(self)) return return # before running my own context's setup, I need to # ask the factory if my context's contexts' setups have been run # get a copy, since we'll be destroying it as we go self.setupContext(context) else: self.setupContext(context)
return # note that I ran the setup for this context, so that I'll run # the teardown in my teardown names = self.classSetup else:
if self.context is None: return "test suite" return "test suite for %s" % self.context
log.debug( "No reason to teardown (was_setup? %s was_torndown? %s)" % (self.was_setup, self.was_torndown)) return log.debug("No context to tear down") return
# for each ancestor... if the ancestor was setup # and I did the setup, I can do teardown log.debug('ancestor %s was not setup', ancestor) continue else: self.teardownContext(context)
return names = self.classTeardown else:
# FIXME the wrapping has to move to the factory? else: config=self.config, resultProxy=self.resultProxy)
"Access the tests in this suite. Tests are returned " "inside of a context wrapper.")
"""Factory for ContextSuites. Called with a collection of tests, the factory decides on a hierarchy of contexts by introspecting the collection or the tests themselves to find the objects containing the test objects. It always returns one suite, but that suite may consist of a hierarchy of nested suites. """ config = Config() self.suiteClass = suiteClass # Using a singleton to represent default instead of None allows # passing resultProxy=None to turn proxying off.
"""Return ``ContextSuite`` for tests. ``tests`` may either be a callable (in which case the resulting ContextSuite will have no parent context and be evaluated lazily) or an iterable. In that case the tests will wrapped in nose.case.Test, be examined and the context of each found and a suite of suites returned, organized into a stack with the outermost suites belonging to the outermost contexts. """ except MixedContextError: return self.makeSuite(self.mixedSuites(tests), None, **kw)
"""Return the ancestry of the context (that is, all of the packages and modules containing the context), in order of descent with the outermost ancestor last. This method is a generator. """ return # Methods include reference to module they are defined in, we # don't want that, instead want the module the class is in now # (classes are re-ancestored elsewhere). context = context.__self__.__class__ context = context.__self__.__class__ ancestors = context.__module__.split('.') else: raise TypeError("%s has no ancestors?" % context)
return None # Don't look at suites for contexts, only tests continue elif context != ctx: raise MixedContextError( "Tests with different contexts in same suite! %s != %s" % (context, ctx))
tests, context=context, config=self.config, factory=self, resultProxy=self.resultProxy, **kw) getattr(context, '__name__', None))
"""The complex case where there are tests that don't all share the same context. Groups tests into suites with common ancestors, according to the following (essentially tail-recursive) procedure:
Starting with the context of the first test, if it is not None, look for tests in the remaining tests that share that ancestor. If any are found, group into a suite with that ancestor as the context, and replace the current suite with that suite. Continue this process for each ancestor of the first test, until all ancestors have been processed. At this point if any tests remain, recurse with those tests as the input, returning a list of the common suite (which may be the suite or test we started with, if no common tests were found) plus the results of recursion. """ if not tests: return [] head = tests.pop(0) if not tests: return [head] # short circuit when none are left to combine suite = head # the common ancestry suite, so far tail = tests[:] context = getattr(head, 'context', None) if context is not None: ancestors = [context] + [a for a in self.ancestry(context)] for ancestor in ancestors: common = [suite] # tests with ancestor in common, so far remain = [] # tests that remain to be processed for test in tail: found_common = False test_ctx = getattr(test, 'context', None) if test_ctx is None: remain.append(test) continue if test_ctx is ancestor: common.append(test) continue for test_ancestor in self.ancestry(test_ctx): if test_ancestor is ancestor: common.append(test) found_common = True break if not found_common: remain.append(test) if common: suite = self.makeSuite(common, ancestor) tail = self.mixedSuites(remain) return [suite] + tail
log.debug("I won't wrap") return tests elif isinstance(test, ContextList): wrapped.append(self.makeSuite(test, context=test.context)) else: wrapped.append( Test(test, config=self.config, resultProxy=self.resultProxy) )
"""Not quite a suite -- a group of tests in a context. This is used to hint the ContextSuiteFactory about what context the tests belong to, in cases where it may be ambiguous or missing. """
"""Wraps suite and calls final function after suite has executed. Used to call final functions in cases (like running in the standard test runner) where test running is not under nose's control. """ super(FinalizingSuiteWrapper, self).__init__() self.suite = suite self.finalize = finalize
return self.run(*arg, **kw)
# 2.7 compat return iter(self.suite)
try: return self.suite(*arg, **kw) finally: self.finalize(*arg, **kw)
# backwards compat -- sort of raise NotImplementedError( "TestDir is not usable with nose 0.10. The class is present " "in nose.suite for backwards compatibility purposes but it " "may not be used.")
raise NotImplementedError( "TestModule is not usable with nose 0.10. The class is present " "in nose.suite for backwards compatibility purposes but it " "may not be used.") |