| 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 |