1"""Running tests"""
2
3import sys
4import time
5
6from . import result
7from .signals import registerResult
8
9__unittest = True
10
11
12class _WritelnDecorator(object):
13    """Used to decorate file-like objects with a handy 'writeln' method"""
14    def __init__(self,stream):
15        self.stream = stream
16
17    def __getattr__(self, attr):
18        if attr in ('stream', '__getstate__'):
19            raise AttributeError(attr)
20        return getattr(self.stream,attr)
21
22    def writeln(self, arg=None):
23        if arg:
24            self.write(arg)
25        self.write('\n') # text-mode streams translate to \r\n if needed
26
27
28class TextTestResult(result.TestResult):
29    """A test result class that can print formatted text results to a stream.
30
31    Used by TextTestRunner.
32    """
33    separator1 = '=' * 70
34    separator2 = '-' * 70
35
36    def __init__(self, stream, descriptions, verbosity):
37        super(TextTestResult, self).__init__(stream, descriptions, verbosity)
38        self.stream = stream
39        self.showAll = verbosity > 1
40        self.dots = verbosity == 1
41        self.descriptions = descriptions
42
43    def getDescription(self, test):
44        doc_first_line = test.shortDescription()
45        if self.descriptions and doc_first_line:
46            return '\n'.join((str(test), doc_first_line))
47        else:
48            return str(test)
49
50    def startTest(self, test):
51        super(TextTestResult, self).startTest(test)
52        if self.showAll:
53            self.stream.write(self.getDescription(test))
54            self.stream.write(" ... ")
55            self.stream.flush()
56
57    def addSuccess(self, test):
58        super(TextTestResult, self).addSuccess(test)
59        if self.showAll:
60            self.stream.writeln("ok")
61        elif self.dots:
62            self.stream.write('.')
63            self.stream.flush()
64
65    def addError(self, test, err):
66        super(TextTestResult, self).addError(test, err)
67        if self.showAll:
68            self.stream.writeln("ERROR")
69        elif self.dots:
70            self.stream.write('E')
71            self.stream.flush()
72
73    def addFailure(self, test, err):
74        super(TextTestResult, self).addFailure(test, err)
75        if self.showAll:
76            self.stream.writeln("FAIL")
77        elif self.dots:
78            self.stream.write('F')
79            self.stream.flush()
80
81    def addSkip(self, test, reason):
82        super(TextTestResult, self).addSkip(test, reason)
83        if self.showAll:
84            self.stream.writeln("skipped {0!r}".format(reason))
85        elif self.dots:
86            self.stream.write("s")
87            self.stream.flush()
88
89    def addExpectedFailure(self, test, err):
90        super(TextTestResult, self).addExpectedFailure(test, err)
91        if self.showAll:
92            self.stream.writeln("expected failure")
93        elif self.dots:
94            self.stream.write("x")
95            self.stream.flush()
96
97    def addUnexpectedSuccess(self, test):
98        super(TextTestResult, self).addUnexpectedSuccess(test)
99        if self.showAll:
100            self.stream.writeln("unexpected success")
101        elif self.dots:
102            self.stream.write("u")
103            self.stream.flush()
104
105    def printErrors(self):
106        if self.dots or self.showAll:
107            self.stream.writeln()
108        self.printErrorList('ERROR', self.errors)
109        self.printErrorList('FAIL', self.failures)
110
111    def printErrorList(self, flavour, errors):
112        for test, err in errors:
113            self.stream.writeln(self.separator1)
114            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
115            self.stream.writeln(self.separator2)
116            self.stream.writeln("%s" % err)
117
118
119class TextTestRunner(object):
120    """A test runner class that displays results in textual form.
121
122    It prints out the names of tests as they are run, errors as they
123    occur, and a summary of the results at the end of the test run.
124    """
125    resultclass = TextTestResult
126
127    def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1,
128                 failfast=False, buffer=False, resultclass=None):
129        self.stream = _WritelnDecorator(stream)
130        self.descriptions = descriptions
131        self.verbosity = verbosity
132        self.failfast = failfast
133        self.buffer = buffer
134        if resultclass is not None:
135            self.resultclass = resultclass
136
137    def _makeResult(self):
138        return self.resultclass(self.stream, self.descriptions, self.verbosity)
139
140    def run(self, test):
141        "Run the given test case or test suite."
142        result = self._makeResult()
143        registerResult(result)
144        result.failfast = self.failfast
145        result.buffer = self.buffer
146        startTime = time.time()
147        startTestRun = getattr(result, 'startTestRun', None)
148        if startTestRun is not None:
149            startTestRun()
150        try:
151            test(result)
152        finally:
153            stopTestRun = getattr(result, 'stopTestRun', None)
154            if stopTestRun is not None:
155                stopTestRun()
156        stopTime = time.time()
157        timeTaken = stopTime - startTime
158        result.printErrors()
159        if hasattr(result, 'separator2'):
160            self.stream.writeln(result.separator2)
161        run = result.testsRun
162        self.stream.writeln("Ran %d test%s in %.3fs" %
163                            (run, run != 1 and "s" or "", timeTaken))
164        self.stream.writeln()
165
166        expectedFails = unexpectedSuccesses = skipped = 0
167        try:
168            results = map(len, (result.expectedFailures,
169                                result.unexpectedSuccesses,
170                                result.skipped))
171        except AttributeError:
172            pass
173        else:
174            expectedFails, unexpectedSuccesses, skipped = results
175
176        infos = []
177        if not result.wasSuccessful():
178            self.stream.write("FAILED")
179            failed, errored = map(len, (result.failures, result.errors))
180            if failed:
181                infos.append("failures=%d" % failed)
182            if errored:
183                infos.append("errors=%d" % errored)
184        else:
185            self.stream.write("OK")
186        if skipped:
187            infos.append("skipped=%d" % skipped)
188        if expectedFails:
189            infos.append("expected failures=%d" % expectedFails)
190        if unexpectedSuccesses:
191            infos.append("unexpected successes=%d" % unexpectedSuccesses)
192        if infos:
193            self.stream.writeln(" (%s)" % (", ".join(infos),))
194        else:
195            self.stream.write("\n")
196        return result
197