root/galaxy-central/eggs/nose-0.11.1-py2.6.egg/nose/suite.py @ 3

リビジョン 3, 20.9 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1"""
2Test Suites
3-----------
4
5Provides a LazySuite, which is a suite whose test list is a generator
6function, and ContextSuite,which can run fixtures (setup/teardown
7functions or methods) for the context that contains its tests.
8
9"""
10from __future__ import generators
11
12import logging
13import sys
14import unittest
15from nose.case import Test
16from nose.config import Config
17from nose.proxy import ResultProxyFactory
18from nose.util import isclass, resolve_name, try_run
19
20if sys.platform == 'cli':
21    import clr
22    clr.AddReference("IronPython")
23    from IronPython.Runtime.Exceptions import StringException
24
25log = logging.getLogger(__name__)
26#log.setLevel(logging.DEBUG)
27
28# Singleton for default value -- see ContextSuite.__init__ below
29_def = object()
30
31
32class MixedContextError(Exception):
33    """Error raised when a context suite sees tests from more than
34    one context.
35    """
36    pass
37
38
39class LazySuite(unittest.TestSuite):
40    """A suite that may use a generator as its list of tests
41    """
42    def __init__(self, tests=()):
43        """Initialize the suite. tests may be an iterable or a generator
44        """
45        self._set_tests(tests)
46               
47    def __iter__(self):
48        return iter(self._tests)
49       
50    def __repr__(self):
51        return "<%s tests=generator (%s)>" % (
52            unittest._strclass(self.__class__), id(self))
53
54    def __hash__(self):
55        return object.__hash__(self)
56   
57    __str__ = __repr__
58
59    def addTest(self, test):
60        self._precache.append(test)
61
62    def __nonzero__(self):
63        log.debug("tests in %s?", id(self))
64        if self._precache:
65            return True
66        if self.test_generator is None:
67            return False
68        try:
69            test = self.test_generator.next()
70            if test is not None:
71                self._precache.append(test)
72                return True
73        except StopIteration:
74            pass
75        return False
76
77    def _get_tests(self):
78        log.debug("precache is %s", self._precache)
79        for test in self._precache:
80            yield test
81        if self.test_generator is None:
82            return
83        for test in self.test_generator:
84            yield test
85
86    def _set_tests(self, tests):
87        self._precache = []
88        is_suite = isinstance(tests, unittest.TestSuite)
89        if callable(tests) and not is_suite:
90            self.test_generator = tests()
91        elif is_suite:
92            # Suites need special treatment: they must be called like
93            # tests for their setup/teardown to run (if any)
94            self.addTests([tests])
95            self.test_generator = None
96        else:
97            self.addTests(tests)
98            self.test_generator = None
99
100    _tests = property(_get_tests, _set_tests, None,
101                      "Access the tests in this suite. Access is through a "
102                      "generator, so iteration may not be repeatable.")
103
104       
105class ContextSuite(LazySuite):
106    """A suite with context.
107
108    A ContextSuite executes fixtures (setup and teardown functions or
109    methods) for the context containing its tests.
110
111    The context may be explicitly passed. If it is not, a context (or
112    nested set of contexts) will be constructed by examining the tests
113    in the suite.
114    """   
115    failureException = unittest.TestCase.failureException
116    was_setup = False
117    was_torndown = False
118    classSetup = ('setup_class', 'setup_all', 'setupClass', 'setupAll',
119                     'setUpClass', 'setUpAll')
120    classTeardown = ('teardown_class', 'teardown_all', 'teardownClass',
121                     'teardownAll', 'tearDownClass', 'tearDownAll')
122    moduleSetup = ('setup_module', 'setupModule', 'setUpModule', 'setup',
123                   'setUp')
124    moduleTeardown = ('teardown_module', 'teardownModule', 'tearDownModule',
125                      'teardown', 'tearDown')
126    packageSetup = ('setup_package', 'setupPackage', 'setUpPackage')
127    packageTeardown = ('teardown_package', 'teardownPackage',
128                       'tearDownPackage')
129   
130    def __init__(self, tests=(), context=None, factory=None,
131                 config=None, resultProxy=None, can_split=True):
132        log.debug("Context suite for %s (%s) (%s)", tests, context, id(self))
133        self.context = context
134        self.factory = factory
135        if config is None:
136            config = Config()
137        self.config = config
138        self.resultProxy = resultProxy
139        self.has_run = False
140        self.can_split = can_split
141        LazySuite.__init__(self, tests)
142
143    def __repr__(self):
144        return "<%s context=%s>" % (
145            unittest._strclass(self.__class__),
146            getattr(self.context, '__name__', self.context))
147    __str__ = __repr__
148
149    def __hash__(self):
150        return object.__hash__(self)
151
152    # 2.3 compat -- force 2.4 call sequence
153    def __call__(self, *arg, **kw):
154        return self.run(*arg, **kw)
155
156    def exc_info(self):
157        """Hook for replacing error tuple output
158        """
159        return sys.exc_info()
160   
161    def _exc_info(self):
162        """Bottleneck to fix up IronPython string exceptions
163        """
164        e = self.exc_info()
165        if sys.platform == 'cli':
166            if isinstance(e[0], StringException):
167                # IronPython throws these StringExceptions, but
168                # traceback checks type(etype) == str. Make a real
169                # string here.
170                e = (str(e[0]), e[1], e[2])
171
172        return e
173
174    def run(self, result):
175        """Run tests in suite inside of suite fixtures.
176        """
177        # proxy the result for myself
178        if self.resultProxy:
179            result, orig = self.resultProxy(result, self), result
180        else:
181            result, orig = result, result
182        try:
183            self.setUp()
184        except KeyboardInterrupt:
185            raise
186        except:
187            result.addError(self, self._exc_info())
188            return
189        try:
190            for test in self._tests:
191                if result.shouldStop:
192                    log.debug("stopping")
193                    break
194                # each nose.case.Test will create its own result proxy
195                # so the cases need the original result, to avoid proxy
196                # chains
197                test(orig)
198        finally:
199            self.has_run = True
200            try:
201                self.tearDown()
202            except KeyboardInterrupt:
203                raise
204            except:
205                result.addError(self, self._exc_info())
206
207    def hasFixtures(self, ctx_callback=None):
208        context = self.context
209        if context is None:
210            return False
211        if self.implementsAnyFixture(context, ctx_callback=ctx_callback):
212            return True
213        # My context doesn't have any, but its ancestors might
214        factory = self.factory
215        if factory:
216            ancestors = factory.context.get(self, [])
217            for ancestor in ancestors:
218                if self.implementsAnyFixture(
219                    ancestor, ctx_callback=ctx_callback):
220                    return True
221        return False
222
223    def implementsAnyFixture(self, context, ctx_callback):
224        if isclass(context):
225            names = self.classSetup + self.classTeardown
226        else:
227            names = self.moduleSetup + self.moduleTeardown
228            if hasattr(context, '__path__'):
229                names += self.packageSetup + self.packageTeardown
230        # If my context has any fixture attribute, I have fixtures
231        fixt = False
232        for m in names:
233            if hasattr(context, m):
234                fixt = True
235                break
236        if ctx_callback is None:
237            return fixt
238        return ctx_callback(context, fixt)
239   
240    def setUp(self):
241        log.debug("suite %s setUp called, tests: %s", id(self), self._tests)
242        if not self:
243            # I have no tests
244            log.debug("suite %s has no tests", id(self))
245            return
246        if self.was_setup:
247            log.debug("suite %s already set up", id(self))
248            return
249        context = self.context
250        if context is None:
251            return
252        # before running my own context's setup, I need to
253        # ask the factory if my context's contexts' setups have been run
254        factory = self.factory
255        if factory:
256            # get a copy, since we'll be destroying it as we go
257            ancestors = factory.context.get(self, [])[:]
258            while ancestors:
259                ancestor = ancestors.pop()
260                log.debug("ancestor %s may need setup", ancestor)
261                if ancestor in factory.was_setup:
262                    continue
263                log.debug("ancestor %s does need setup", ancestor)
264                self.setupContext(ancestor)
265            if not context in factory.was_setup:
266                self.setupContext(context)
267        else:
268            self.setupContext(context)
269        self.was_setup = True
270        log.debug("completed suite setup")
271
272    def setupContext(self, context):
273        self.config.plugins.startContext(context)
274        log.debug("%s setup context %s", self, context)
275        if self.factory:
276            if context in self.factory.was_setup:
277                return
278            # note that I ran the setup for this context, so that I'll run
279            # the teardown in my teardown
280            self.factory.was_setup[context] = self
281        if isclass(context):
282            names = self.classSetup
283        else:
284            names = self.moduleSetup
285            if hasattr(context, '__path__'):
286                names = self.packageSetup + names
287        try_run(context, names)
288
289    def shortDescription(self):
290        if self.context is None:
291            return "test suite"
292        return "test suite for %s" % self.context
293
294    def tearDown(self):
295        log.debug('context teardown')
296        if not self.was_setup or self.was_torndown:
297            log.debug(
298                "No reason to teardown (was_setup? %s was_torndown? %s)"
299                % (self.was_setup, self.was_torndown))
300            return
301        self.was_torndown = True
302        context = self.context
303        if context is None:
304            log.debug("No context to tear down")
305            return
306
307        # for each ancestor... if the ancestor was setup
308        # and I did the setup, I can do teardown
309        factory = self.factory
310        if factory:
311            ancestors = factory.context.get(self, []) + [context]
312            for ancestor in ancestors:
313                log.debug('ancestor %s may need teardown', ancestor)
314                if not ancestor in factory.was_setup:
315                    log.debug('ancestor %s was not setup', ancestor)
316                    continue
317                if ancestor in factory.was_torndown:
318                    log.debug('ancestor %s already torn down', ancestor)
319                    continue
320                setup = factory.was_setup[ancestor]
321                log.debug("%s setup ancestor %s", setup, ancestor)
322                if setup is self:
323                    self.teardownContext(ancestor)
324        else:
325            self.teardownContext(context)
326       
327    def teardownContext(self, context):
328        log.debug("%s teardown context %s", self, context)
329        if self.factory:
330            if context in self.factory.was_torndown:
331                return
332            self.factory.was_torndown[context] = self
333        if isclass(context):
334            names = self.classTeardown
335        else:
336            names = self.moduleTeardown
337            if hasattr(context, '__path__'):
338                names = self.packageTeardown + names
339        try_run(context, names)
340        self.config.plugins.stopContext(context)
341
342    # FIXME the wrapping has to move to the factory?
343    def _get_wrapped_tests(self):
344        for test in self._get_tests():
345            if isinstance(test, Test) or isinstance(test, unittest.TestSuite):
346                yield test
347            else:
348                yield Test(test,
349                           config=self.config,
350                           resultProxy=self.resultProxy)
351
352    _tests = property(_get_wrapped_tests, LazySuite._set_tests, None,
353                      "Access the tests in this suite. Tests are returned "
354                      "inside of a context wrapper.")
355
356
357class ContextSuiteFactory(object):
358    """Factory for ContextSuites. Called with a collection of tests,
359    the factory decides on a hierarchy of contexts by introspecting
360    the collection or the tests themselves to find the objects
361    containing the test objects. It always returns one suite, but that
362    suite may consist of a hierarchy of nested suites.
363    """
364    suiteClass = ContextSuite
365    def __init__(self, config=None, suiteClass=None, resultProxy=_def):
366        if config is None:
367            config = Config()
368        self.config = config
369        if suiteClass is not None:
370            self.suiteClass = suiteClass
371        # Using a singleton to represent default instead of None allows
372        # passing resultProxy=None to turn proxying off.
373        if resultProxy is _def:
374            resultProxy = ResultProxyFactory(config=config)
375        self.resultProxy = resultProxy
376        self.suites = {}
377        self.context = {}
378        self.was_setup = {}
379        self.was_torndown = {}
380
381    def __call__(self, tests, **kw):
382        """Return ``ContextSuite`` for tests. ``tests`` may either
383        be a callable (in which case the resulting ContextSuite will
384        have no parent context and be evaluated lazily) or an
385        iterable. In that case the tests will wrapped in
386        nose.case.Test, be examined and the context of each found and a
387        suite of suites returned, organized into a stack with the
388        outermost suites belonging to the outermost contexts.
389        """
390        log.debug("Create suite for %s", tests)
391        context = kw.pop('context', getattr(tests, 'context', None))
392        log.debug("tests %s context %s", tests, context)
393        if context is None:
394            tests = self.wrapTests(tests)
395            try:
396                context = self.findContext(tests)
397            except MixedContextError:
398                return self.makeSuite(self.mixedSuites(tests), None, **kw)
399        return self.makeSuite(tests, context, **kw)
400       
401    def ancestry(self, context):
402        """Return the ancestry of the context (that is, all of the
403        packages and modules containing the context), in order of
404        descent with the outermost ancestor last.
405        This method is a generator.
406        """
407        log.debug("get ancestry %s", context)
408        if context is None:
409            return
410        # Methods include reference to module they are defined in, we
411        # don't want that, instead want the module the class is in now
412        # (classes are re-ancestored elsewhere).
413        if hasattr(context, 'im_class'):
414            context = context.im_class
415        if hasattr(context, '__module__'):
416            ancestors = context.__module__.split('.')
417        elif hasattr(context, '__name__'):
418            ancestors = context.__name__.split('.')[:-1]
419        else:
420            raise TypeError("%s has no ancestors?" % context)
421        while ancestors:
422            log.debug(" %s ancestors %s", context, ancestors)
423            yield resolve_name('.'.join(ancestors))               
424            ancestors.pop()
425
426    def findContext(self, tests):
427        if callable(tests) or isinstance(tests, unittest.TestSuite):
428            return None
429        context = None
430        for test in tests:
431            # Don't look at suites for contexts, only tests
432            ctx = getattr(test, 'context', None)
433            if ctx is None:
434                continue
435            if context is None:
436                context = ctx
437            elif context != ctx:
438                raise MixedContextError(
439                    "Tests with different contexts in same suite! %s != %s"
440                    % (context, ctx))
441        return context
442
443    def makeSuite(self, tests, context, **kw):
444        suite = self.suiteClass(
445            tests, context=context, config=self.config, factory=self,
446            resultProxy=self.resultProxy, **kw)
447        if context is not None:
448            self.suites.setdefault(context, []).append(suite)
449            self.context.setdefault(suite, []).append(context)
450            log.debug("suite %s has context %s", suite,
451                      getattr(context, '__name__', None))
452            for ancestor in self.ancestry(context):
453                self.suites.setdefault(ancestor, []).append(suite)
454                self.context[suite].append(ancestor)
455                log.debug("suite %s has ancestor %s", suite, ancestor.__name__)
456        return suite
457
458    def mixedSuites(self, tests):
459        """The complex case where there are tests that don't all share
460        the same context. Groups tests into suites with common ancestors,
461        according to the following (essentially tail-recursive) procedure:
462
463        Starting with the context of the first test, if it is not
464        None, look for tests in the remaining tests that share that
465        ancestor. If any are found, group into a suite with that
466        ancestor as the context, and replace the current suite with
467        that suite. Continue this process for each ancestor of the
468        first test, until all ancestors have been processed. At this
469        point if any tests remain, recurse with those tests as the
470        input, returning a list of the common suite (which may be the
471        suite or test we started with, if no common tests were found)
472        plus the results of recursion.
473        """
474        if not tests:
475            return []
476        head = tests.pop(0)
477        if not tests:
478            return [head] # short circuit when none are left to combine
479        suite = head # the common ancestry suite, so far
480        tail = tests[:]
481        context = getattr(head, 'context', None)
482        if context is not None:
483            ancestors = [context] + [a for a in self.ancestry(context)]
484            for ancestor in ancestors:
485                common = [suite] # tests with ancestor in common, so far
486                remain = [] # tests that remain to be processed
487                for test in tail:
488                    found_common = False
489                    test_ctx = getattr(test, 'context', None)
490                    if test_ctx is None:
491                        remain.append(test)
492                        continue
493                    if test_ctx is ancestor:
494                        common.append(test)
495                        continue         
496                    for test_ancestor in self.ancestry(test_ctx):
497                        if test_ancestor is ancestor:
498                            common.append(test)
499                            found_common = True
500                            break
501                    if not found_common:
502                        remain.append(test)
503                if common:
504                    suite = self.makeSuite(common, ancestor)
505                tail = remain
506        return [suite] + self.mixedSuites(tail)
507           
508    def wrapTests(self, tests):
509        log.debug("wrap %s", tests)
510        if callable(tests) or isinstance(tests, unittest.TestSuite):
511            log.debug("I won't wrap")
512            return tests
513        wrapped = []
514        for test in tests:
515            log.debug("wrapping %s", test)
516            if isinstance(test, Test) or isinstance(test, unittest.TestSuite):
517                wrapped.append(test)
518            elif isinstance(test, ContextList):
519                wrapped.append(self.makeSuite(test, context=test.context))
520            else:
521                wrapped.append(
522                    Test(test, config=self.config, resultProxy=self.resultProxy)
523                    )
524        return wrapped
525
526
527class ContextList(object):
528    """Not quite a suite -- a group of tests in a context. This is used
529    to hint the ContextSuiteFactory about what context the tests
530    belong to, in cases where it may be ambiguous or missing.
531    """
532    def __init__(self, tests, context=None):
533        self.tests = tests
534        self.context = context
535
536    def __iter__(self):
537        return iter(self.tests)
538
539
540class FinalizingSuiteWrapper(unittest.TestSuite):
541    """Wraps suite and calls final function after suite has
542    executed. Used to call final functions in cases (like running in
543    the standard test runner) where test running is not under nose's
544    control.   
545    """
546    def __init__(self, suite, finalize):
547        self.suite = suite
548        self.finalize = finalize
549
550    def __call__(self, *arg, **kw):
551        return self.run(*arg, **kw)
552
553    def run(self, *arg, **kw):
554        try:
555            return self.suite(*arg, **kw)
556        finally:
557            self.finalize(*arg, **kw)
558
559
560# backwards compat -- sort of
561class TestDir:
562    def __init__(*arg, **kw):
563        raise NotImplementedError(
564            "TestDir is not usable with nose 0.10. The class is present "
565            "in nose.suite for backwards compatibility purposes but it "
566            "may not be used.")
567
568
569class TestModule:
570    def __init__(*arg, **kw):
571        raise NotImplementedError(
572            "TestModule is not usable with nose 0.10. The class is present "
573            "in nose.suite for backwards compatibility purposes but it "
574            "may not be used.")
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。