OLD | NEW |
| (Empty) |
1 # Copyright (c) 2001-2008 Twisted Matrix Laboratories. | |
2 # See LICENSE for details. | |
3 | |
4 | |
5 """ | |
6 Test cases for failure module. | |
7 """ | |
8 | |
9 import sys | |
10 import StringIO | |
11 import traceback | |
12 | |
13 from twisted.trial import unittest, util | |
14 | |
15 from twisted.python import failure | |
16 | |
17 try: | |
18 from twisted.test import raiser | |
19 except ImportError: | |
20 raiser = None | |
21 | |
22 | |
23 class BrokenStr(Exception): | |
24 def __str__(self): | |
25 raise self | |
26 | |
27 | |
28 def getDivisionFailure(): | |
29 try: | |
30 1/0 | |
31 except: | |
32 f = failure.Failure() | |
33 return f | |
34 | |
35 | |
36 class FailureTestCase(unittest.TestCase): | |
37 | |
38 def testFailAndTrap(self): | |
39 """Trapping a failure.""" | |
40 try: | |
41 raise NotImplementedError('test') | |
42 except: | |
43 f = failure.Failure() | |
44 error = f.trap(SystemExit, RuntimeError) | |
45 self.assertEquals(error, RuntimeError) | |
46 self.assertEquals(f.type, NotImplementedError) | |
47 | |
48 def test_notTrapped(self): | |
49 """Making sure trap doesn't trap what it shouldn't.""" | |
50 try: | |
51 raise ValueError() | |
52 except: | |
53 f = failure.Failure() | |
54 self.assertRaises(failure.Failure, f.trap, OverflowError) | |
55 | |
56 def testPrinting(self): | |
57 out = StringIO.StringIO() | |
58 try: | |
59 1/0 | |
60 except: | |
61 f = failure.Failure() | |
62 f.printDetailedTraceback(out) | |
63 f.printBriefTraceback(out) | |
64 f.printTraceback(out) | |
65 | |
66 def testExplictPass(self): | |
67 e = RuntimeError() | |
68 f = failure.Failure(e) | |
69 f.trap(RuntimeError) | |
70 self.assertEquals(f.value, e) | |
71 | |
72 | |
73 def _getInnermostFrameLine(self, f): | |
74 try: | |
75 f.raiseException() | |
76 except ZeroDivisionError: | |
77 tb = traceback.extract_tb(sys.exc_info()[2]) | |
78 return tb[-1][-1] | |
79 else: | |
80 raise Exception( | |
81 "f.raiseException() didn't raise ZeroDivisionError!?") | |
82 | |
83 | |
84 def testRaiseExceptionWithTB(self): | |
85 f = getDivisionFailure() | |
86 innerline = self._getInnermostFrameLine(f) | |
87 self.assertEquals(innerline, '1/0') | |
88 | |
89 | |
90 def testLackOfTB(self): | |
91 f = getDivisionFailure() | |
92 f.cleanFailure() | |
93 innerline = self._getInnermostFrameLine(f) | |
94 self.assertEquals(innerline, '1/0') | |
95 | |
96 testLackOfTB.todo = "the traceback is not preserved, exarkun said he'll try
to fix this! god knows how" | |
97 | |
98 | |
99 _stringException = "bugger off" | |
100 def _getStringFailure(self): | |
101 try: | |
102 raise self._stringException | |
103 except: | |
104 f = failure.Failure() | |
105 return f | |
106 | |
107 def test_raiseStringExceptions(self): | |
108 # String exceptions used to totally bugged f.raiseException | |
109 f = self._getStringFailure() | |
110 try: | |
111 f.raiseException() | |
112 except: | |
113 self.assertEquals(sys.exc_info()[0], self._stringException) | |
114 else: | |
115 raise AssertionError("Should have raised") | |
116 test_raiseStringExceptions.suppress = [ | |
117 util.suppress(message='raising a string exception is deprecated')] | |
118 | |
119 | |
120 def test_printStringExceptions(self): | |
121 """ | |
122 L{Failure.printTraceback} should write out stack and exception | |
123 information, even for string exceptions. | |
124 """ | |
125 failure = self._getStringFailure() | |
126 output = StringIO.StringIO() | |
127 failure.printTraceback(file=output) | |
128 lines = output.getvalue().splitlines() | |
129 # The last line should be the value of the raised string | |
130 self.assertEqual(lines[-1], self._stringException) | |
131 | |
132 test_printStringExceptions.suppress = [ | |
133 util.suppress(message='raising a string exception is deprecated')] | |
134 | |
135 if sys.version_info[:2] >= (2, 6): | |
136 skipMsg = ("String exceptions aren't supported anymore starting " | |
137 "Python 2.6") | |
138 test_raiseStringExceptions.skip = skipMsg | |
139 test_printStringExceptions.skip = skipMsg | |
140 | |
141 | |
142 def testBrokenStr(self): | |
143 """ | |
144 Formatting a traceback of a Failure which refers to an object | |
145 that has a broken __str__ implementation should not cause | |
146 getTraceback to raise an exception. | |
147 """ | |
148 x = BrokenStr() | |
149 try: | |
150 str(x) | |
151 except: | |
152 f = failure.Failure() | |
153 self.assertEquals(f.value, x) | |
154 try: | |
155 f.getTraceback() | |
156 except: | |
157 self.fail("getTraceback() shouldn't raise an exception") | |
158 | |
159 | |
160 def testConstructionFails(self): | |
161 """ | |
162 Creating a Failure with no arguments causes it to try to discover the | |
163 current interpreter exception state. If no such state exists, creating | |
164 the Failure should raise a synchronous exception. | |
165 """ | |
166 self.assertRaises(failure.NoCurrentExceptionError, failure.Failure) | |
167 | |
168 def test_getTracebackObject(self): | |
169 """ | |
170 If the C{Failure} has not been cleaned, then C{getTracebackObject} | |
171 should return the traceback object that it was given in the | |
172 constructor. | |
173 """ | |
174 f = getDivisionFailure() | |
175 self.assertEqual(f.getTracebackObject(), f.tb) | |
176 | |
177 def test_getTracebackObjectFromClean(self): | |
178 """ | |
179 If the Failure has been cleaned, then C{getTracebackObject} should | |
180 return an object that looks the same to L{traceback.extract_tb}. | |
181 """ | |
182 f = getDivisionFailure() | |
183 expected = traceback.extract_tb(f.getTracebackObject()) | |
184 f.cleanFailure() | |
185 observed = traceback.extract_tb(f.getTracebackObject()) | |
186 self.assertEqual(expected, observed) | |
187 | |
188 def test_getTracebackObjectWithoutTraceback(self): | |
189 """ | |
190 L{failure.Failure}s need not be constructed with traceback objects. If | |
191 a C{Failure} has no traceback information at all, C{getTracebackObject} | |
192 should just return None. | |
193 | |
194 None is a good value, because traceback.extract_tb(None) -> []. | |
195 """ | |
196 f = failure.Failure(Exception("some error")) | |
197 self.assertEqual(f.getTracebackObject(), None) | |
198 | |
199 class FindFailureTests(unittest.TestCase): | |
200 """ | |
201 Tests for functionality related to L{Failure._findFailure}. | |
202 """ | |
203 | |
204 def test_findNoFailureInExceptionHandler(self): | |
205 """ | |
206 Within an exception handler, _findFailure should return | |
207 C{None} in case no Failure is associated with the current | |
208 exception. | |
209 """ | |
210 try: | |
211 1/0 | |
212 except: | |
213 self.assertEqual(failure.Failure._findFailure(), None) | |
214 else: | |
215 self.fail("No exception raised from 1/0!?") | |
216 | |
217 | |
218 def test_findNoFailure(self): | |
219 """ | |
220 Outside of an exception handler, _findFailure should return None. | |
221 """ | |
222 self.assertEqual(sys.exc_info()[-1], None) #environment sanity check | |
223 self.assertEqual(failure.Failure._findFailure(), None) | |
224 | |
225 | |
226 def test_findFailure(self): | |
227 """ | |
228 Within an exception handler, it should be possible to find the | |
229 original Failure that caused the current exception (if it was | |
230 caused by raiseException). | |
231 """ | |
232 f = getDivisionFailure() | |
233 f.cleanFailure() | |
234 try: | |
235 f.raiseException() | |
236 except: | |
237 self.assertEqual(failure.Failure._findFailure(), f) | |
238 else: | |
239 self.fail("No exception raised from raiseException!?") | |
240 | |
241 | |
242 def test_failureConstructionFindsOriginalFailure(self): | |
243 """ | |
244 When a Failure is constructed in the context of an exception | |
245 handler that is handling an exception raised by | |
246 raiseException, the new Failure should be chained to that | |
247 original Failure. | |
248 """ | |
249 f = getDivisionFailure() | |
250 f.cleanFailure() | |
251 try: | |
252 f.raiseException() | |
253 except: | |
254 newF = failure.Failure() | |
255 self.assertEqual(f.getTraceback(), newF.getTraceback()) | |
256 else: | |
257 self.fail("No exception raised from raiseException!?") | |
258 | |
259 | |
260 def test_failureConstructionWithMungedStackSucceeds(self): | |
261 """ | |
262 Pyrex and Cython are known to insert fake stack frames so as to give | |
263 more Python-like tracebacks. These stack frames with empty code objects | |
264 should not break extraction of the exception. | |
265 """ | |
266 try: | |
267 raiser.raiseException() | |
268 except raiser.RaiserException: | |
269 f = failure.Failure() | |
270 self.assertTrue(f.check(raiser.RaiserException)) | |
271 else: | |
272 self.fail("No exception raised from extension?!") | |
273 | |
274 | |
275 if raiser is None: | |
276 skipMsg = "raiser extension not available" | |
277 test_failureConstructionWithMungedStackSucceeds.skip = skipMsg | |
278 | |
279 | |
280 | |
281 class TestFormattableTraceback(unittest.TestCase): | |
282 """ | |
283 Whitebox tests that show that L{failure._Traceback} constructs objects that | |
284 can be used by L{traceback.extract_tb}. | |
285 | |
286 If the objects can be used by L{traceback.extract_tb}, then they can be | |
287 formatted using L{traceback.format_tb} and friends. | |
288 """ | |
289 | |
290 def test_singleFrame(self): | |
291 """ | |
292 A C{_Traceback} object constructed with a single frame should be able | |
293 to be passed to L{traceback.extract_tb}, and we should get a singleton | |
294 list containing a (filename, lineno, methodname, line) tuple. | |
295 """ | |
296 tb = failure._Traceback([['method', 'filename.py', 123, {}, {}]]) | |
297 # Note that we don't need to test that extract_tb correctly extracts | |
298 # the line's contents. In this case, since filename.py doesn't exist, | |
299 # it will just use None. | |
300 self.assertEqual(traceback.extract_tb(tb), | |
301 [('filename.py', 123, 'method', None)]) | |
302 | |
303 def test_manyFrames(self): | |
304 """ | |
305 A C{_Traceback} object constructed with multiple frames should be able | |
306 to be passed to L{traceback.extract_tb}, and we should get a list | |
307 containing a tuple for each frame. | |
308 """ | |
309 tb = failure._Traceback([ | |
310 ['method1', 'filename.py', 123, {}, {}], | |
311 ['method2', 'filename.py', 235, {}, {}]]) | |
312 self.assertEqual(traceback.extract_tb(tb), | |
313 [('filename.py', 123, 'method1', None), | |
314 ('filename.py', 235, 'method2', None)]) | |
315 | |
316 | |
317 if sys.version_info[:2] >= (2, 5): | |
318 from twisted.test.generator_failure_tests import TwoPointFiveFailureTests | |
OLD | NEW |