blob: 6aac8df8ae6c76101260e23f7effc3e9649eb355 [file] [log] [blame]
Michael W. Hudson53d58bb2002-08-30 13:09:51 +00001# Testing the line trace facility.
2
3from test import test_support
4import unittest
5import sys
6import difflib
7
8# A very basic example. If this fails, we're in deep trouble.
9def basic():
10 return 1
11
12basic.events = [(0, 'call'),
13 (1, 'line'),
14 (1, 'return')]
15
16# Armin Rigo's failing example:
17def arigo_example():
18 x = 1
19 del x
20 while 0:
21 pass
22 x = 1
23
24arigo_example.events = [(0, 'call'),
25 (1, 'line'),
26 (2, 'line'),
27 (3, 'line'),
28 (5, 'line'),
29 (5, 'return')]
30
31# check that lines consisting of just one instruction get traced:
32def one_instr_line():
33 x = 1
34 del x
35 x = 1
36
37one_instr_line.events = [(0, 'call'),
38 (1, 'line'),
39 (2, 'line'),
40 (3, 'line'),
41 (3, 'return')]
42
43def no_pop_tops(): # 0
44 x = 1 # 1
45 for a in range(2): # 2
46 if a: # 3
47 x = 1 # 4
48 else: # 5
49 x = 1 # 6
50
51no_pop_tops.events = [(0, 'call'),
52 (1, 'line'),
53 (2, 'line'),
54 (3, 'line'),
55 (6, 'line'),
56 (2, 'line'),
57 (3, 'line'),
58 (4, 'line'),
59 (2, 'line'),
Michael W. Hudson02ff6a92002-09-11 15:36:32 +000060 (2, 'return')]
Michael W. Hudson53d58bb2002-08-30 13:09:51 +000061
62def no_pop_blocks():
63 while 0:
64 bla
65 x = 1
66
67no_pop_blocks.events = [(0, 'call'),
68 (1, 'line'),
69 (3, 'line'),
70 (3, 'return')]
71
Michael W. Hudson519a3422002-09-11 14:47:51 +000072def called(): # line -3
73 x = 1
74
75def call(): # line 0
76 called()
77
78call.events = [(0, 'call'),
79 (1, 'line'),
80 (-3, 'call'),
81 (-2, 'line'),
82 (-2, 'return'),
83 (1, 'return')]
84
85def raises():
86 raise Exception
87
88def test_raise():
89 try:
90 raises()
91 except Exception, exc:
92 x = 1
93
94test_raise.events = [(0, 'call'),
95 (1, 'line'),
96 (2, 'line'),
97 (-3, 'call'),
98 (-2, 'line'),
99 (-2, 'exception'),
100 (2, 'exception'),
101 (3, 'line'),
102 (4, 'line'),
103 (4, 'return')]
104
105def _settrace_and_return(tracefunc):
106 sys.settrace(tracefunc)
107 sys._getframe().f_back.f_trace = tracefunc
108def settrace_and_return(tracefunc):
109 _settrace_and_return(tracefunc)
Tim Peters3de75262002-11-09 05:26:15 +0000110
Michael W. Hudson519a3422002-09-11 14:47:51 +0000111settrace_and_return.events = [(1, 'return')]
112
113def _settrace_and_raise(tracefunc):
114 sys.settrace(tracefunc)
115 sys._getframe().f_back.f_trace = tracefunc
116 raise RuntimeError
117def settrace_and_raise(tracefunc):
118 try:
119 _settrace_and_raise(tracefunc)
120 except RuntimeError, exc:
121 pass
Tim Peters3de75262002-11-09 05:26:15 +0000122
Michael W. Hudson519a3422002-09-11 14:47:51 +0000123settrace_and_raise.events = [(2, 'exception'),
124 (3, 'line'),
125 (4, 'line'),
126 (4, 'return')]
127
Nicholas Bastinaea94592004-03-22 18:30:42 +0000128# implicit return example
129def ireturn_example():
130 a = 5
131 b = 5
132 if a == b:
133 b = a+1
134 else:
135 pass
136
137ireturn_example.events = [(0, 'call'),
138 (1, 'line'),
139 (2, 'line'),
140 (3, 'line'),
141 (4, 'line'),
142 (4, 'return')]
143
Nicholas Bastinfa7bec72004-03-22 19:21:47 +0000144# Tight loop with while(1) example (SF #765624)
145def tightloop_example():
146 items = range(0, 3)
147 try:
148 i = 0
149 while 1:
Nicholas Bastinee6c9b82004-03-22 19:23:46 +0000150 b = items[i]; i+=1
Nicholas Bastinfa7bec72004-03-22 19:21:47 +0000151 except IndexError:
152 pass
153
154tightloop_example.events = [(0, 'call'),
155 (1, 'line'),
156 (2, 'line'),
157 (3, 'line'),
158 (4, 'line'),
159 (5, 'line'),
160 (5, 'line'),
161 (5, 'line'),
Nicholas Bastinee6c9b82004-03-22 19:23:46 +0000162 (5, 'line'),
Nicholas Bastinfa7bec72004-03-22 19:21:47 +0000163 (5, 'exception'),
164 (6, 'line'),
165 (7, 'line'),
166 (7, 'return')]
167
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000168class Tracer:
169 def __init__(self):
170 self.events = []
171 def trace(self, frame, event, arg):
172 self.events.append((frame.f_lineno, event))
173 return self.trace
174
175class TraceTestCase(unittest.TestCase):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000176 def compare_events(self, line_offset, events, expected_events):
Tim Peters3de75262002-11-09 05:26:15 +0000177 events = [(l - line_offset, e) for (l, e) in events]
Michael W. Hudson519a3422002-09-11 14:47:51 +0000178 if events != expected_events:
179 self.fail(
180 "events did not match expectation:\n" +
181 "\n".join(difflib.ndiff(map(str, expected_events),
182 map(str, events))))
Tim Peters3de75262002-11-09 05:26:15 +0000183
184
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000185 def run_test(self, func):
186 tracer = Tracer()
187 sys.settrace(tracer.trace)
188 func()
189 sys.settrace(None)
Michael W. Hudson519a3422002-09-11 14:47:51 +0000190 self.compare_events(func.func_code.co_firstlineno,
191 tracer.events, func.events)
192
193 def run_test2(self, func):
194 tracer = Tracer()
195 func(tracer.trace)
196 sys.settrace(None)
197 self.compare_events(func.func_code.co_firstlineno,
198 tracer.events, func.events)
Tim Peters3de75262002-11-09 05:26:15 +0000199
Nicholas Bastinaea94592004-03-22 18:30:42 +0000200 def test_01_basic(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000201 self.run_test(basic)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000202 def test_02_arigo(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000203 self.run_test(arigo_example)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000204 def test_03_one_instr(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000205 self.run_test(one_instr_line)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000206 def test_04_no_pop_blocks(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000207 self.run_test(no_pop_blocks)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000208 def test_05_no_pop_tops(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000209 self.run_test(no_pop_tops)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000210 def test_06_call(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000211 self.run_test(call)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000212 def test_07_raise(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000213 self.run_test(test_raise)
214
Nicholas Bastinaea94592004-03-22 18:30:42 +0000215 def test_08_settrace_and_return(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000216 self.run_test2(settrace_and_return)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000217 def test_09_settrace_and_raise(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000218 self.run_test2(settrace_and_raise)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000219 def test_10_ireturn(self):
220 self.run_test(ireturn_example)
Nicholas Bastinfa7bec72004-03-22 19:21:47 +0000221 def test_11_tightloop(self):
222 self.run_test(tightloop_example)
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000223
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000224class RaisingTraceFuncTestCase(unittest.TestCase):
Michael W. Hudson006c7522002-11-08 13:08:46 +0000225 def trace(self, frame, event, arg):
226 """A trace function that raises an exception in response to a
227 specific trace event."""
228 if event == self.raiseOnEvent:
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000229 raise ValueError # just something that isn't RuntimeError
Michael W. Hudson006c7522002-11-08 13:08:46 +0000230 else:
231 return self.trace
Tim Peters3de75262002-11-09 05:26:15 +0000232
Michael W. Hudson006c7522002-11-08 13:08:46 +0000233 def f(self):
234 """The function to trace; raises an exception if that's the case
235 we're testing, so that the 'exception' trace event fires."""
236 if self.raiseOnEvent == 'exception':
237 x = 0
238 y = 1/x
239 else:
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000240 return 1
Tim Peters3de75262002-11-09 05:26:15 +0000241
Michael W. Hudson006c7522002-11-08 13:08:46 +0000242 def run_test_for_event(self, event):
243 """Tests that an exception raised in response to the given event is
244 handled OK."""
245 self.raiseOnEvent = event
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000246 try:
247 for i in xrange(sys.getrecursionlimit() + 1):
Michael W. Hudson006c7522002-11-08 13:08:46 +0000248 sys.settrace(self.trace)
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000249 try:
Michael W. Hudson006c7522002-11-08 13:08:46 +0000250 self.f()
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000251 except ValueError:
252 pass
253 else:
254 self.fail("exception not thrown!")
255 except RuntimeError:
256 self.fail("recursion counter not reset")
Tim Peters3de75262002-11-09 05:26:15 +0000257
Michael W. Hudson006c7522002-11-08 13:08:46 +0000258 # Test the handling of exceptions raised by each kind of trace event.
259 def test_call(self):
260 self.run_test_for_event('call')
261 def test_line(self):
262 self.run_test_for_event('line')
263 def test_return(self):
264 self.run_test_for_event('return')
265 def test_exception(self):
266 self.run_test_for_event('exception')
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000267
Michael W. Hudson58ee2af2003-04-29 16:18:47 +0000268 def test_trash_stack(self):
269 def f():
270 for i in range(5):
271 print i # line tracing will raise an exception at this line
272
273 def g(frame, why, extra):
274 if (why == 'line' and
275 frame.f_lineno == f.func_code.co_firstlineno + 2):
276 raise RuntimeError, "i am crashing"
277 return g
278
279 sys.settrace(g)
280 try:
281 f()
282 except RuntimeError:
283 # the test is really that this doesn't segfault:
284 import gc
285 gc.collect()
286 else:
287 self.fail("exception not propagated")
288
Michael W. Hudsoncfd38842002-12-17 16:15:34 +0000289
290# 'Jump' tests: assigning to frame.f_lineno within a trace function
291# moves the execution position - it's how debuggers implement a Jump
292# command (aka. "Set next statement").
293
294class JumpTracer:
295 """Defines a trace function that jumps from one place to another,
296 with the source and destination lines of the jump being defined by
297 the 'jump' property of the function under test."""
298
299 def __init__(self, function):
300 self.function = function
301 self.jumpFrom = function.jump[0]
302 self.jumpTo = function.jump[1]
303 self.done = False
304
305 def trace(self, frame, event, arg):
306 if not self.done and frame.f_code == self.function.func_code:
307 firstLine = frame.f_code.co_firstlineno
308 if frame.f_lineno == firstLine + self.jumpFrom:
309 # Cope with non-integer self.jumpTo (because of
310 # no_jump_to_non_integers below).
311 try:
312 frame.f_lineno = firstLine + self.jumpTo
313 except TypeError:
314 frame.f_lineno = self.jumpTo
315 self.done = True
316 return self.trace
317
318# The first set of 'jump' tests are for things that are allowed:
319
320def jump_simple_forwards(output):
321 output.append(1)
322 output.append(2)
323 output.append(3)
324
325jump_simple_forwards.jump = (1, 3)
326jump_simple_forwards.output = [3]
327
328def jump_simple_backwards(output):
329 output.append(1)
330 output.append(2)
331
332jump_simple_backwards.jump = (2, 1)
333jump_simple_backwards.output = [1, 1, 2]
334
335def jump_out_of_block_forwards(output):
336 for i in 1, 2:
337 output.append(2)
338 for j in [3]: # Also tests jumping over a block
339 output.append(4)
340 output.append(5)
341
342jump_out_of_block_forwards.jump = (3, 5)
343jump_out_of_block_forwards.output = [2, 5]
344
345def jump_out_of_block_backwards(output):
346 output.append(1)
347 for i in [1]:
348 output.append(3)
349 for j in [2]: # Also tests jumping over a block
350 output.append(5)
351 output.append(6)
352 output.append(7)
353
354jump_out_of_block_backwards.jump = (6, 1)
355jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7]
356
357def jump_to_codeless_line(output):
358 output.append(1)
359 # Jumping to this line should skip to the next one.
360 output.append(3)
361
362jump_to_codeless_line.jump = (1, 2)
363jump_to_codeless_line.output = [3]
364
365def jump_to_same_line(output):
366 output.append(1)
367 output.append(2)
368 output.append(3)
369
370jump_to_same_line.jump = (2, 2)
371jump_to_same_line.output = [1, 2, 3]
372
373# Tests jumping within a finally block, and over one.
374def jump_in_nested_finally(output):
375 try:
376 output.append(2)
377 finally:
378 output.append(4)
379 try:
380 output.append(6)
381 finally:
382 output.append(8)
383 output.append(9)
384
385jump_in_nested_finally.jump = (4, 9)
386jump_in_nested_finally.output = [2, 9]
387
388# The second set of 'jump' tests are for things that are not allowed:
389
390def no_jump_too_far_forwards(output):
391 try:
392 output.append(2)
393 output.append(3)
394 except ValueError, e:
395 output.append('after' in str(e))
396
397no_jump_too_far_forwards.jump = (3, 6)
398no_jump_too_far_forwards.output = [2, True]
399
400def no_jump_too_far_backwards(output):
401 try:
402 output.append(2)
403 output.append(3)
404 except ValueError, e:
405 output.append('before' in str(e))
406
407no_jump_too_far_backwards.jump = (3, -1)
408no_jump_too_far_backwards.output = [2, True]
409
410# Test each kind of 'except' line.
411def no_jump_to_except_1(output):
412 try:
413 output.append(2)
414 except:
415 e = sys.exc_info()[1]
416 output.append('except' in str(e))
417
418no_jump_to_except_1.jump = (2, 3)
419no_jump_to_except_1.output = [True]
420
421def no_jump_to_except_2(output):
422 try:
423 output.append(2)
424 except ValueError:
425 e = sys.exc_info()[1]
426 output.append('except' in str(e))
427
428no_jump_to_except_2.jump = (2, 3)
429no_jump_to_except_2.output = [True]
430
431def no_jump_to_except_3(output):
432 try:
433 output.append(2)
434 except ValueError, e:
435 output.append('except' in str(e))
436
437no_jump_to_except_3.jump = (2, 3)
438no_jump_to_except_3.output = [True]
439
440def no_jump_to_except_4(output):
441 try:
442 output.append(2)
443 except (ValueError, RuntimeError), e:
444 output.append('except' in str(e))
445
446no_jump_to_except_4.jump = (2, 3)
447no_jump_to_except_4.output = [True]
448
449def no_jump_forwards_into_block(output):
450 try:
451 output.append(2)
452 for i in 1, 2:
453 output.append(4)
454 except ValueError, e:
455 output.append('into' in str(e))
456
457no_jump_forwards_into_block.jump = (2, 4)
458no_jump_forwards_into_block.output = [True]
459
460def no_jump_backwards_into_block(output):
461 try:
462 for i in 1, 2:
463 output.append(3)
464 output.append(4)
465 except ValueError, e:
466 output.append('into' in str(e))
467
468no_jump_backwards_into_block.jump = (4, 3)
469no_jump_backwards_into_block.output = [3, 3, True]
470
471def no_jump_into_finally_block(output):
472 try:
473 try:
474 output.append(3)
475 x = 1
476 finally:
477 output.append(6)
478 except ValueError, e:
479 output.append('finally' in str(e))
480
481no_jump_into_finally_block.jump = (4, 6)
482no_jump_into_finally_block.output = [3, 6, True] # The 'finally' still runs
483
484def no_jump_out_of_finally_block(output):
485 try:
486 try:
487 output.append(3)
488 finally:
489 output.append(5)
490 output.append(6)
491 except ValueError, e:
492 output.append('finally' in str(e))
493
494no_jump_out_of_finally_block.jump = (5, 1)
495no_jump_out_of_finally_block.output = [3, True]
496
497# This verifies the line-numbers-must-be-integers rule.
498def no_jump_to_non_integers(output):
499 try:
500 output.append(2)
501 except ValueError, e:
502 output.append('integer' in str(e))
503
504no_jump_to_non_integers.jump = (2, "Spam")
505no_jump_to_non_integers.output = [True]
506
507# This verifies that you can't set f_lineno via _getframe or similar
508# trickery.
509def no_jump_without_trace_function():
510 try:
511 previous_frame = sys._getframe().f_back
512 previous_frame.f_lineno = previous_frame.f_lineno
513 except ValueError, e:
514 # This is the exception we wanted; make sure the error message
515 # talks about trace functions.
516 if 'trace' not in str(e):
517 raise
518 else:
519 # Something's wrong - the expected exception wasn't raised.
520 raise RuntimeError, "Trace-function-less jump failed to fail"
521
522
523class JumpTestCase(unittest.TestCase):
524 def compare_jump_output(self, expected, received):
525 if received != expected:
526 self.fail( "Outputs don't match:\n" +
527 "Expected: " + repr(expected) + "\n" +
528 "Received: " + repr(received))
529
530 def run_test(self, func):
531 tracer = JumpTracer(func)
532 sys.settrace(tracer.trace)
533 output = []
534 func(output)
535 sys.settrace(None)
536 self.compare_jump_output(func.output, output)
537
538 def test_01_jump_simple_forwards(self):
539 self.run_test(jump_simple_forwards)
540 def test_02_jump_simple_backwards(self):
541 self.run_test(jump_simple_backwards)
542 def test_03_jump_out_of_block_forwards(self):
543 self.run_test(jump_out_of_block_forwards)
544 def test_04_jump_out_of_block_backwards(self):
545 self.run_test(jump_out_of_block_backwards)
546 def test_05_jump_to_codeless_line(self):
547 self.run_test(jump_to_codeless_line)
548 def test_06_jump_to_same_line(self):
549 self.run_test(jump_to_same_line)
550 def test_07_jump_in_nested_finally(self):
551 self.run_test(jump_in_nested_finally)
552 def test_08_no_jump_too_far_forwards(self):
553 self.run_test(no_jump_too_far_forwards)
554 def test_09_no_jump_too_far_backwards(self):
555 self.run_test(no_jump_too_far_backwards)
556 def test_10_no_jump_to_except_1(self):
557 self.run_test(no_jump_to_except_1)
558 def test_11_no_jump_to_except_2(self):
559 self.run_test(no_jump_to_except_2)
560 def test_12_no_jump_to_except_3(self):
561 self.run_test(no_jump_to_except_3)
562 def test_13_no_jump_to_except_4(self):
563 self.run_test(no_jump_to_except_4)
564 def test_14_no_jump_forwards_into_block(self):
565 self.run_test(no_jump_forwards_into_block)
566 def test_15_no_jump_backwards_into_block(self):
567 self.run_test(no_jump_backwards_into_block)
568 def test_16_no_jump_into_finally_block(self):
569 self.run_test(no_jump_into_finally_block)
570 def test_17_no_jump_out_of_finally_block(self):
571 self.run_test(no_jump_out_of_finally_block)
572 def test_18_no_jump_to_non_integers(self):
573 self.run_test(no_jump_to_non_integers)
574 def test_19_no_jump_without_trace_function(self):
575 no_jump_without_trace_function()
576
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000577def test_main():
Walter Dörwald21d3a322003-05-01 17:45:56 +0000578 test_support.run_unittest(
579 TraceTestCase,
580 RaisingTraceFuncTestCase,
581 JumpTestCase
582 )
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000583
584if __name__ == "__main__":
585 test_main()