~ubuntu-branches/ubuntu/wily/nose2/wily

« back to all changes in this revision

Viewing changes to nose2/plugins/loader/generators.py

  • Committer: Package Import Robot
  • Author(s): Barry Warsaw
  • Date: 2013-09-09 22:14:45 UTC
  • Revision ID: package-import@ubuntu.com-20130909221445-zdvvvebxfucvavw5
Tags: upstream-0.4.7
ImportĀ upstreamĀ versionĀ 0.4.7

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
Load tests from generators.
 
3
 
 
4
This plugin implements :func:`loadTestFromTestCase`,
 
5
:func:`loadTestsFromName` and :func:`loadTestFromModule` to enable
 
6
loading tests from generators.
 
7
 
 
8
Generators may be functions or methods in test cases. In either case,
 
9
they must yield a callable and arguments for that callable once for
 
10
each test they generate. The callable and arguments may all be in one
 
11
tuple, or the arguments may be grouped into a separate tuple::
 
12
 
 
13
  def test_gen():
 
14
      yield check, 1, 2
 
15
      yield check, (1, 2)
 
16
 
 
17
To address a particular generated test via a command-line test name,
 
18
append a colon (':') followed by the index, *starting from 1*, of the
 
19
generated case you want to execute.
 
20
 
 
21
"""
 
22
# This module contains some code copied from unittest2 and other code
 
23
# developed in reference to unittest2.
 
24
# unittest2 is Copyright (c) 2001-2010 Python Software Foundation; All
 
25
# Rights Reserved. See: http://docs.python.org/license.html
 
26
 
 
27
import functools
 
28
import logging
 
29
import sys
 
30
import types
 
31
import unittest
 
32
 
 
33
from nose2 import exceptions, util
 
34
from nose2.events import Plugin
 
35
from nose2.compat import unittest as ut2
 
36
 
 
37
 
 
38
log = logging.getLogger(__name__)
 
39
__unittest = True
 
40
 
 
41
 
 
42
class Generators(Plugin):
 
43
 
 
44
    """Loader plugin that loads generator tests"""
 
45
    alwaysOn = True
 
46
    configSection = 'generators'
 
47
 
 
48
    def registerInSubprocess(self, event):
 
49
        event.pluginClasses.append(self.__class__)
 
50
 
 
51
    def unpack(self, generator):
 
52
        for index, func_args in enumerate(generator):
 
53
            try:
 
54
                func, args = func_args
 
55
                if not isinstance(args, tuple):
 
56
                    args = (args,)
 
57
                yield index, (func, args)
 
58
            except ValueError:
 
59
                func, args = func_args[0], func_args[1:]
 
60
                yield index, (func, args)
 
61
 
 
62
    def loadTestsFromTestCase(self, event):
 
63
        """Load generator tests from test case"""
 
64
        log.debug('loadTestsFromTestCase %s', event.testCase)
 
65
        testCaseClass = event.testCase
 
66
        for name in dir(testCaseClass):
 
67
            method = getattr(testCaseClass, name)
 
68
            if (name.startswith(self.session.testMethodPrefix) and
 
69
                hasattr(getattr(testCaseClass, name), '__call__') and
 
70
                util.isgenerator(method)):
 
71
                instance = testCaseClass(name)
 
72
                event.extraTests.extend(
 
73
                    self._testsFromGenerator(
 
74
                        event, name, method(instance), testCaseClass)
 
75
                )
 
76
 
 
77
    def loadTestsFromTestClass(self, event):
 
78
        testCaseClass = event.testCase
 
79
        for name in dir(testCaseClass):
 
80
            method = getattr(testCaseClass, name)
 
81
            if (name.startswith(self.session.testMethodPrefix) and
 
82
                hasattr(getattr(testCaseClass, name), '__call__') and
 
83
                util.isgenerator(method)):
 
84
                instance = testCaseClass()
 
85
                event.extraTests.extend(
 
86
                    self._testsFromGeneratorMethod(
 
87
                        event, name, method, instance)
 
88
                )
 
89
 
 
90
    def getTestCaseNames(self, event):
 
91
        """Get generator test case names from test case class"""
 
92
        log.debug('getTestCaseNames %s', event.testCase)
 
93
        names = filter(event.isTestMethod, dir(event.testCase))
 
94
        klass = event.testCase
 
95
        for name in names:
 
96
            method = getattr(klass, name)
 
97
            if util.isgenerator(method):
 
98
                event.excludedNames.append(name)
 
99
 
 
100
    def getTestMethodNames(self, event):
 
101
        return self.getTestCaseNames(event)
 
102
 
 
103
    def loadTestsFromName(self, event):
 
104
        """Load tests from generator named on command line"""
 
105
        original_name = name = event.name
 
106
        module = event.module
 
107
        try:
 
108
            result = util.test_from_name(name, module)
 
109
        except (AttributeError, ImportError) as e:
 
110
            event.handled = True
 
111
            return event.loader.failedLoadTests(name, e)
 
112
        if result is None:
 
113
            # we can't find it - let the default case handle it
 
114
            return
 
115
 
 
116
        parent, obj, name, index = result
 
117
        if not util.isgenerator(obj):
 
118
            return
 
119
 
 
120
        if (index is None and not
 
121
            isinstance(parent, type) and not
 
122
            isinstance(obj, types.FunctionType)):
 
123
            log.debug("Don't know how to load generator tests from %s", obj)
 
124
            return
 
125
 
 
126
        if (parent and
 
127
            isinstance(parent, type) and
 
128
            issubclass(parent, unittest.TestCase)):
 
129
            # generator method in test case
 
130
            instance = parent(obj.__name__)
 
131
            tests = list(
 
132
                self._testsFromGenerator(
 
133
                    event, obj.__name__, obj(instance), parent)
 
134
            )
 
135
        elif (parent and
 
136
              isinstance(parent, type)):
 
137
              # generator method in test class
 
138
            method = obj
 
139
            instance = parent()
 
140
            tests = list(
 
141
                self._testsFromGeneratorMethod(event, name, method, instance)
 
142
            )
 
143
        else:
 
144
            # generator func
 
145
            tests = list(self._testsFromGeneratorFunc(event, obj))
 
146
 
 
147
        if index is not None:
 
148
            try:
 
149
                tests = [tests[index - 1]]
 
150
            except IndexError:
 
151
                raise exceptions.TestNotFoundError(original_name)
 
152
 
 
153
        suite = event.loader.suiteClass()
 
154
        suite.addTests(tests)
 
155
        event.handled = True
 
156
        return suite
 
157
 
 
158
    def loadTestsFromModule(self, event):
 
159
        """Load tests from generator functions in a module"""
 
160
        module = event.module
 
161
 
 
162
        def is_test(obj):
 
163
            return (obj.__name__.startswith(self.session.testMethodPrefix)
 
164
                    and util.isgenerator(obj))
 
165
        tests = []
 
166
        for name in dir(module):
 
167
            obj = getattr(module, name)
 
168
            if isinstance(obj, types.FunctionType) and is_test(obj):
 
169
                tests.extend(self._testsFromGeneratorFunc(event, obj))
 
170
        event.extraTests.extend(tests)
 
171
 
 
172
    def _testsFromGenerator(self, event, name, generator, testCaseClass):
 
173
        try:
 
174
            for index, (func, args) in self.unpack(generator):
 
175
                method_name = util.name_from_args(name, index, args)
 
176
                setattr(testCaseClass, method_name, None)
 
177
                instance = testCaseClass(method_name)
 
178
                delattr(testCaseClass, method_name)
 
179
 
 
180
                def method(func=func, args=args):
 
181
                    return func(*args)
 
182
                method = functools.update_wrapper(method, func)
 
183
                setattr(instance, method_name, method)
 
184
                yield instance
 
185
        except:
 
186
            exc_info = sys.exc_info()
 
187
            test_name = '%s.%s.%s' % (testCaseClass.__module__,
 
188
                                      testCaseClass.__name__,
 
189
                                      name)
 
190
            yield event.loader.failedLoadTests(test_name, exc_info)
 
191
 
 
192
    def _testsFromGeneratorFunc(self, event, obj):
 
193
        extras = list(obj())
 
194
        name = '%s.%s' % (obj.__module__, obj.__name__)
 
195
        args = {}
 
196
        setUp = getattr(obj, 'setUp', None)
 
197
        tearDown = getattr(obj, 'tearDown', None)
 
198
        if setUp is not None:
 
199
            args['setUp'] = setUp
 
200
        if tearDown is not None:
 
201
            args['tearDown'] = tearDown
 
202
 
 
203
        def createTest(name):
 
204
            return util.transplant_class(
 
205
                GeneratorFunctionCase, obj.__module__)(name, **args)
 
206
        for test in self._testsFromGenerator(event, name, extras, createTest):
 
207
            yield test
 
208
 
 
209
    def _testsFromGeneratorMethod(self, event, name, method, instance):
 
210
        extras = list(method(instance))
 
211
        name = "%s.%s.%s" % (instance.__class__.__module__,
 
212
                             instance.__class__.__name__,
 
213
                             method.__name__)
 
214
        args = {}
 
215
        setUp = getattr(instance, 'setUp', None)
 
216
        tearDown = getattr(instance, 'tearDown', None)
 
217
        if setUp is not None:
 
218
            args['setUp'] = setUp
 
219
        if tearDown is not None:
 
220
            args['tearDown'] = tearDown
 
221
 
 
222
        def createTest(name):
 
223
            return util.transplant_class(
 
224
                GeneratorMethodCase(instance.__class__),
 
225
                instance.__class__.__module__)(name, **args)
 
226
        for test in self._testsFromGenerator(event, name, extras, createTest):
 
227
            yield test
 
228
 
 
229
 
 
230
class GeneratorFunctionCase(ut2.FunctionTestCase):
 
231
 
 
232
    def __init__(self, name, **args):
 
233
        self._funcName = name
 
234
        ut2.FunctionTestCase.__init__(self, None, **args)
 
235
 
 
236
    _testFunc = property(lambda self: getattr(self, self._funcName),
 
237
                         lambda self, func: None)
 
238
 
 
239
    def __repr__(self):
 
240
        return self._funcName
 
241
 
 
242
    id = __str__ = __repr__
 
243
 
 
244
 
 
245
def GeneratorMethodCase(cls):
 
246
    class _GeneratorMethodCase(GeneratorFunctionCase):
 
247
 
 
248
        @classmethod
 
249
        def setUpClass(klass):
 
250
            if hasattr(cls, 'setUpClass'):
 
251
                cls.setUpClass()
 
252
 
 
253
        @classmethod
 
254
        def tearDownClass(klass):
 
255
            if hasattr(cls, 'tearDownClass'):
 
256
                cls.tearDownClass()
 
257
    return _GeneratorMethodCase