1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
5 Test cases for failure module.
14 from twisted.trial import unittest, util
16 from twisted.python import failure
19 from twisted.test import raiser
24 def getDivisionFailure(*args, **kwargs):
26 Make a C{Failure} of a divide-by-zero error.
28 @param args: Any C{*args} are passed to Failure's constructor.
29 @param kwargs: Any C{**kwargs} are passed to Failure's constructor.
34 f = failure.Failure(*args, **kwargs)
38 class FailureTestCase(unittest.TestCase):
40 def testFailAndTrap(self):
41 """Trapping a failure."""
43 raise NotImplementedError('test')
46 error = f.trap(SystemExit, RuntimeError)
47 self.assertEqual(error, RuntimeError)
48 self.assertEqual(f.type, NotImplementedError)
51 def test_notTrapped(self):
52 """Making sure trap doesn't trap what it shouldn't."""
57 self.assertRaises(failure.Failure, f.trap, OverflowError)
60 def assertStartsWith(self, s, prefix):
62 Assert that s starts with a particular prefix.
64 self.assertTrue(s.startswith(prefix),
65 '%r is not the start of %r' % (prefix, s))
68 def test_printingSmokeTest(self):
70 None of the print* methods fail when called.
72 f = getDivisionFailure()
73 out = StringIO.StringIO()
74 f.printDetailedTraceback(out)
75 self.assertStartsWith(out.getvalue(), '*--- Failure')
76 out = StringIO.StringIO()
77 f.printBriefTraceback(out)
78 self.assertStartsWith(out.getvalue(), 'Traceback')
79 out = StringIO.StringIO()
81 self.assertStartsWith(out.getvalue(), 'Traceback')
84 def test_printingCapturedVarsSmokeTest(self):
86 None of the print* methods fail when called on a L{Failure} constructed
87 with C{captureVars=True}.
89 Local variables on the stack can be seen in the detailed traceback.
91 exampleLocalVar = 'xyzzy'
92 f = getDivisionFailure(captureVars=True)
93 out = StringIO.StringIO()
94 f.printDetailedTraceback(out)
95 self.assertStartsWith(out.getvalue(), '*--- Failure')
96 self.assertNotEqual(None, re.search('exampleLocalVar.*xyzzy',
98 out = StringIO.StringIO()
99 f.printBriefTraceback(out)
100 self.assertStartsWith(out.getvalue(), 'Traceback')
101 out = StringIO.StringIO()
102 f.printTraceback(out)
103 self.assertStartsWith(out.getvalue(), 'Traceback')
106 def test_printingCapturedVarsCleanedSmokeTest(self):
108 C{printDetailedTraceback} includes information about local variables on
109 the stack after C{cleanFailure} has been called.
111 exampleLocalVar = 'xyzzy'
112 f = getDivisionFailure(captureVars=True)
114 out = StringIO.StringIO()
115 f.printDetailedTraceback(out)
116 self.assertNotEqual(None, re.search('exampleLocalVar.*xyzzy',
120 def test_printingNoVars(self):
122 Calling C{Failure()} with no arguments does not capture any locals or
123 globals, so L{printDetailedTraceback} cannot show them in its output.
125 out = StringIO.StringIO()
126 f = getDivisionFailure()
127 f.printDetailedTraceback(out)
128 # There should be no variables in the detailed output. Variables are
129 # printed on lines with 2 leading spaces.
130 linesWithVars = [line for line in out.getvalue().splitlines()
131 if line.startswith(' ')]
132 self.assertEqual([], linesWithVars)
133 self.assertSubstring(
134 'Capture of Locals and Globals disabled', out.getvalue())
137 def test_printingCaptureVars(self):
139 Calling C{Failure(captureVars=True)} captures the locals and globals
140 for its stack frames, so L{printDetailedTraceback} will show them in
143 out = StringIO.StringIO()
144 f = getDivisionFailure(captureVars=True)
145 f.printDetailedTraceback(out)
146 # Variables are printed on lines with 2 leading spaces.
147 linesWithVars = [line for line in out.getvalue().splitlines()
148 if line.startswith(' ')]
149 self.assertNotEqual([], linesWithVars)
152 def testExplictPass(self):
154 f = failure.Failure(e)
156 self.assertEqual(f.value, e)
159 def _getInnermostFrameLine(self, f):
162 except ZeroDivisionError:
163 tb = traceback.extract_tb(sys.exc_info()[2])
167 "f.raiseException() didn't raise ZeroDivisionError!?")
170 def testRaiseExceptionWithTB(self):
171 f = getDivisionFailure()
172 innerline = self._getInnermostFrameLine(f)
173 self.assertEqual(innerline, '1/0')
176 def testLackOfTB(self):
177 f = getDivisionFailure()
179 innerline = self._getInnermostFrameLine(f)
180 self.assertEqual(innerline, '1/0')
182 testLackOfTB.todo = "the traceback is not preserved, exarkun said he'll try to fix this! god knows how"
185 _stringException = "bugger off"
186 def _getStringFailure(self):
188 raise self._stringException
190 f = failure.Failure()
194 def test_raiseStringExceptions(self):
195 # String exceptions used to totally bugged f.raiseException
196 f = self._getStringFailure()
200 self.assertEqual(sys.exc_info()[0], self._stringException)
202 raise AssertionError("Should have raised")
203 test_raiseStringExceptions.suppress = [
204 util.suppress(message='raising a string exception is deprecated')]
207 def test_printStringExceptions(self):
209 L{Failure.printTraceback} should write out stack and exception
210 information, even for string exceptions.
212 failure = self._getStringFailure()
213 output = StringIO.StringIO()
214 failure.printTraceback(file=output)
215 lines = output.getvalue().splitlines()
216 # The last line should be the value of the raised string
217 self.assertEqual(lines[-1], self._stringException)
219 test_printStringExceptions.suppress = [
220 util.suppress(message='raising a string exception is deprecated')]
222 if sys.version_info[:2] >= (2, 6):
223 skipMsg = ("String exceptions aren't supported anymore starting "
225 test_raiseStringExceptions.skip = skipMsg
226 test_printStringExceptions.skip = skipMsg
229 def testConstructionFails(self):
231 Creating a Failure with no arguments causes it to try to discover the
232 current interpreter exception state. If no such state exists, creating
233 the Failure should raise a synchronous exception.
235 self.assertRaises(failure.NoCurrentExceptionError, failure.Failure)
238 def test_getTracebackObject(self):
240 If the C{Failure} has not been cleaned, then C{getTracebackObject}
241 returns the traceback object that captured in its constructor.
243 f = getDivisionFailure()
244 self.assertEqual(f.getTracebackObject(), f.tb)
247 def test_getTracebackObjectFromCaptureVars(self):
249 C{captureVars=True} has no effect on the result of
250 C{getTracebackObject}.
254 except ZeroDivisionError:
255 noVarsFailure = failure.Failure()
256 varsFailure = failure.Failure(captureVars=True)
257 self.assertEqual(noVarsFailure.getTracebackObject(), varsFailure.tb)
260 def test_getTracebackObjectFromClean(self):
262 If the Failure has been cleaned, then C{getTracebackObject} returns an
263 object that looks the same to L{traceback.extract_tb}.
265 f = getDivisionFailure()
266 expected = traceback.extract_tb(f.getTracebackObject())
268 observed = traceback.extract_tb(f.getTracebackObject())
269 self.assertNotEqual(None, expected)
270 self.assertEqual(expected, observed)
273 def test_getTracebackObjectFromCaptureVarsAndClean(self):
275 If the Failure was created with captureVars, then C{getTracebackObject}
276 returns an object that looks the same to L{traceback.extract_tb}.
278 f = getDivisionFailure(captureVars=True)
279 expected = traceback.extract_tb(f.getTracebackObject())
281 observed = traceback.extract_tb(f.getTracebackObject())
282 self.assertEqual(expected, observed)
285 def test_getTracebackObjectWithoutTraceback(self):
287 L{failure.Failure}s need not be constructed with traceback objects. If
288 a C{Failure} has no traceback information at all, C{getTracebackObject}
291 None is a good value, because traceback.extract_tb(None) -> [].
293 f = failure.Failure(Exception("some error"))
294 self.assertEqual(f.getTracebackObject(), None)
298 class BrokenStr(Exception):
300 An exception class the instances of which cannot be presented as strings via
304 # Could raise something else, but there's no point as yet.
309 class BrokenExceptionMetaclass(type):
311 A metaclass for an exception type which cannot be presented as a string via
315 raise ValueError("You cannot make a string out of me.")
319 class BrokenExceptionType(Exception, object):
321 The aforementioned exception type which cnanot be presented as a string via
324 __metaclass__ = BrokenExceptionMetaclass
328 class GetTracebackTests(unittest.TestCase):
330 Tests for L{Failure.getTraceback}.
332 def _brokenValueTest(self, detail):
334 Construct a L{Failure} with an exception that raises an exception from
335 its C{__str__} method and then call C{getTraceback} with the specified
336 detail and verify that it returns a string.
339 f = failure.Failure(x)
340 traceback = f.getTraceback(detail=detail)
341 self.assertIsInstance(traceback, str)
344 def test_brokenValueBriefDetail(self):
346 A L{Failure} might wrap an exception with a C{__str__} method which
347 raises an exception. In this case, calling C{getTraceback} on the
348 failure with the C{"brief"} detail does not raise an exception.
350 self._brokenValueTest("brief")
353 def test_brokenValueDefaultDetail(self):
355 Like test_brokenValueBriefDetail, but for the C{"default"} detail case.
357 self._brokenValueTest("default")
360 def test_brokenValueVerboseDetail(self):
362 Like test_brokenValueBriefDetail, but for the C{"default"} detail case.
364 self._brokenValueTest("verbose")
367 def _brokenTypeTest(self, detail):
369 Construct a L{Failure} with an exception type that raises an exception
370 from its C{__str__} method and then call C{getTraceback} with the
371 specified detail and verify that it returns a string.
373 f = failure.Failure(BrokenExceptionType())
374 traceback = f.getTraceback(detail=detail)
375 self.assertIsInstance(traceback, str)
378 def test_brokenTypeBriefDetail(self):
380 A L{Failure} might wrap an exception the type object of which has a
381 C{__str__} method which raises an exception. In this case, calling
382 C{getTraceback} on the failure with the C{"brief"} detail does not raise
385 self._brokenTypeTest("brief")
388 def test_brokenTypeDefaultDetail(self):
390 Like test_brokenTypeBriefDetail, but for the C{"default"} detail case.
392 self._brokenTypeTest("default")
395 def test_brokenTypeVerboseDetail(self):
397 Like test_brokenTypeBriefDetail, but for the C{"verbose"} detail case.
399 self._brokenTypeTest("verbose")
403 class FindFailureTests(unittest.TestCase):
405 Tests for functionality related to L{Failure._findFailure}.
408 def test_findNoFailureInExceptionHandler(self):
410 Within an exception handler, _findFailure should return
411 C{None} in case no Failure is associated with the current
417 self.assertEqual(failure.Failure._findFailure(), None)
419 self.fail("No exception raised from 1/0!?")
422 def test_findNoFailure(self):
424 Outside of an exception handler, _findFailure should return None.
426 self.assertEqual(sys.exc_info()[-1], None) #environment sanity check
427 self.assertEqual(failure.Failure._findFailure(), None)
430 def test_findFailure(self):
432 Within an exception handler, it should be possible to find the
433 original Failure that caused the current exception (if it was
434 caused by raiseException).
436 f = getDivisionFailure()
441 self.assertEqual(failure.Failure._findFailure(), f)
443 self.fail("No exception raised from raiseException!?")
446 def test_failureConstructionFindsOriginalFailure(self):
448 When a Failure is constructed in the context of an exception
449 handler that is handling an exception raised by
450 raiseException, the new Failure should be chained to that
453 f = getDivisionFailure()
458 newF = failure.Failure()
459 self.assertEqual(f.getTraceback(), newF.getTraceback())
461 self.fail("No exception raised from raiseException!?")
464 def test_failureConstructionWithMungedStackSucceeds(self):
466 Pyrex and Cython are known to insert fake stack frames so as to give
467 more Python-like tracebacks. These stack frames with empty code objects
468 should not break extraction of the exception.
471 raiser.raiseException()
472 except raiser.RaiserException:
473 f = failure.Failure()
474 self.assertTrue(f.check(raiser.RaiserException))
476 self.fail("No exception raised from extension?!")
480 skipMsg = "raiser extension not available"
481 test_failureConstructionWithMungedStackSucceeds.skip = skipMsg
485 class TestFormattableTraceback(unittest.TestCase):
487 Whitebox tests that show that L{failure._Traceback} constructs objects that
488 can be used by L{traceback.extract_tb}.
490 If the objects can be used by L{traceback.extract_tb}, then they can be
491 formatted using L{traceback.format_tb} and friends.
494 def test_singleFrame(self):
496 A C{_Traceback} object constructed with a single frame should be able
497 to be passed to L{traceback.extract_tb}, and we should get a singleton
498 list containing a (filename, lineno, methodname, line) tuple.
500 tb = failure._Traceback([['method', 'filename.py', 123, {}, {}]])
501 # Note that we don't need to test that extract_tb correctly extracts
502 # the line's contents. In this case, since filename.py doesn't exist,
503 # it will just use None.
504 self.assertEqual(traceback.extract_tb(tb),
505 [('filename.py', 123, 'method', None)])
508 def test_manyFrames(self):
510 A C{_Traceback} object constructed with multiple frames should be able
511 to be passed to L{traceback.extract_tb}, and we should get a list
512 containing a tuple for each frame.
514 tb = failure._Traceback([
515 ['method1', 'filename.py', 123, {}, {}],
516 ['method2', 'filename.py', 235, {}, {}]])
517 self.assertEqual(traceback.extract_tb(tb),
518 [('filename.py', 123, 'method1', None),
519 ('filename.py', 235, 'method2', None)])
523 class TestFrameAttributes(unittest.TestCase):
525 _Frame objects should possess some basic attributes that qualify them as
526 fake python Frame objects.
529 def test_fakeFrameAttributes(self):
531 L{_Frame} instances have the C{f_globals} and C{f_locals} attributes
532 bound to C{dict} instance. They also have the C{f_code} attribute
533 bound to something like a code object.
535 frame = failure._Frame("dummyname", "dummyfilename")
536 self.assertIsInstance(frame.f_globals, dict)
537 self.assertIsInstance(frame.f_locals, dict)
538 self.assertIsInstance(frame.f_code, failure._Code)
542 class TestDebugMode(unittest.TestCase):
544 Failure's debug mode should allow jumping into the debugger.
549 Override pdb.post_mortem so we can make sure it's called.
551 # Make sure any changes we make are reversed:
552 post_mortem = pdb.post_mortem
553 origInit = failure.Failure.__dict__['__init__']
555 pdb.post_mortem = post_mortem
556 failure.Failure.__dict__['__init__'] = origInit
557 self.addCleanup(restore)
560 pdb.post_mortem = self.result.append
561 failure.startDebugMode()
564 def test_regularFailure(self):
566 If startDebugMode() is called, calling Failure() will first call
567 pdb.post_mortem with the traceback.
572 typ, exc, tb = sys.exc_info()
573 f = failure.Failure()
574 self.assertEqual(self.result, [tb])
575 self.assertEqual(f.captureVars, False)
578 def test_captureVars(self):
580 If startDebugMode() is called, passing captureVars to Failure() will
586 typ, exc, tb = sys.exc_info()
587 f = failure.Failure(captureVars=True)
588 self.assertEqual(self.result, [tb])
589 self.assertEqual(f.captureVars, True)
593 if sys.version_info[:2] >= (2, 5):
594 from twisted.test.generator_failure_tests import TwoPointFiveFailureTests