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