~ubuntu-branches/ubuntu/trusty/python3.4/trusty-proposed

« back to all changes in this revision

Viewing changes to Lib/unittest/suite.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2013-11-25 09:44:27 UTC
  • Revision ID: package-import@ubuntu.com-20131125094427-lzxj8ap5w01lmo7f
Tags: upstream-3.4~b1
ImportĀ upstreamĀ versionĀ 3.4~b1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""TestSuite"""
 
2
 
 
3
import sys
 
4
 
 
5
from . import case
 
6
from . import util
 
7
 
 
8
__unittest = True
 
9
 
 
10
 
 
11
def _call_if_exists(parent, attr):
 
12
    func = getattr(parent, attr, lambda: None)
 
13
    func()
 
14
 
 
15
 
 
16
class BaseTestSuite(object):
 
17
    """A simple test suite that doesn't provide class or module shared fixtures.
 
18
    """
 
19
    _cleanup = True
 
20
 
 
21
    def __init__(self, tests=()):
 
22
        self._tests = []
 
23
        self.addTests(tests)
 
24
 
 
25
    def __repr__(self):
 
26
        return "<%s tests=%s>" % (util.strclass(self.__class__), list(self))
 
27
 
 
28
    def __eq__(self, other):
 
29
        if not isinstance(other, self.__class__):
 
30
            return NotImplemented
 
31
        return list(self) == list(other)
 
32
 
 
33
    def __ne__(self, other):
 
34
        return not self == other
 
35
 
 
36
    def __iter__(self):
 
37
        return iter(self._tests)
 
38
 
 
39
    def countTestCases(self):
 
40
        cases = 0
 
41
        for test in self:
 
42
            cases += test.countTestCases()
 
43
        return cases
 
44
 
 
45
    def addTest(self, test):
 
46
        # sanity checks
 
47
        if not callable(test):
 
48
            raise TypeError("{} is not callable".format(repr(test)))
 
49
        if isinstance(test, type) and issubclass(test,
 
50
                                                 (case.TestCase, TestSuite)):
 
51
            raise TypeError("TestCases and TestSuites must be instantiated "
 
52
                            "before passing them to addTest()")
 
53
        self._tests.append(test)
 
54
 
 
55
    def addTests(self, tests):
 
56
        if isinstance(tests, str):
 
57
            raise TypeError("tests must be an iterable of tests, not a string")
 
58
        for test in tests:
 
59
            self.addTest(test)
 
60
 
 
61
    def run(self, result):
 
62
        for index, test in enumerate(self):
 
63
            if result.shouldStop:
 
64
                break
 
65
            test(result)
 
66
            if self._cleanup:
 
67
                self._removeTestAtIndex(index)
 
68
        return result
 
69
 
 
70
    def _removeTestAtIndex(self, index):
 
71
        """Stop holding a reference to the TestCase at index."""
 
72
        try:
 
73
            self._tests[index] = None
 
74
        except TypeError:
 
75
            # support for suite implementations that have overriden self._test
 
76
            pass
 
77
 
 
78
    def __call__(self, *args, **kwds):
 
79
        return self.run(*args, **kwds)
 
80
 
 
81
    def debug(self):
 
82
        """Run the tests without collecting errors in a TestResult"""
 
83
        for test in self:
 
84
            test.debug()
 
85
 
 
86
 
 
87
class TestSuite(BaseTestSuite):
 
88
    """A test suite is a composite test consisting of a number of TestCases.
 
89
 
 
90
    For use, create an instance of TestSuite, then add test case instances.
 
91
    When all tests have been added, the suite can be passed to a test
 
92
    runner, such as TextTestRunner. It will run the individual test cases
 
93
    in the order in which they were added, aggregating the results. When
 
94
    subclassing, do not forget to call the base class constructor.
 
