0001 # Testing the line trace facility. 0002 0003 from test import test_support 0004 import unittest 0005 import sys 0006 import difflib 0007 0008 # A very basic example. If this fails, we're in deep trouble. 0009 def basic(): 0010 return 1 0011 0012 basic.events = [(0, 'call'), 0013 (1, 'line'), 0014 (1, 'return')] 0015 0016 # Armin Rigo's failing example: 0017 def arigo_example(): 0018 x = 1 0019 del x 0020 while 0: 0021 pass 0022 x = 1 0023 0024 arigo_example.events = [(0, 'call'), 0025 (1, 'line'), 0026 (2, 'line'), 0027 (3, 'line'), 0028 (5, 'line'), 0029 (5, 'return')] 0030 0031 # check that lines consisting of just one instruction get traced: 0032 def one_instr_line(): 0033 x = 1 0034 del x 0035 x = 1 0036 0037 one_instr_line.events = [(0, 'call'), 0038 (1, 'line'), 0039 (2, 'line'), 0040 (3, 'line'), 0041 (3, 'return')] 0042 0043 def no_pop_tops(): # 0 0044 x = 1 # 1 0045 for a in range(2): # 2 0046 if a: # 3 0047 x = 1 # 4 0048 else: # 5 0049 x = 1 # 6 0050 0051 no_pop_tops.events = [(0, 'call'), 0052 (1, 'line'), 0053 (2, 'line'), 0054 (3, 'line'), 0055 (6, 'line'), 0056 (2, 'line'), 0057 (3, 'line'), 0058 (4, 'line'), 0059 (2, 'line'), 0060 (2, 'return')] 0061 0062 def no_pop_blocks(): 0063 while 0: 0064 bla 0065 x = 1 0066 0067 no_pop_blocks.events = [(0, 'call'), 0068 (1, 'line'), 0069 (3, 'line'), 0070 (3, 'return')] 0071 0072 def called(): # line -3 0073 x = 1 0074 0075 def call(): # line 0 0076 called() 0077 0078 call.events = [(0, 'call'), 0079 (1, 'line'), 0080 (-3, 'call'), 0081 (-2, 'line'), 0082 (-2, 'return'), 0083 (1, 'return')] 0084 0085 def raises(): 0086 raise Exception 0087 0088 def test_raise(): 0089 try: 0090 raises() 0091 except Exception, exc: 0092 x = 1 0093 0094 test_raise.events = [(0, 'call'), 0095 (1, 'line'), 0096 (2, 'line'), 0097 (-3, 'call'), 0098 (-2, 'line'), 0099 (-2, 'exception'), 0100 (2, 'exception'), 0101 (3, 'line'), 0102 (4, 'line'), 0103 (4, 'return')] 0104 0105 def _settrace_and_return(tracefunc): 0106 sys.settrace(tracefunc) 0107 sys._getframe().f_back.f_trace = tracefunc 0108 def settrace_and_return(tracefunc): 0109 _settrace_and_return(tracefunc) 0110 0111 settrace_and_return.events = [(1, 'return')] 0112 0113 def _settrace_and_raise(tracefunc): 0114 sys.settrace(tracefunc) 0115 sys._getframe().f_back.f_trace = tracefunc 0116 raise RuntimeError 0117 def settrace_and_raise(tracefunc): 0118 try: 0119 _settrace_and_raise(tracefunc) 0120 except RuntimeError, exc: 0121 pass 0122 0123 settrace_and_raise.events = [(2, 'exception'), 0124 (3, 'line'), 0125 (4, 'line'), 0126 (4, 'return')] 0127 0128 # implicit return example 0129 def ireturn_example(): 0130 a = 5 0131 b = 5 0132 if a == b: 0133 b = a+1 0134 else: 0135 pass 0136 0137 ireturn_example.events = [(0, 'call'), 0138 (1, 'line'), 0139 (2, 'line'), 0140 (3, 'line'), 0141 (4, 'line'), 0142 (4, 'return')] 0143 0144 # Tight loop with while(1) example (SF #765624) 0145 def tightloop_example(): 0146 items = range(0, 3) 0147 try: 0148 i = 0 0149 while 1: 0150 b = items[i]; i+=1 0151 except IndexError: 0152 pass 0153 0154 tightloop_example.events = [(0, 'call'), 0155 (1, 'line'), 0156 (2, 'line'), 0157 (3, 'line'), 0158 (4, 'line'), 0159 (5, 'line'), 0160 (5, 'line'), 0161 (5, 'line'), 0162 (5, 'line'), 0163 (5, 'exception'), 0164 (6, 'line'), 0165 (7, 'line'), 0166 (7, 'return')] 0167 0168 def tighterloop_example(): 0169 items = range(1, 4) 0170 try: 0171 i = 0 0172 while 1: i = items[i] 0173 except IndexError: 0174 pass 0175 0176 tighterloop_example.events = [(0, 'call'), 0177 (1, 'line'), 0178 (2, 'line'), 0179 (3, 'line'), 0180 (4, 'line'), 0181 (4, 'line'), 0182 (4, 'line'), 0183 (4, 'line'), 0184 (4, 'exception'), 0185 (5, 'line'), 0186 (6, 'line'), 0187 (6, 'return')] 0188 0189 class Tracer: 0190 def __init__(self): 0191 self.events = [] 0192 def trace(self, frame, event, arg): 0193 self.events.append((frame.f_lineno, event)) 0194 return self.trace 0195 0196 class TraceTestCase(unittest.TestCase): 0197 def compare_events(self, line_offset, events, expected_events): 0198 events = [(l - line_offset, e) for (l, e) in events] 0199 if events != expected_events: 0200 self.fail( 0201 "events did not match expectation:\n" + 0202 "\n".join(difflib.ndiff(map(str, expected_events), 0203 map(str, events)))) 0204 0205 0206 def run_test(self, func): 0207 tracer = Tracer() 0208 sys.settrace(tracer.trace) 0209 func() 0210 sys.settrace(None) 0211 self.compare_events(func.func_code.co_firstlineno, 0212 tracer.events, func.events) 0213 0214 def run_test2(self, func): 0215 tracer = Tracer() 0216 func(tracer.trace) 0217 sys.settrace(None) 0218 self.compare_events(func.func_code.co_firstlineno, 0219 tracer.events, func.events) 0220 0221 def test_01_basic(self): 0222 self.run_test(basic) 0223 def test_02_arigo(self): 0224 self.run_test(arigo_example) 0225 def test_03_one_instr(self): 0226 self.run_test(one_instr_line) 0227 def test_04_no_pop_blocks(self): 0228 self.run_test(no_pop_blocks) 0229 def test_05_no_pop_tops(self): 0230 self.run_test(no_pop_tops) 0231 def test_06_call(self): 0232 self.run_test(call) 0233 def test_07_raise(self): 0234 self.run_test(test_raise) 0235 0236 def test_08_settrace_and_return(self): 0237 self.run_test2(settrace_and_return) 0238 def test_09_settrace_and_raise(self): 0239 self.run_test2(settrace_and_raise) 0240 def test_10_ireturn(self): 0241 self.run_test(ireturn_example) 0242 def test_11_tightloop(self): 0243 self.run_test(tightloop_example) 0244 def test_12_tighterloop(self): 0245 self.run_test(tighterloop_example) 0246 0247 class RaisingTraceFuncTestCase(unittest.TestCase): 0248 def trace(self, frame, event, arg): 0249 """A trace function that raises an exception in response to a 0250 specific trace event.""" 0251 if event == self.raiseOnEvent: 0252 raise ValueError # just something that isn't RuntimeError 0253 else: 0254 return self.trace 0255 0256 def f(self): 0257 """The function to trace; raises an exception if that's the case 0258 we're testing, so that the 'exception' trace event fires.""" 0259 if self.raiseOnEvent == 'exception': 0260 x = 0 0261 y = 1/x 0262 else: 0263 return 1 0264 0265 def run_test_for_event(self, event): 0266 """Tests that an exception raised in response to the given event is 0267 handled OK.""" 0268 self.raiseOnEvent = event 0269 try: 0270 for i in xrange(sys.getrecursionlimit() + 1): 0271 sys.settrace(self.trace) 0272 try: 0273 self.f() 0274 except ValueError: 0275 pass 0276 else: 0277 self.fail("exception not thrown!") 0278 except RuntimeError: 0279 self.fail("recursion counter not reset") 0280 0281 # Test the handling of exceptions raised by each kind of trace event. 0282 def test_call(self): 0283 self.run_test_for_event('call') 0284 def test_line(self): 0285 self.run_test_for_event('line') 0286 def test_return(self): 0287 self.run_test_for_event('return') 0288 def test_exception(self): 0289 self.run_test_for_event('exception') 0290 0291 def test_trash_stack(self): 0292 def f(): 0293 for i in range(5): 0294 print i # line tracing will raise an exception at this line 0295 0296 def g(frame, why, extra): 0297 if (why == 'line' and 0298 frame.f_lineno == f.func_code.co_firstlineno + 2): 0299 raise RuntimeError, "i am crashing" 0300 return g 0301 0302 sys.settrace(g) 0303 try: 0304 f() 0305 except RuntimeError: 0306 # the test is really that this doesn't segfault: 0307 import gc 0308 gc.collect() 0309 else: 0310 self.fail("exception not propagated") 0311 0312 0313 # 'Jump' tests: assigning to frame.f_lineno within a trace function 0314 # moves the execution position - it's how debuggers implement a Jump 0315 # command (aka. "Set next statement"). 0316 0317 class JumpTracer: 0318 """Defines a trace function that jumps from one place to another, 0319 with the source and destination lines of the jump being defined by 0320 the 'jump' property of the function under test.""" 0321 0322 def __init__(self, function): 0323 self.function = function 0324 self.jumpFrom = function.jump[0] 0325 self.jumpTo = function.jump[1] 0326 self.done = False 0327 0328 def trace(self, frame, event, arg): 0329 if not self.done and frame.f_code == self.function.func_code: 0330 firstLine = frame.f_code.co_firstlineno 0331 if frame.f_lineno == firstLine + self.jumpFrom: 0332 # Cope with non-integer self.jumpTo (because of 0333 # no_jump_to_non_integers below). 0334 try: 0335 frame.f_lineno = firstLine + self.jumpTo 0336 except TypeError: 0337 frame.f_lineno = self.jumpTo 0338 self.done = True 0339 return self.trace 0340 0341 # The first set of 'jump' tests are for things that are allowed: 0342 0343 def jump_simple_forwards(output): 0344 output.append(1) 0345 output.append(2) 0346 output.append(3) 0347 0348 jump_simple_forwards.jump = (1, 3) 0349 jump_simple_forwards.output = [3] 0350 0351 def jump_simple_backwards(output): 0352 output.append(1) 0353 output.append(2) 0354 0355 jump_simple_backwards.jump = (2, 1) 0356 jump_simple_backwards.output = [1, 1, 2] 0357 0358 def jump_out_of_block_forwards(output): 0359 for i in 1, 2: 0360 output.append(2) 0361 for j in [3]: # Also tests jumping over a block 0362 output.append(4) 0363 output.append(5) 0364 0365 jump_out_of_block_forwards.jump = (3, 5) 0366 jump_out_of_block_forwards.output = [2, 5] 0367 0368 def jump_out_of_block_backwards(output): 0369 output.append(1) 0370 for i in [1]: 0371 output.append(3) 0372 for j in [2]: # Also tests jumping over a block 0373 output.append(5) 0374 output.append(6) 0375 output.append(7) 0376 0377 jump_out_of_block_backwards.jump = (6, 1) 0378 jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7] 0379 0380 def jump_to_codeless_line(output): 0381 output.append(1) 0382 # Jumping to this line should skip to the next one. 0383 output.append(3) 0384 0385 jump_to_codeless_line.jump = (1, 2) 0386 jump_to_codeless_line.output = [3] 0387 0388 def jump_to_same_line(output): 0389 output.append(1) 0390 output.append(2) 0391 output.append(3) 0392 0393 jump_to_same_line.jump = (2, 2) 0394 jump_to_same_line.output = [1, 2, 3] 0395 0396 # Tests jumping within a finally block, and over one. 0397 def jump_in_nested_finally(output): 0398 try: 0399 output.append(2) 0400 finally: 0401 output.append(4) 0402 try: 0403 output.append(6) 0404 finally: 0405 output.append(8) 0406 output.append(9) 0407 0408 jump_in_nested_finally.jump = (4, 9) 0409 jump_in_nested_finally.output = [2, 9] 0410 0411 # The second set of 'jump' tests are for things that are not allowed: 0412 0413 def no_jump_too_far_forwards(output): 0414 try: 0415 output.append(2) 0416 output.append(3) 0417 except ValueError, e: 0418 output.append('after' in str(e)) 0419 0420 no_jump_too_far_forwards.jump = (3, 6) 0421 no_jump_too_far_forwards.output = [2, True] 0422 0423 def no_jump_too_far_backwards(output): 0424 try: 0425 output.append(2) 0426 output.append(3) 0427 except ValueError, e: 0428 output.append('before' in str(e)) 0429 0430 no_jump_too_far_backwards.jump = (3, -1) 0431 no_jump_too_far_backwards.output = [2, True] 0432 0433 # Test each kind of 'except' line. 0434 def no_jump_to_except_1(output): 0435 try: 0436 output.append(2) 0437 except: 0438 e = sys.exc_info()[1] 0439 output.append('except' in str(e)) 0440 0441 no_jump_to_except_1.jump = (2, 3) 0442 no_jump_to_except_1.output = [True] 0443 0444 def no_jump_to_except_2(output): 0445 try: 0446 output.append(2) 0447 except ValueError: 0448 e = sys.exc_info()[1] 0449 output.append('except' in str(e)) 0450 0451 no_jump_to_except_2.jump = (2, 3) 0452 no_jump_to_except_2.output = [True] 0453 0454 def no_jump_to_except_3(output): 0455 try: 0456 output.append(2) 0457 except ValueError, e: 0458 output.append('except' in str(e)) 0459 0460 no_jump_to_except_3.jump = (2, 3) 0461 no_jump_to_except_3.output = [True] 0462 0463 def no_jump_to_except_4(output): 0464 try: 0465 output.append(2) 0466 except (ValueError, RuntimeError), e: 0467 output.append('except' in str(e)) 0468 0469 no_jump_to_except_4.jump = (2, 3) 0470 no_jump_to_except_4.output = [True] 0471 0472 def no_jump_forwards_into_block(output): 0473 try: 0474 output.append(2) 0475 for i in 1, 2: 0476 output.append(4) 0477 except ValueError, e: 0478 output.append('into' in str(e)) 0479 0480 no_jump_forwards_into_block.jump = (2, 4) 0481 no_jump_forwards_into_block.output = [True] 0482 0483 def no_jump_backwards_into_block(output): 0484 try: 0485 for i in 1, 2: 0486 output.append(3) 0487 output.append(4) 0488 except ValueError, e: 0489 output.append('into' in str(e)) 0490 0491 no_jump_backwards_into_block.jump = (4, 3) 0492 no_jump_backwards_into_block.output = [3, 3, True] 0493 0494 def no_jump_into_finally_block(output): 0495 try: 0496 try: 0497 output.append(3) 0498 x = 1 0499 finally: 0500 output.append(6) 0501 except ValueError, e: 0502 output.append('finally' in str(e)) 0503 0504 no_jump_into_finally_block.jump = (4, 6) 0505 no_jump_into_finally_block.output = [3, 6, True] # The 'finally' still runs 0506 0507 def no_jump_out_of_finally_block(output): 0508 try: 0509 try: 0510 output.append(3) 0511 finally: 0512 output.append(5) 0513 output.append(6) 0514 except ValueError, e: 0515 output.append('finally' in str(e)) 0516 0517 no_jump_out_of_finally_block.jump = (5, 1) 0518 no_jump_out_of_finally_block.output = [3, True] 0519 0520 # This verifies the line-numbers-must-be-integers rule. 0521 def no_jump_to_non_integers(output): 0522 try: 0523 output.append(2) 0524 except ValueError, e: 0525 output.append('integer' in str(e)) 0526 0527 no_jump_to_non_integers.jump = (2, "Spam") 0528 no_jump_to_non_integers.output = [True] 0529 0530 # This verifies that you can't set f_lineno via _getframe or similar 0531 # trickery. 0532 def no_jump_without_trace_function(): 0533 try: 0534 previous_frame = sys._getframe().f_back 0535 previous_frame.f_lineno = previous_frame.f_lineno 0536 except ValueError, e: 0537 # This is the exception we wanted; make sure the error message 0538 # talks about trace functions. 0539 if 'trace' not in str(e): 0540 raise 0541 else: 0542 # Something's wrong - the expected exception wasn't raised. 0543 raise RuntimeError, "Trace-function-less jump failed to fail" 0544 0545 0546 class JumpTestCase(unittest.TestCase): 0547 def compare_jump_output(self, expected, received): 0548 if received != expected: 0549 self.fail( "Outputs don't match:\n" + 0550 "Expected: " + repr(expected) + "\n" + 0551 "Received: " + repr(received)) 0552 0553 def run_test(self, func): 0554 tracer = JumpTracer(func) 0555 sys.settrace(tracer.trace) 0556 output = [] 0557 func(output) 0558 sys.settrace(None) 0559 self.compare_jump_output(func.output, output) 0560 0561 def test_01_jump_simple_forwards(self): 0562 self.run_test(jump_simple_forwards) 0563 def test_02_jump_simple_backwards(self): 0564 self.run_test(jump_simple_backwards) 0565 def test_03_jump_out_of_block_forwards(self): 0566 self.run_test(jump_out_of_block_forwards) 0567 def test_04_jump_out_of_block_backwards(self): 0568 self.run_test(jump_out_of_block_backwards) 0569 def test_05_jump_to_codeless_line(self): 0570 self.run_test(jump_to_codeless_line) 0571 def test_06_jump_to_same_line(self): 0572 self.run_test(jump_to_same_line) 0573 def test_07_jump_in_nested_finally(self): 0574 self.run_test(jump_in_nested_finally) 0575 def test_08_no_jump_too_far_forwards(self): 0576 self.run_test(no_jump_too_far_forwards) 0577 def test_09_no_jump_too_far_backwards(self): 0578 self.run_test(no_jump_too_far_backwards) 0579 def test_10_no_jump_to_except_1(self): 0580 self.run_test(no_jump_to_except_1) 0581 def test_11_no_jump_to_except_2(self): 0582 self.run_test(no_jump_to_except_2) 0583 def test_12_no_jump_to_except_3(self): 0584 self.run_test(no_jump_to_except_3) 0585 def test_13_no_jump_to_except_4(self): 0586 self.run_test(no_jump_to_except_4) 0587 def test_14_no_jump_forwards_into_block(self): 0588 self.run_test(no_jump_forwards_into_block) 0589 def test_15_no_jump_backwards_into_block(self): 0590 self.run_test(no_jump_backwards_into_block) 0591 def test_16_no_jump_into_finally_block(self): 0592 self.run_test(no_jump_into_finally_block) 0593 def test_17_no_jump_out_of_finally_block(self): 0594 self.run_test(no_jump_out_of_finally_block) 0595 def test_18_no_jump_to_non_integers(self): 0596 self.run_test(no_jump_to_non_integers) 0597 def test_19_no_jump_without_trace_function(self): 0598 no_jump_without_trace_function() 0599 0600 def test_main(): 0601 test_support.run_unittest( 0602 TraceTestCase, 0603 RaisingTraceFuncTestCase, 0604 JumpTestCase 0605 ) 0606 0607 if __name__ == "__main__": 0608 test_main() 0609
Generated by PyXR 0.9.4