blob: ec80e8eea5871c4a6c9aa856f902ecfbba2a20db [file] [log] [blame]
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00001"""Test result object"""
2
3import traceback
4
5from . import util
Michael Foord1b9e9532010-03-22 01:01:34 +00006from functools import wraps
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00007
Michael Foordb1aa30f2010-03-22 00:06:30 +00008__unittest = True
9
Michael Foord1b9e9532010-03-22 01:01:34 +000010def failfast(method):
11 @wraps(method)
12 def inner(self, *args, **kw):
13 if getattr(self, 'failfast', False):
14 self.stop()
15 return method(self, *args, **kw)
16 return inner
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000017
18class TestResult(object):
19 """Holder for test result information.
20
21 Test results are automatically managed by the TestCase and TestSuite
22 classes, and do not need to be explicitly manipulated by writers of tests.
23
24 Each instance holds the total number of tests run, and collections of
25 failures and errors that occurred among those test runs. The collections
26 contain tuples of (testcase, exceptioninfo), where exceptioninfo is the
27 formatted traceback of the error that occurred.
28 """
Michael Foord5ffa3252010-03-07 22:04:55 +000029 _previousTestClass = None
30 _moduleSetUpFailed = False
Michael Foordd99ef9a2010-02-23 17:00:53 +000031 def __init__(self, stream=None, descriptions=None, verbosity=None):
Michael Foord1b9e9532010-03-22 01:01:34 +000032 self.failfast = False
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000033 self.failures = []
34 self.errors = []
35 self.testsRun = 0
36 self.skipped = []
37 self.expectedFailures = []
38 self.unexpectedSuccesses = []
39 self.shouldStop = False
40
Michael Foordd99ef9a2010-02-23 17:00:53 +000041 def printErrors(self):
42 "Called by TestRunner after test run"
43
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000044 def startTest(self, test):
45 "Called when the given test is about to be run"
Michael Foorddb43b5a2010-02-10 14:25:12 +000046 self.testsRun += 1
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000047
48 def startTestRun(self):
49 """Called once before any tests are executed.
50
51 See startTest for a method called before each test.
52 """
53
54 def stopTest(self, test):
Michael Foorddb43b5a2010-02-10 14:25:12 +000055 """Called when the given test has been run"""
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000056
57 def stopTestRun(self):
58 """Called once after all tests are executed.
59
60 See stopTest for a method called after each test.
61 """
62
Michael Foord1b9e9532010-03-22 01:01:34 +000063 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000064 def addError(self, test, err):
65 """Called when an error has occurred. 'err' is a tuple of values as
66 returned by sys.exc_info().
67 """
68 self.errors.append((test, self._exc_info_to_string(err, test)))
69
Michael Foord1b9e9532010-03-22 01:01:34 +000070 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000071 def addFailure(self, test, err):
72 """Called when an error has occurred. 'err' is a tuple of values as
73 returned by sys.exc_info()."""
74 self.failures.append((test, self._exc_info_to_string(err, test)))
75
76 def addSuccess(self, test):
77 "Called when a test has completed successfully"
78 pass
79
80 def addSkip(self, test, reason):
81 """Called when a test is skipped."""
82 self.skipped.append((test, reason))
83
84 def addExpectedFailure(self, test, err):
85 """Called when an expected failure/error occured."""
86 self.expectedFailures.append(
87 (test, self._exc_info_to_string(err, test)))
88
Michael Foord1b9e9532010-03-22 01:01:34 +000089 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000090 def addUnexpectedSuccess(self, test):
91 """Called when a test was expected to fail, but succeed."""
92 self.unexpectedSuccesses.append(test)
93
94 def wasSuccessful(self):
95 "Tells whether or not this result was a success"
96 return len(self.failures) == len(self.errors) == 0
97
98 def stop(self):
99 "Indicates that the tests should be aborted"
100 self.shouldStop = True
101
102 def _exc_info_to_string(self, err, test):
103 """Converts a sys.exc_info()-style tuple of values into a string."""
104 exctype, value, tb = err
105 # Skip test runner traceback levels
106 while tb and self._is_relevant_tb_level(tb):
107 tb = tb.tb_next
108 if exctype is test.failureException:
109 # Skip assert*() traceback levels
110 length = self._count_relevant_tb_levels(tb)
111 return ''.join(traceback.format_exception(exctype, value, tb, length))
112 return ''.join(traceback.format_exception(exctype, value, tb))
113
114 def _is_relevant_tb_level(self, tb):
Michael Foordb1aa30f2010-03-22 00:06:30 +0000115 return '__unittest' in tb.tb_frame.f_globals
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000116
117 def _count_relevant_tb_levels(self, tb):
118 length = 0
119 while tb and not self._is_relevant_tb_level(tb):
120 length += 1
121 tb = tb.tb_next
122 return length
123
124 def __repr__(self):
Michael Foordae3db0a2010-02-22 23:28:32 +0000125 return ("<%s run=%i errors=%i failures=%i>" %
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000126 (util.strclass(self.__class__), self.testsRun, len(self.errors),
Michael Foordae3db0a2010-02-22 23:28:32 +0000127 len(self.failures)))