95
    """
 
96
 
 
97
    def run(self, result, debug=False):
 
98
        topLevel = False
 
99
        if getattr(result, '_testRunEntered', False) is False:
 
100
            result._testRunEntered = topLevel = True
 
101
 
 
102
        for index, test in enumerate(self):
 
103
            if result.shouldStop:
 
104
                break
 
105
 
 
106
            if _isnotsuite(test):
 
107
                self._tearDownPreviousClass(test, result)
 
108
                self._handleModuleFixture(test, result)
 
109
                self._handleClassSetUp(test, result)
 
110
                result._previousTestClass = test.__class__
 
111
 
 
112
                if (getattr(test.__class__, '_classSetupFailed', False) or
 
113
                    getattr(result, '_moduleSetUpFailed', False)):
 
114
                    continue
 
115
 
 
116
            if not debug:
 
117
                test(result)
 
118
            else:
 
119
                test.debug()
 
120
 
 
121
            if self._cleanup:
 
122
                self._removeTestAtIndex(index)
 
123
 
 
124
        if topLevel:
 
125
            self._tearDownPreviousClass(None, result)
 
126
            self._handleModuleTearDown(result)
 
127
            result._testRunEntered = False
 
128
        return result
 
129
 
 
130
    def debug(self):
 
131
        """Run the tests without collecting errors in a TestResult"""
 
132
        debug = _DebugResult()
 
133
        self.run(debug, True)
 
134
 
 
135
    ################################
 
136
 
 
137
    def _handleClassSetUp(self, test, result):
 
138
        previousClass = getattr(result, '_previousTestClass', None)
 
139
        currentClass = test.__class__
 
140
        if currentClass == previousClass:
 
141
            return
 
142
        if result._moduleSetUpFailed:
 
143
            return
 
144
        if getattr(currentClass, "__unittest_skip__", False):
 
145
            return
 
146
 
 
147
        try:
 
148
            currentClass._classSetupFailed = False
 
149
        except TypeError:
 
150
            # test may actually be a function
 
151
            # so its class will be a builtin-type
 
152
            pass
 
153
 
 
154
        setUpClass = getattr(currentClass, 'setUpClass', None)
 
155
        if setUpClass is not None:
 
156
            _call_if_exists(result, '_setupStdout')
 
157
            try:
 
158
                setUpClass()
 
159
            except Exception as e:
 
160
                if isinstance(result, _DebugResult):
 
161
                    raise
 
162
                currentClass._classSetupFailed = True
 
163
                className = util.strclass(currentClass)
 
164
                errorName = 'setUpClass (%s)' % className
 
165
                self._addClassOrModuleLevelException(result, e, errorName)
 
166
            finally:
 
167
                _call_if_exists(result, '_restoreStdout')
 
168
 
 
169
    def _get_previous_module(self, result):
 
170
        previousModule = None
 
171
        previousClass = getattr(result, '_previousTestClass', None)
 
172
        if previousClass is not None:
 
173
            previousModule = previousClass.__module__
 
174
        return previousModule
 
175
 
 
176
 
 
177
    def _handleModuleFixture(self, test, result):
 
178
        previousModule = self._get_previous_module(result)
 
179
        currentModule = test.__class__.__module__
 
180
        if currentModule == previousModule:
 
181
            return
 
182
 
 
183
        self._handleModuleTearDown(result)
 
184
 
 
185
 
 
186
        result._moduleSetUpFailed = False
 
187
        try:
 
188
            module = sys.modules[currentModule]
 
189
        except KeyError:
 
190
            return
 
191
        setUpModule = getattr(module, 'setUpModule', None)
 
192
        if setUpModule is not None:
 
193
            _call_if_exists(result, '_setupStdout')
 
194
            try:
 
195
                setUpModule()
 
196
            except Exception as e:
 
197
                if isinstance(result, _DebugResult):
 
198
                    raise
 
199
                result._moduleSetUpFailed = True
 
200
                errorName = 'setUpModule (%s)' % currentModule
 
201
                self._addClassOrModuleLevelException(result, e, errorName)
 
202
            finally:
 
203
                _call_if_exists(result, '_restoreStdout')
 
204
 
 
205
    def _addClassOrModuleLevelException(self, result, exception, errorName):
 
206
        error = _ErrorHolder(errorName)
 
207
        addSkip = getattr(result, 'addSkip', None)
 
208
        if addSkip is not None and isinstance(exception, case.SkipTest):
 
209
            addSkip(error, str(exception))
 
210
        else:
 
211
            result.addError(error, sys.exc_info())
 
212
 
 
213
    def _handleModuleTearDown(self, result):
 
214
        previousModule = self._get_previous_module(result)
 
215
        if previousModule is None:
 
216
            return
 
217
        if result._moduleSetUpFailed:
 
218
            return
 
219
 
 
220
        try:
 
221
            module = sys.modules[previousModule]
 
222
        except KeyError:
 
223
            return
 
224
 
 
225
        tearDownModule = getattr(module, 'tearDownModule', None)
 
226
        if tearDownModule is not None:
 
227
            _call_if_exists(result, '_setupStdout')
 
228
            try:
 
229
                tearDownModule()
 
230
            except Exception as e:
 
231
                if isinstance(result, _DebugResult):
 
232
                    raise
 
233
                errorName = 'tearDownModule (%s)' % previousModule
 
234
                self._addClassOrModuleLevelException(result, e, errorName)
 
235
            finally:
 
236
                _call_if_exists(result, '_restoreStdout')
 
237
 
 
238
    def _tearDownPreviousClass(self, test, result):
 
239
        previousClass = getattr(result, '_previousTestClass', None)
 
240
        currentClass = test.__class__
 
241
        if currentClass == previousClass:
 
242
            return
 
243
        if getattr(previousClass, '_classSetupFailed', False):
 
244
            return
 
245
        if getattr(result, '_moduleSetUpFailed', False):
 
246
            return
 
247
        if getattr(previousClass, "__unittest_skip__", False):
 
248
            return
 
249
 
 
250
        tearDownClass = getattr(previousClass, 'tearDownClass', None)
 
251
        if tearDownClass is not None:
 
252
            _call_if_exists(result, '_setupStdout')
 
253
            try:
 
254
                tearDownClass()
 
255
            except Exception as e:
 
256
                if isinstance(result, _DebugResult):
 
257
                    raise
 
258
                className = util.strclass(previousClass)
 
259
                errorName = 'tearDownClass (%s)' % className
 
260
                self._addClassOrModuleLevelException(result, e, errorName)
 
261
            finally:
 
262
                _call_if_exists(result, '_restoreStdout')
 
263
 
 
264
 
 
265
class _ErrorHolder(object):
 
266
    """
 
