blob: 944ff9ac701925b224263f04930246d1636117d1 [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'),
Barry Warsawe2eca0b2005-08-15 18:14:19 +0000100 (-2, 'return'),
Michael W. Hudson519a3422002-09-11 14:47:51 +0000101 (2, 'exception'),
102 (3, 'line'),
103 (4, 'line'),
104 (4, 'return')]
105
106def _settrace_and_return(tracefunc):
107 sys.settrace(tracefunc)
108 sys._getframe().f_back.f_trace = tracefunc
109def settrace_and_return(tracefunc):
110 _settrace_and_return(tracefunc)
Tim Peters3de75262002-11-09 05:26:15 +0000111
Michael W. Hudson519a3422002-09-11 14:47:51 +0000112settrace_and_return.events = [(1, 'return')]
113
114def _settrace_and_raise(tracefunc):
115 sys.settrace(tracefunc)
116 sys._getframe().f_back.f_trace = tracefunc
117 raise RuntimeError
118def settrace_and_raise(tracefunc):
119 try:
120 _settrace_and_raise(tracefunc)
121 except RuntimeError, exc:
122 pass
Tim Peters3de75262002-11-09 05:26:15 +0000123
Michael W. Hudson519a3422002-09-11 14:47:51 +0000124settrace_and_raise.events = [(2, 'exception'),
125 (3, 'line'),
126 (4, 'line'),
127 (4, 'return')]
128
Nicholas Bastinaea94592004-03-22 18:30:42 +0000129# implicit return example
130def ireturn_example():
131 a = 5
132 b = 5
133 if a == b:
134 b = a+1
135 else:
136 pass
137
138ireturn_example.events = [(0, 'call'),
139 (1, 'line'),
140 (2, 'line'),
141 (3, 'line'),
142 (4, 'line'),
143 (4, 'return')]
144
Nicholas Bastinfa7bec72004-03-22 19:21:47 +0000145# Tight loop with while(1) example (SF #765624)
146def tightloop_example():
147 items = range(0, 3)
148 try:
149 i = 0
150 while 1:
Nicholas Bastinee6c9b82004-03-22 19:23:46 +0000151 b = items[i]; i+=1
Nicholas Bastinfa7bec72004-03-22 19:21:47 +0000152 except IndexError:
153 pass
154
155tightloop_example.events = [(0, 'call'),
156 (1, 'line'),
157 (2, 'line'),
158 (3, 'line'),
159 (4, 'line'),
160 (5, 'line'),
161 (5, 'line'),
162 (5, 'line'),
Nicholas Bastinee6c9b82004-03-22 19:23:46 +0000163 (5, 'line'),
Nicholas Bastinfa7bec72004-03-22 19:21:47 +0000164 (5, 'exception'),
165 (6, 'line'),
166 (7, 'line'),
167 (7, 'return')]
168
Armin Rigo70693382004-03-22 19:30:39 +0000169def tighterloop_example():
170 items = range(1, 4)
171 try:
172 i = 0
173 while 1: i = items[i]
174 except IndexError:
175 pass
176
177tighterloop_example.events = [(0, 'call'),
178 (1, 'line'),
179 (2, 'line'),
180 (3, 'line'),
181 (4, 'line'),
182 (4, 'line'),
183 (4, 'line'),
184 (4, 'line'),
185 (4, 'exception'),
186 (5, 'line'),
187 (6, 'line'),
188 (6, 'return')]
189
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000190class Tracer:
191 def __init__(self):
192 self.events = []
193 def trace(self, frame, event, arg):
194 self.events.append((frame.f_lineno, event))
195 return self.trace
196
197class TraceTestCase(unittest.TestCase):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000198 def compare_events(self, line_offset, events, expected_events):
Tim Peters3de75262002-11-09 05:26:15 +0000199 events = [(l - line_offset, e) for (l, e) in events]
Michael W. Hudson519a3422002-09-11 14:47:51 +0000200 if events != expected_events:
201 self.fail(
202 "events did not match expectation:\n" +
203 "\n".join(difflib.ndiff(map(str, expected_events),
204 map(str, events))))
Tim Peters3de75262002-11-09 05:26:15 +0000205
206
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000207 def run_test(self, func):
208 tracer = Tracer()
209 sys.settrace(tracer.trace)
210 func()
211 sys.settrace(None)
Michael W. Hudson519a3422002-09-11 14:47:51 +0000212 self.compare_events(func.func_code.co_firstlineno,
213 tracer.events, func.events)
214
215 def run_test2(self, func):
216 tracer = Tracer()
217 func(tracer.trace)
218 sys.settrace(None)
219 self.compare_events(func.func_code.co_firstlineno,
220 tracer.events, func.events)
Tim Peters3de75262002-11-09 05:26:15 +0000221
Nicholas Bastinaea94592004-03-22 18:30:42 +0000222 def test_01_basic(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000223 self.run_test(basic)
Jeremy Hyltonbc2a62f2005-10-20 14:27:21 +0000224## XXX: These tests fail with the new ast compiler. They must
225## be fixed before a release.
226## def test_02_arigo(self):
227## self.run_test(arigo_example)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000228 def test_03_one_instr(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000229 self.run_test(one_instr_line)
Jeremy Hyltonbc2a62f2005-10-20 14:27:21 +0000230## def test_04_no_pop_blocks(self):
231## self.run_test(no_pop_blocks)
232## def test_05_no_pop_tops(self):
233## self.run_test(no_pop_tops)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000234 def test_06_call(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000235 self.run_test(call)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000236 def test_07_raise(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000237 self.run_test(test_raise)
238
Nicholas Bastinaea94592004-03-22 18:30:42 +0000239 def test_08_settrace_and_return(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000240 self.run_test2(settrace_and_return)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000241 def test_09_settrace_and_raise(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000242 self.run_test2(settrace_and_raise)
Jeremy Hyltonbc2a62f2005-10-20 14:27:21 +0000243## def test_10_ireturn(self):
244## self.run_test(ireturn_example)
Nicholas Bastinfa7bec72004-03-22 19:21:47 +0000245 def test_11_tightloop(self):
246 self.run_test(tightloop_example)
Armin Rigo70693382004-03-22 19:30:39 +0000247 def test_12_tighterloop(self):
248 self.run_test(tighterloop_example)
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000249
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000250class RaisingTraceFuncTestCase(unittest.TestCase):
Michael W. Hudson006c7522002-11-08 13:08:46 +0000251 def trace(self, frame, event, arg):
252 """A trace function that raises an exception in response to a
253 specific trace event."""
254 if event == self.raiseOnEvent:
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000255 raise ValueError # just something that isn't RuntimeError
Michael W. Hudson006c7522002-11-08 13:08:46 +0000256 else:
257 return self.trace
Tim Peters3de75262002-11-09 05:26:15 +0000258
Michael W. Hudson006c7522002-11-08 13:08:46 +0000259 def f(self):
260 """The function to trace; raises an exception if that's the case
261 we're testing, so that the 'exception' trace event fires."""
262 if self.raiseOnEvent == 'exception':
263 x = 0
264 y = 1/x
265 else:
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000266 return 1
Tim Peters3de75262002-11-09 05:26:15 +0000267
Michael W. Hudson006c7522002-11-08 13:08:46 +0000268 def run_test_for_event(self, event):
269 """Tests that an exception raised in response to the given event is
270 handled OK."""
271 self.raiseOnEvent = event
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000272 try:
273 for i in xrange(sys.getrecursionlimit() + 1):
Michael W. Hudson006c7522002-11-08 13:08:46 +0000274 sys.settrace(self.trace)
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000275 try:
Michael W. Hudson006c7522002-11-08 13:08:46 +0000276 self.f()
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000277 except ValueError:
278 pass
279 else:
280 self.fail("exception not thrown!")
281 except RuntimeError:
282 self.fail("recursion counter not reset")
Tim Peters3de75262002-11-09 05:26:15 +0000283
Michael W. Hudson006c7522002-11-08 13:08:46 +0000284 # Test the handling of exceptions raised by each kind of trace event.
285 def test_call(self):
286 self.run_test_for_event('call')
287 def test_line(self):
288 self.run_test_for_event('line')
289 def test_return(self):
290 self.run_test_for_event('return')
291 def test_exception(self):
292 self.run_test_for_event('exception')
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000293
Michael W. Hudson58ee2af2003-04-29 16:18:47 +0000294 def test_trash_stack(self):
295 def f():
296 for i in range(5):
297 print i # line tracing will raise an exception at this line
298
299 def g(frame, why, extra):
300 if (why == 'line' and
301 frame.f_lineno == f.func_code.co_firstlineno + 2):
302 raise RuntimeError, "i am crashing"
303 return g
304
305 sys.settrace(g)
306 try:
307 f()
308 except RuntimeError:
309 # the test is really that this doesn't segfault:
310 import gc
311 gc.collect()
312 else:
313 self.fail("exception not propagated")
314
Michael W. Hudsoncfd38842002-12-17 16:15:34 +0000315
316# 'Jump' tests: assigning to frame.f_lineno within a trace function
317# moves the execution position - it's how debuggers implement a Jump
318# command (aka. "Set next statement").
319
320class JumpTracer:
321 """Defines a trace function that jumps from one place to another,
322 with the source and destination lines of the jump being defined by
323 the 'jump' property of the function under test."""
324
325 def __init__(self, function):
326 self.function = function
327 self.jumpFrom = function.jump[0]
328 self.jumpTo = function.jump[1]
329 self.done = False
330
331 def trace(self, frame, event, arg):
332 if not self.done and frame.f_code == self.function.func_code:
333 firstLine = frame.f_code.co_firstlineno
334 if frame.f_lineno == firstLine + self.jumpFrom:
335 # Cope with non-integer self.jumpTo (because of
336 # no_jump_to_non_integers below).
337 try:
338 frame.f_lineno = firstLine + self.jumpTo
339 except TypeError:
340 frame.f_lineno = self.jumpTo
341 self.done = True
342 return self.trace
343
344# The first set of 'jump' tests are for things that are allowed:
345
346def jump_simple_forwards(output):
347 output.append(1)
348 output.append(2)
349 output.append(3)
350
351jump_simple_forwards.jump = (1, 3)
352jump_simple_forwards.output = [3]
353
354def jump_simple_backwards(output):
355 output.append(1)
356 output.append(2)
357
358jump_simple_backwards.jump = (2, 1)
359jump_simple_backwards.output = [1, 1, 2]
360
361def jump_out_of_block_forwards(output):
362 for i in 1, 2:
363 output.append(2)
364 for j in [3]: # Also tests jumping over a block
365 output.append(4)
366 output.append(5)
367
368jump_out_of_block_forwards.jump = (3, 5)
369jump_out_of_block_forwards.output = [2, 5]
370
371def jump_out_of_block_backwards(output):
372 output.append(1)
373 for i in [1]:
374 output.append(3)
375 for j in [2]: # Also tests jumping over a block
376 output.append(5)
377 output.append(6)
378 output.append(7)
379
380jump_out_of_block_backwards.jump = (6, 1)
381jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7]
382
383def jump_to_codeless_line(output):
384 output.append(1)
385 # Jumping to this line should skip to the next one.
386 output.append(3)
387
388jump_to_codeless_line.jump = (1, 2)
389jump_to_codeless_line.output = [3]
390
391def jump_to_same_line(output):
392 output.append(1)
393 output.append(2)
394 output.append(3)
395
396jump_to_same_line.jump = (2, 2)
397jump_to_same_line.output = [1, 2, 3]
398
399# Tests jumping within a finally block, and over one.
400def jump_in_nested_finally(output):
401 try:
402 output.append(2)
403 finally:
404 output.append(4)
405 try:
406 output.append(6)
407 finally:
408 output.append(8)
409 output.append(9)
410
411jump_in_nested_finally.jump = (4, 9)
412jump_in_nested_finally.output = [2, 9]
413
414# The second set of 'jump' tests are for things that are not allowed:
415
416def no_jump_too_far_forwards(output):
417 try:
418 output.append(2)
419 output.append(3)
420 except ValueError, e:
421 output.append('after' in str(e))
422
423no_jump_too_far_forwards.jump = (3, 6)
424no_jump_too_far_forwards.output = [2, True]
425
426def no_jump_too_far_backwards(output):
427 try:
428 output.append(2)
429 output.append(3)
430 except ValueError, e:
431 output.append('before' in str(e))
432
433no_jump_too_far_backwards.jump = (3, -1)
434no_jump_too_far_backwards.output = [2, True]
435
436# Test each kind of 'except' line.
437def no_jump_to_except_1(output):
438 try:
439 output.append(2)
440 except:
441 e = sys.exc_info()[1]
442 output.append('except' in str(e))
443
444no_jump_to_except_1.jump = (2, 3)
445no_jump_to_except_1.output = [True]
446
447def no_jump_to_except_2(output):
448 try:
449 output.append(2)
450 except ValueError:
451 e = sys.exc_info()[1]
452 output.append('except' in str(e))
453
454no_jump_to_except_2.jump = (2, 3)
455no_jump_to_except_2.output = [True]
456
457def no_jump_to_except_3(output):
458 try:
459 output.append(2)
460 except ValueError, e:
461 output.append('except' in str(e))
462
463no_jump_to_except_3.jump = (2, 3)
464no_jump_to_except_3.output = [True]
465
466def no_jump_to_except_4(output):
467 try:
468 output.append(2)
469 except (ValueError, RuntimeError), e:
470 output.append('except' in str(e))
471
472no_jump_to_except_4.jump = (2, 3)
473no_jump_to_except_4.output = [True]
474
475def no_jump_forwards_into_block(output):
476 try:
477 output.append(2)
478 for i in 1, 2:
479 output.append(4)
480 except ValueError, e:
481 output.append('into' in str(e))
482
483no_jump_forwards_into_block.jump = (2, 4)
484no_jump_forwards_into_block.output = [True]
485
486def no_jump_backwards_into_block(output):
487 try:
488 for i in 1, 2:
489 output.append(3)
490 output.append(4)
491 except ValueError, e:
492 output.append('into' in str(e))
493
494no_jump_backwards_into_block.jump = (4, 3)
495no_jump_backwards_into_block.output = [3, 3, True]
496
497def no_jump_into_finally_block(output):
498 try:
499 try:
500 output.append(3)
501 x = 1
502 finally:
503 output.append(6)
504 except ValueError, e:
505 output.append('finally' in str(e))
506
507no_jump_into_finally_block.jump = (4, 6)
508no_jump_into_finally_block.output = [3, 6, True] # The 'finally' still runs
509
510def no_jump_out_of_finally_block(output):
511 try:
512 try:
513 output.append(3)
514 finally:
515 output.append(5)
516 output.append(6)
517 except ValueError, e:
518 output.append('finally' in str(e))
519
520no_jump_out_of_finally_block.jump = (5, 1)
521no_jump_out_of_finally_block.output = [3, True]
522
523# This verifies the line-numbers-must-be-integers rule.
524def no_jump_to_non_integers(output):
525 try:
526 output.append(2)
527 except ValueError, e:
528 output.append('integer' in str(e))
529
530no_jump_to_non_integers.jump = (2, "Spam")
531no_jump_to_non_integers.output = [True]
532
533# This verifies that you can't set f_lineno via _getframe or similar
534# trickery.
535def no_jump_without_trace_function():
536 try:
537 previous_frame = sys._getframe().f_back
538 previous_frame.f_lineno = previous_frame.f_lineno
539 except ValueError, e:
540 # This is the exception we wanted; make sure the error message
541 # talks about trace functions.
542 if 'trace' not in str(e):
543 raise
544 else:
545 # Something's wrong - the expected exception wasn't raised.
546 raise RuntimeError, "Trace-function-less jump failed to fail"
547
548
549class JumpTestCase(unittest.TestCase):
550 def compare_jump_output(self, expected, received):
551 if received != expected:
552 self.fail( "Outputs don't match:\n" +
553 "Expected: " + repr(expected) + "\n" +
554 "Received: " + repr(received))
555
556 def run_test(self, func):
557 tracer = JumpTracer(func)
558 sys.settrace(tracer.trace)
559 output = []
560 func(output)
561 sys.settrace(None)
562 self.compare_jump_output(func.output, output)
563
564 def test_01_jump_simple_forwards(self):
565 self.run_test(jump_simple_forwards)
566 def test_02_jump_simple_backwards(self):
567 self.run_test(jump_simple_backwards)
568 def test_03_jump_out_of_block_forwards(self):
569 self.run_test(jump_out_of_block_forwards)
570 def test_04_jump_out_of_block_backwards(self):
571 self.run_test(jump_out_of_block_backwards)
572 def test_05_jump_to_codeless_line(self):
573 self.run_test(jump_to_codeless_line)
574 def test_06_jump_to_same_line(self):
575 self.run_test(jump_to_same_line)
576 def test_07_jump_in_nested_finally(self):
577 self.run_test(jump_in_nested_finally)
578 def test_08_no_jump_too_far_forwards(self):
579 self.run_test(no_jump_too_far_forwards)
580 def test_09_no_jump_too_far_backwards(self):
581 self.run_test(no_jump_too_far_backwards)
Jeremy Hyltonbc2a62f2005-10-20 14:27:21 +0000582# XXX: These tests cause the interpreter to crash. The frame_setlineno()
583# function no longer works correctly because the lineno table generated by
584# the AST compiler is slightly different than with the old compiler.
585# def test_10_no_jump_to_except_1(self):
586# self.run_test(no_jump_to_except_1)
587# def test_11_no_jump_to_except_2(self):
588# self.run_test(no_jump_to_except_2)
589# def test_12_no_jump_to_except_3(self):
590# self.run_test(no_jump_to_except_3)
591# def test_13_no_jump_to_except_4(self):
592# self.run_test(no_jump_to_except_4)
Michael W. Hudsoncfd38842002-12-17 16:15:34 +0000593 def test_14_no_jump_forwards_into_block(self):
594 self.run_test(no_jump_forwards_into_block)
595 def test_15_no_jump_backwards_into_block(self):
596 self.run_test(no_jump_backwards_into_block)
597 def test_16_no_jump_into_finally_block(self):
598 self.run_test(no_jump_into_finally_block)
599 def test_17_no_jump_out_of_finally_block(self):
600 self.run_test(no_jump_out_of_finally_block)
601 def test_18_no_jump_to_non_integers(self):
602 self.run_test(no_jump_to_non_integers)
603 def test_19_no_jump_without_trace_function(self):
604 no_jump_without_trace_function()
605
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000606def test_main():
Walter Dörwald21d3a322003-05-01 17:45:56 +0000607 test_support.run_unittest(
608 TraceTestCase,
609 RaisingTraceFuncTestCase,
610 JumpTestCase
611 )
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000612
613if __name__ == "__main__":
614 test_main()