• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Running tests"""
2
3import sys
4import time
5import warnings
6
7from . import result
8from .signals import registerResult
9
10__unittest = True
11
12
13class _WritelnDecorator(object):
14    """Used to decorate file-like objects with a handy 'writeln' method"""
15    def __init__(self,stream):
16        self.stream = stream
17
18    def __getattr__(self, attr):
19        if attr in ('stream', '__getstate__'):
20            raise AttributeError(attr)
21        return getattr(self.stream,attr)
22
23    def writeln(self, arg=None):
24        if arg:
25            self.write(arg)
26        self.write('\n') # text-mode streams translate to \r\n if needed
27
28
29class TextTestResult(result.TestResult):
30    """A test result class that can print formatted text results to a stream.
31
32    Used by TextTestRunner.
33    """
34    separator1 = '=' * 70
35    separator2 = '-' * 70
36
37    def __init__(self, stream, descriptions, verbosity):
38        super(TextTestResult, self).__init__(stream, descriptions, verbosity)
39        self.stream = stream
40        self.showAll = verbosity > 1
41        self.dots = verbosity == 1
42        self.descriptions = descriptions
43
44    def getDescription(self, test):
45        doc_first_line = test.shortDescription()
46        if self.descriptions and doc_first_line:
47            return '\n'.join((str(test), doc_first_line))
48        else:
49            return str(test)
50
51    def startTest(self, test):
52        super(TextTestResult, self).startTest(test)
53        if self.showAll:
54            self.stream.write(self.getDescription(test))
55            self.stream.write(" ... ")
56            self.stream.flush()
57
58    def addSuccess(self, test):
59        super(TextTestResult, self).addSuccess(test)
60        if self.showAll:
61            self.stream.writeln("ok")
62            self.stream.flush()
63        elif self.dots:
64            self.stream.write('.')
65            self.stream.flush()
66
67    def addError(self, test, err):
68        super(TextTestResult, self).addError(test, err)
69        if self.showAll:
70            self.stream.writeln("ERROR")
71            self.stream.flush()
72        elif self.dots:
73            self.stream.write('E')
74            self.stream.flush()
75
76    def addFailure(self, test, err):
77        super(TextTestResult, self).addFailure(test, err)
78        if self.showAll:
79            self.stream.writeln("FAIL")
80            self.stream.flush()
81        elif self.dots:
82            self.stream.write('F')
83            self.stream.flush()
84
85    def addSkip(self, test, reason):
86        super(TextTestResult, self).addSkip(test, reason)
87        if self.showAll:
88            self.stream.writeln("skipped {0!r}".format(reason))
89            self.stream.flush()
90        elif self.dots:
91            self.stream.write("s")
92            self.stream.flush()
93
94    def addExpectedFailure(self, test, err):
95        super(TextTestResult, self).addExpectedFailure(test, err)
96        if self.showAll:
97            self.stream.writeln("expected failure")
98            self.stream.flush()
99        elif self.dots:
100            self.stream.write("x")
101            self.stream.flush()
102
103    def addUnexpectedSuccess(self, test):
104        super(TextTestResult, self).addUnexpectedSuccess(test)
105        if self.showAll:
106            self.stream.writeln("unexpected success")
107            self.stream.flush()
108        elif self.dots:
109            self.stream.write("u")
110            self.stream.flush()
111
112    def printErrors(self):
113        if self.dots or self.showAll:
114            self.stream.writeln()
115            self.stream.flush()
116        self.printErrorList('ERROR', self.errors)
117        self.printErrorList('FAIL', self.failures)
118
119    def printErrorList(self, flavour, errors):
120        for test, err in errors:
121            self.stream.writeln(self.separator1)
122            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
123            self.stream.writeln(self.separator2)
124            self.stream.writeln("%s" % err)
125            self.stream.flush()
126
127
128class TextTestRunner(object):
129    """A test runner class that displays results in textual form.
130
131    It prints out the names of tests as they are run, errors as they
132    occur, and a summary of the results at the end of the test run.
133    """
134    resultclass = TextTestResult
135
136    def __init__(self, stream=None, descriptions=True, verbosity=1,
137                 failfast=False, buffer=False, resultclass=None, warnings=None,
138                 *, tb_locals=False):
139        """Construct a TextTestRunner.
140
141        Subclasses should accept **kwargs to ensure compatibility as the
142        interface changes.
143        """
144        if stream is None:
145            stream = sys.stderr
146        self.stream = _WritelnDecorator(stream)
147        self.descriptions = descriptions
148        self.verbosity = verbosity
149        self.failfast = failfast
150        self.buffer = buffer
151        self.tb_locals = tb_locals
152        self.warnings = warnings
153        if resultclass is not None:
154            self.resultclass = resultclass
155
156    def _makeResult(self):
157        return self.resultclass(self.stream, self.descriptions, self.verbosity)
158
159    def run(self, test):
160        "Run the given test case or test suite."
161        result = self._makeResult()
162        registerResult(result)
163        result.failfast = self.failfast
164        result.buffer = self.buffer
165        result.tb_locals = self.tb_locals
166        with warnings.catch_warnings():
167            if self.warnings:
168                # if self.warnings is set, use it to filter all the warnings
169                warnings.simplefilter(self.warnings)
170                # if the filter is 'default' or 'always', special-case the
171                # warnings from the deprecated unittest methods to show them
172                # no more than once per module, because they can be fairly
173                # noisy.  The -Wd and -Wa flags can be used to bypass this
174                # only when self.warnings is None.
175                if self.warnings in ['default', 'always']:
176                    warnings.filterwarnings('module',
177                            category=DeprecationWarning,
178                            message=r'Please use assert\w+ instead.')
179            startTime = time.perf_counter()
180            startTestRun = getattr(result, 'startTestRun', None)
181            if startTestRun is not None:
182                startTestRun()
183            try:
184                test(result)
185            finally:
186                stopTestRun = getattr(result, 'stopTestRun', None)
187                if stopTestRun is not None:
188                    stopTestRun()
189            stopTime = time.perf_counter()
190        timeTaken = stopTime - startTime
191        result.printErrors()
192        if hasattr(result, 'separator2'):
193            self.stream.writeln(result.separator2)
194        run = result.testsRun
195        self.stream.writeln("Ran %d test%s in %.3fs" %
196                            (run, run != 1 and "s" or "", timeTaken))
197        self.stream.writeln()
198
199        expectedFails = unexpectedSuccesses = skipped = 0
200        try:
201            results = map(len, (result.expectedFailures,
202                                result.unexpectedSuccesses,
203                                result.skipped))
204        except AttributeError:
205            pass
206        else:
207            expectedFails, unexpectedSuccesses, skipped = results
208
209        infos = []
210        if not result.wasSuccessful():
211            self.stream.write("FAILED")
212            failed, errored = len(result.failures), len(result.errors)
213            if failed:
214                infos.append("failures=%d" % failed)
215            if errored:
216                infos.append("errors=%d" % errored)
217        else:
218            self.stream.write("OK")
219        if skipped:
220            infos.append("skipped=%d" % skipped)
221        if expectedFails:
222            infos.append("expected failures=%d" % expectedFails)
223        if unexpectedSuccesses:
224            infos.append("unexpected successes=%d" % unexpectedSuccesses)
225        if infos:
226            self.stream.writeln(" (%s)" % (", ".join(infos),))
227        else:
228            self.stream.write("\n")
229        self.stream.flush()
230        return result
231