267
    Placeholder for a TestCase inside a result. As far as a TestResult
 
268
    is concerned, this looks exactly like a unit test. Used to insert
 
269
    arbitrary errors into a test suite run.
 
270
    """
 
271
    # Inspired by the ErrorHolder from Twisted:
 
272
    # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
 
273
 
 
274
    # attribute used by TestResult._exc_info_to_string
 
275
    failureException = None
 
276
 
 
277
    def __init__(self, description):
 
278
        self.description = description
 
279
 
 
280
    def id(self):
 
281
        return self.description
 
282
 
 
283
    def shortDescription(self):
 
284
        return None
 
285
 
 
286
    def __repr__(self):
 
287
        return "<ErrorHolder description=%r>" % (self.description,)
 
288
 
 
289
    def __str__(self):
 
290
        return self.id()
 
291
 
 
292
    def run(self, result):
 
293
        # could call result.addError(...) - but this test-like object
 
294
        # shouldn't be run anyway
 
295
        pass
 
296
 
 
297
    def __call__(self, result):
 
298
        return self.run(result)
 
299
 
 
300
    def countTestCases(self):
 
301
        return 0
 
302
 
 
303
def _isnotsuite(test):
 
304
    "A crude way to tell apart testcases and suites with duck-typing"
 
305
    try:
 
306
        iter(test)
 
307
    except TypeError:
 
308
        return True
 
309
    return False
 
310
 
 
311
 
 
312
class _DebugResult(object):
 
313
    "Used by the TestSuite to hold previous class when running in debug."
 
314
    _previousTestClass = None
 
315
    _moduleSetUpFailed = False
 
316
    shouldStop = False