| """ |
| Test Suites |
| """ |
| from __future__ import generators |
| |
| import sys |
| import unittest |
| from nose_helper.case import Test |
| from nose_helper.config import Config |
| from nose_helper.util import isclass, resolve_name, try_run |
| PYTHON_VERSION_MAJOR = sys.version_info[0] |
| class LazySuite(unittest.TestSuite): |
| """A suite that may use a generator as its list of tests |
| """ |
| def __init__(self, tests=()): |
| self._set_tests(tests) |
| |
| def __iter__(self): |
| return iter(self._tests) |
| |
| def __hash__(self): |
| return object.__hash__(self) |
| |
| def addTest(self, test): |
| self._precache.append(test) |
| |
| def __nonzero__(self): |
| if self._precache: |
| return True |
| if self.test_generator is None: |
| return False |
| try: |
| test = self.test_generator.next() |
| if test is not None: |
| self._precache.append(test) |
| return True |
| except StopIteration: |
| pass |
| return False |
| |
| def _get_tests(self): |
| if self.test_generator is not None: |
| for i in self.test_generator: |
| yield i |
| for test in self._precache: |
| yield test |
| |
| def _set_tests(self, tests): |
| self._precache = [] |
| is_suite = isinstance(tests, unittest.TestSuite) |
| if hasattr(tests, '__call__') and not is_suite: |
| self.test_generator = tests() |
| self.test_generator_counter = list(tests()) |
| elif is_suite: |
| self.addTests([tests]) |
| self.test_generator = None |
| self.test_generator_counter = None |
| else: |
| self.addTests(tests) |
| self.test_generator = None |
| self.test_generator_counter = None |
| |
| def countTestCases(self): |
| counter = 0 |
| generator = self.test_generator_counter |
| if generator is not None: |
| for test in generator: |
| counter +=1 |
| for test in self._precache: |
| counter += test.countTestCases() |
| return counter |
| |
| _tests = property(_get_tests, _set_tests, None, |
| "Access the tests in this suite.") |
| |
| class ContextSuite(LazySuite): |
| """A suite with context. |
| """ |
| was_setup = False |
| was_torndown = False |
| classSetup = ('setup_class', 'setup_all', 'setupClass', 'setupAll', |
| 'setUpClass', 'setUpAll') |
| classTeardown = ('teardown_class', 'teardown_all', 'teardownClass', |
| 'teardownAll', 'tearDownClass', 'tearDownAll') |
| moduleSetup = ('setup_module', 'setupModule', 'setUpModule', 'setup', |
| 'setUp') |
| moduleTeardown = ('teardown_module', 'teardownModule', 'tearDownModule', |
| 'teardown', 'tearDown') |
| packageSetup = ('setup_package', 'setupPackage', 'setUpPackage') |
| packageTeardown = ('teardown_package', 'teardownPackage', |
| 'tearDownPackage') |
| |
| def __init__(self, tests=(), context=None, factory=None, |
| config=None): |
| |
| self.context = context |
| self.factory = factory |
| if config is None: |
| config = Config() |
| self.config = config |
| self.has_run = False |
| self.error_context = None |
| LazySuite.__init__(self, tests) |
| |
| def __hash__(self): |
| return object.__hash__(self) |
| |
| def __call__(self, *arg, **kw): |
| return self.run(*arg, **kw) |
| |
| def _exc_info(self): |
| return sys.exc_info() |
| |
| def addTests(self, tests, context=None): |
| if context: |
| self.context = context |
| if PYTHON_VERSION_MAJOR < 3 and isinstance(tests, basestring): |
| raise TypeError("tests must be an iterable of tests, not a string") |
| else: |
| if isinstance(tests, str): |
| raise TypeError("tests must be an iterable of tests, not a string") |
| for test in tests: |
| self.addTest(test) |
| |
| def run(self, result): |
| """Run tests in suite inside of suite fixtures. |
| """ |
| result, orig = result, result |
| try: |
| self.setUp() |
| except KeyboardInterrupt: |
| raise |
| except: |
| self.error_context = 'setup' |
| result.addError(self, self._exc_info()) |
| return |
| try: |
| for test in self._tests: |
| if result.shouldStop: |
| break |
| test(orig) |
| finally: |
| self.has_run = True |
| try: |
| self.tearDown() |
| except KeyboardInterrupt: |
| raise |
| except: |
| self.error_context = 'teardown' |
| result.addError(self, self._exc_info()) |
| |
| def setUp(self): |
| if not self: |
| return |
| if self.was_setup: |
| return |
| context = self.context |
| if context is None: |
| return |
| |
| factory = self.factory |
| if factory: |
| ancestors = factory.context.get(self, [])[:] |
| while ancestors: |
| ancestor = ancestors.pop() |
| if ancestor in factory.was_setup: |
| continue |
| self.setupContext(ancestor) |
| if not context in factory.was_setup: |
| self.setupContext(context) |
| else: |
| self.setupContext(context) |
| self.was_setup = True |
| |
| def setupContext(self, context): |
| if self.factory: |
| if context in self.factory.was_setup: |
| return |
| self.factory.was_setup[context] = self |
| if isclass(context): |
| names = self.classSetup |
| else: |
| names = self.moduleSetup |
| if hasattr(context, '__path__'): |
| names = self.packageSetup + names |
| try_run(context, names) |
| |
| def tearDown(self): |
| if not self.was_setup or self.was_torndown: |
| return |
| self.was_torndown = True |
| context = self.context |
| if context is None: |
| return |
| |
| factory = self.factory |
| if factory: |
| ancestors = factory.context.get(self, []) + [context] |
| for ancestor in ancestors: |
| if not ancestor in factory.was_setup: |
| continue |
| if ancestor in factory.was_torndown: |
| continue |
| setup = factory.was_setup[ancestor] |
| if setup is self: |
| self.teardownContext(ancestor) |
| else: |
| self.teardownContext(context) |
| |
| def teardownContext(self, context): |
| if self.factory: |
| if context in self.factory.was_torndown: |
| return |
| self.factory.was_torndown[context] = self |
| if isclass(context): |
| names = self.classTeardown |
| else: |
| names = self.moduleTeardown |
| if hasattr(context, '__path__'): |
| names = self.packageTeardown + names |
| try_run(context, names) |
| |
| def _get_wrapped_tests(self): |
| for test in self._get_tests(): |
| if isinstance(test, Test) or isinstance(test, unittest.TestSuite): |
| yield test |
| else: |
| yield Test(test, |
| config=self.config) |
| |
| _tests = property(_get_wrapped_tests, LazySuite._set_tests, None, |
| "Access the tests in this suite. Tests are returned " |
| "inside of a context wrapper.") |
| |
| class ContextSuiteFactory(object): |
| suiteClass = ContextSuite |
| def __init__(self, config=None): |
| if config is None: |
| config = Config() |
| self.config = config |
| self.suites = {} |
| self.context = {} |
| self.was_setup = {} |
| self.was_torndown = {} |
| |
| def __call__(self, tests, **kw): |
| """Return 'ContextSuite' for tests. |
| """ |
| context = kw.pop('context', getattr(tests, 'context', None)) |
| if context is None: |
| tests = self.wrapTests(tests) |
| context = self.findContext(tests) |
| return self.makeSuite(tests, context, **kw) |
| |
| def ancestry(self, context): |
| """Return the ancestry of the context |
| """ |
| if context is None: |
| return |
| if hasattr(context, 'im_class'): |
| context = context.im_class |
| if hasattr(context, '__module__'): |
| ancestors = context.__module__.split('.') |
| elif hasattr(context, '__name__'): |
| ancestors = context.__name__.split('.')[:-1] |
| else: |
| raise TypeError("%s has no ancestors?" % context) |
| while ancestors: |
| yield resolve_name('.'.join(ancestors)) |
| ancestors.pop() |
| |
| def findContext(self, tests): |
| if hasattr(tests, '__call__') or isinstance(tests, unittest.TestSuite): |
| return None |
| context = None |
| for test in tests: |
| # Don't look at suites for contexts, only tests |
| ctx = getattr(test, 'context', None) |
| if ctx is None: |
| continue |
| if context is None: |
| context = ctx |
| return context |
| |
| def makeSuite(self, tests, context, **kw): |
| suite = self.suiteClass( |
| tests, context=context, config=self.config, factory=self, **kw) |
| if context is not None: |
| self.suites.setdefault(context, []).append(suite) |
| self.context.setdefault(suite, []).append(context) |
| for ancestor in self.ancestry(context): |
| self.suites.setdefault(ancestor, []).append(suite) |
| self.context[suite].append(ancestor) |
| |
| return suite |
| |
| def wrapTests(self, tests): |
| if hasattr(tests, '__call__') or isinstance(tests, unittest.TestSuite): |
| return tests |
| wrapped = [] |
| for test in tests: |
| if isinstance(test, Test) or isinstance(test, unittest.TestSuite): |
| wrapped.append(test) |
| elif isinstance(test, ContextList): |
| wrapped.append(self.makeSuite(test, context=test.context)) |
| else: |
| wrapped.append( |
| Test(test, config=self.config) |
| ) |
| return wrapped |
| |
| class ContextList(object): |
| """a group of tests in a context. |
| """ |
| def __init__(self, tests, context=None): |
| self.tests = tests |
| self.context = context |
| |
| def __iter__(self): |
| return iter(self.tests) |