blob: f85c669630216d8b231cca19d4f0e26f4a6dd3df [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
Armin Rigo70693382004-03-22 19:30:39 +0000168def tighterloop_example():
169 items = range(1, 4)
170 try:
171 i = 0
172 while 1: i = items[i]
173 except IndexError:
174 pass
175
176tighterloop_example.events = [(0, 'call'),
177 (1, 'line'),
178 (2, 'line'),
179 (3, 'line'),
180 (4, 'line'),
181 (4, 'line'),
182 (4, 'line'),
183 (4, 'line'),
184 (4, 'exception'),
185 (5, 'line'),
186 (6, 'line'),
187 (6, 'return')]
188
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000189class Tracer:
190 def __init__(self):
191 self.events = []
192 def trace(self, frame, event, arg):
193 self.events.append((frame.f_lineno, event))
194 return self.trace
195
196class TraceTestCase(unittest.TestCase):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000197 def compare_events(self, line_offset, events, expected_events):
Tim Peters3de75262002-11-09 05:26:15 +0000198 events = [(l - line_offset, e) for (l, e) in events]
Michael W. Hudson519a3422002-09-11 14:47:51 +0000199 if events != expected_events:
200 self.fail(
201 "events did not match expectation:\n" +
202 "\n".join(difflib.ndiff(map(str, expected_events),
203 map(str, events))))
Tim Peters3de75262002-11-09 05:26:15 +0000204
205
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000206 def run_test(self, func):
207 tracer = Tracer()
208 sys.settrace(tracer.trace)
209 func()
210 sys.settrace(None)
Michael W. Hudson519a3422002-09-11 14:47:51 +0000211 self.compare_events(func.func_code.co_firstlineno,
212 tracer.events, func.events)
213
214 def run_test2(self, func):
215 tracer = Tracer()
216 func(tracer.trace)
217 sys.settrace(None)
218 self.compare_events(func.func_code.co_firstlineno,
219 tracer.events, func.events)
Tim Peters3de75262002-11-09 05:26:15 +0000220
Nicholas Bastinaea94592004-03-22 18:30:42 +0000221 def test_01_basic(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000222 self.run_test(basic)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000223 def test_02_arigo(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000224 self.run_test(arigo_example)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000225 def test_03_one_instr(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000226 self.run_test(one_instr_line)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000227 def test_04_no_pop_blocks(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000228 self.run_test(no_pop_blocks)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000229 def test_05_no_pop_tops(self):
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000230 self.run_test(no_pop_tops)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000231 def test_06_call(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000232 self.run_test(call)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000233 def test_07_raise(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000234 self.run_test(test_raise)
235
Nicholas Bastinaea94592004-03-22 18:30:42 +0000236 def test_08_settrace_and_return(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000237 self.run_test2(settrace_and_return)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000238 def test_09_settrace_and_raise(self):
Michael W. Hudson519a3422002-09-11 14:47:51 +0000239 self.run_test2(settrace_and_raise)
Nicholas Bastinaea94592004-03-22 18:30:42 +0000240 def test_10_ireturn(self):
241 self.run_test(ireturn_example)
Nicholas Bastinfa7bec72004-03-22 19:21:47 +0000242 def test_11_tightloop(self):
243 self.run_test(tightloop_example)
Armin Rigo70693382004-03-22 19:30:39 +0000244 def test_12_tighterloop(self):
245 self.run_test(tighterloop_example)
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000246
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000247class RaisingTraceFuncTestCase(unittest.TestCase):
Michael W. Hudson006c7522002-11-08 13:08:46 +0000248 def trace(self, frame, event, arg):
249 """A trace function that raises an exception in response to a
250 specific trace event."""
251 if event == self.raiseOnEvent:
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000252 raise ValueError # just something that isn't RuntimeError
Michael W. Hudson006c7522002-11-08 13:08:46 +0000253 else:
254 return self.trace
Tim Peters3de75262002-11-09 05:26:15 +0000255
Michael W. Hudson006c7522002-11-08 13:08:46 +0000256 def f(self):
257 """The function to trace; raises an exception if that's the case
258 we're testing, so that the 'exception' trace event fires."""
259 if self.raiseOnEvent == 'exception':
260 x = 0
261 y = 1/x
262 else:
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000263 return 1
Tim Peters3de75262002-11-09 05:26:15 +0000264
Michael W. Hudson006c7522002-11-08 13:08:46 +0000265 def run_test_for_event(self, event):
266 """Tests that an exception raised in response to the given event is
267 handled OK."""
268 self.raiseOnEvent = event
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000269 try:
270 for i in xrange(sys.getrecursionlimit() + 1):
Michael W. Hudson006c7522002-11-08 13:08:46 +0000271 sys.settrace(self.trace)
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000272 try:
Michael W. Hudson006c7522002-11-08 13:08:46 +0000273 self.f()
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000274 except ValueError:
275 pass
276 else:
277 self.fail("exception not thrown!")
278 except RuntimeError:
279 self.fail("recursion counter not reset")
Tim Peters3de75262002-11-09 05:26:15 +0000280
Michael W. Hudson006c7522002-11-08 13:08:46 +0000281 # Test the handling of exceptions raised by each kind of trace event.
282 def test_call(self):
283 self.run_test_for_event('call')
284 def test_line(self):
285 self.run_test_for_event('line')
286 def test_return(self):
287 self.run_test_for_event('return')
288 def test_exception(self):
289 self.run_test_for_event('exception')
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000290
Michael W. Hudson58ee2af2003-04-29 16:18:47 +0000291 def test_trash_stack(self):
292 def f():
293 for i in range(5):
294 print i # line tracing will raise an exception at this line
295
296 def g(frame, why, extra):
297 if (why == 'line' and
298 frame.f_lineno == f.func_code.co_firstlineno + 2):
299 raise RuntimeError, "i am crashing"
300 return g
301
302 sys.settrace(g)
303 try:
304 f()
305 except RuntimeError:
306 # the test is really that this doesn't segfault:
307 import gc
308 gc.collect()
309 else:
310 self.fail("exception not propagated")
311
Michael W. Hudsoncfd38842002-12-17 16:15:34 +0000312
313# 'Jump' tests: assigning to frame.f_lineno within a trace function
314# moves the execution position - it's how debuggers implement a Jump
315# command (aka. "Set next statement").
316
317class JumpTracer:
318 """Defines a trace function that jumps from one place to another,
319 with the source and destination lines of the jump being defined by
320 the 'jump' property of the function under test."""
321
322 def __init__(self, function):
323 self.function = function
324 self.jumpFrom = function.jump[0]
325 self.jumpTo = function.jump[1]
326 self.done = False
327
328 def trace(self, frame, event, arg):
329 if not self.done and frame.f_code == self.function.func_code:
330 firstLine = frame.f_code.co_firstlineno
331 if frame.f_lineno == firstLine + self.jumpFrom:
332 # Cope with non-integer self.jumpTo (because of
333 # no_jump_to_non_integers below).
334 try:
335 frame.f_lineno = firstLine + self.jumpTo
336 except TypeError:
337 frame.f_lineno = self.jumpTo
338 self.done = True
339 return self.trace
340
341# The first set of 'jump' tests are for things that are allowed:
342
343def jump_simple_forwards(output):
344 output.append(1)
345 output.append(2)
346 output.append(3)
347
348jump_simple_forwards.jump = (1, 3)
349jump_simple_forwards.output = [3]
350
351def jump_simple_backwards(output):
352 output.append(1)
353 output.append(2)
354
355jump_simple_backwards.jump = (2, 1)
356jump_simple_backwards.output = [1, 1, 2]
357
358def jump_out_of_block_forwards(output):
359 for i in 1, 2:
360 output.append(2)
361 for j in [3]: # Also tests jumping over a block
362 output.append(4)
363 output.append(5)
364
365jump_out_of_block_forwards.jump = (3, 5)
366jump_out_of_block_forwards.output = [2, 5]
367
368def jump_out_of_block_backwards(output):
369 output.append(1)
370 for i in [1]:
371 output.append(3)
372 for j in [2]: # Also tests jumping over a block
373 output.append(5)
374 output.append(6)
375 output.append(7)
376
377jump_out_of_block_backwards.jump = (6, 1)
378jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7]
379
380def jump_to_codeless_line(output):
381 output.append(1)
382 # Jumping to this line should skip to the next one.
383 output.append(3)
384
385jump_to_codeless_line.jump = (1, 2)
386jump_to_codeless_line.output = [3]
387
388def jump_to_same_line(output):
389 output.append(1)
390 output.append(2)
391 output.append(3)
392
393jump_to_same_line.jump = (2, 2)
394jump_to_same_line.output = [1, 2, 3]
395
396# Tests jumping within a finally block, and over one.
397def jump_in_nested_finally(output):
398 try:
399 output.append(2)
400 finally:
401 output.append(4)
402 try:
403 output.append(6)
404 finally:
405 output.append(8)
406 output.append(9)
407
408jump_in_nested_finally.jump = (4, 9)
409jump_in_nested_finally.output = [2, 9]
410
411# The second set of 'jump' tests are for things that are not allowed:
412
413def no_jump_too_far_forwards(output):
414 try:
415 output.append(2)
416 output.append(3)
417 except ValueError, e:
418 output.append('after' in str(e))
419
420no_jump_too_far_forwards.jump = (3, 6)
421no_jump_too_far_forwards.output = [2, True]
422
423def no_jump_too_far_backwards(output):
424 try:
425 output.append(2)
426 output.append(3)
427 except ValueError, e:
428 output.append('before' in str(e))
429
430no_jump_too_far_backwards.jump = (3, -1)
431no_jump_too_far_backwards.output = [2, True]
432
433# Test each kind of 'except' line.
434def no_jump_to_except_1(output):
435 try:
436 output.append(2)
437 except:
438 e = sys.exc_info()[1]
439 output.append('except' in str(e))
440
441no_jump_to_except_1.jump = (2, 3)
442no_jump_to_except_1.output = [True]
443
444def no_jump_to_except_2(output):
445 try:
446 output.append(2)
447 except ValueError:
448 e = sys.exc_info()[1]
449 output.append('except' in str(e))
450
451no_jump_to_except_2.jump = (2, 3)
452no_jump_to_except_2.output = [True]
453
454def no_jump_to_except_3(output):
455 try:
456 output.append(2)
457 except ValueError, e:
458 output.append('except' in str(e))
459
460no_jump_to_except_3.jump = (2, 3)
461no_jump_to_except_3.output = [True]
462
463def no_jump_to_except_4(output):
464 try:
465 output.append(2)
466 except (ValueError, RuntimeError), e:
467 output.append('except' in str(e))
468
469no_jump_to_except_4.jump = (2, 3)
470no_jump_to_except_4.output = [True]
471
472def no_jump_forwards_into_block(output):
473 try:
474 output.append(2)
475 for i in 1, 2:
476 output.append(4)
477 except ValueError, e:
478 output.append('into' in str(e))
479
480no_jump_forwards_into_block.jump = (2, 4)
481no_jump_forwards_into_block.output = [True]
482
483def no_jump_backwards_into_block(output):
484 try:
485 for i in 1, 2:
486 output.append(3)
487 output.append(4)
488 except ValueError, e:
489 output.append('into' in str(e))
490
491no_jump_backwards_into_block.jump = (4, 3)
492no_jump_backwards_into_block.output = [3, 3, True]
493
494def no_jump_into_finally_block(output):
495 try:
496 try:
497 output.append(3)
498 x = 1
499 finally:
500 output.append(6)
501 except ValueError, e:
502 output.append('finally' in str(e))
503
504no_jump_into_finally_block.jump = (4, 6)
505no_jump_into_finally_block.output = [3, 6, True] # The 'finally' still runs
506
507def no_jump_out_of_finally_block(output):
508 try:
509 try:
510 output.append(3)
511 finally:
512 output.append(5)
513 output.append(6)
514 except ValueError, e:
515 output.append('finally' in str(e))
516
517no_jump_out_of_finally_block.jump = (5, 1)
518no_jump_out_of_finally_block.output = [3, True]
519
520# This verifies the line-numbers-must-be-integers rule.
521def no_jump_to_non_integers(output):
522 try:
523 output.append(2)
524 except ValueError, e:
525 output.append('integer' in str(e))
526
527no_jump_to_non_integers.jump = (2, "Spam")
528no_jump_to_non_integers.output = [True]
529
530# This verifies that you can't set f_lineno via _getframe or similar
531# trickery.
532def no_jump_without_trace_function():
533 try:
534 previous_frame = sys._getframe().f_back
535 previous_frame.f_lineno = previous_frame.f_lineno
536 except ValueError, e:
537 # This is the exception we wanted; make sure the error message
538 # talks about trace functions.
539 if 'trace' not in str(e):
540 raise
541 else:
542 # Something's wrong - the expected exception wasn't raised.
543 raise RuntimeError, "Trace-function-less jump failed to fail"
544
545
546class JumpTestCase(unittest.TestCase):
547 def compare_jump_output(self, expected, received):
548 if received != expected:
549 self.fail( "Outputs don't match:\n" +
550 "Expected: " + repr(expected) + "\n" +
551 "Received: " + repr(received))
552
553 def run_test(self, func):
554 tracer = JumpTracer(func)
555 sys.settrace(tracer.trace)
556 output = []
557 func(output)
558 sys.settrace(None)
559 self.compare_jump_output(func.output, output)
560
561 def test_01_jump_simple_forwards(self):
562 self.run_test(jump_simple_forwards)
563 def test_02_jump_simple_backwards(self):
564 self.run_test(jump_simple_backwards)
565 def test_03_jump_out_of_block_forwards(self):
566 self.run_test(jump_out_of_block_forwards)
567 def test_04_jump_out_of_block_backwards(self):
568 self.run_test(jump_out_of_block_backwards)
569 def test_05_jump_to_codeless_line(self):
570 self.run_test(jump_to_codeless_line)
571 def test_06_jump_to_same_line(self):
572 self.run_test(jump_to_same_line)
573 def test_07_jump_in_nested_finally(self):
574 self.run_test(jump_in_nested_finally)
575 def test_08_no_jump_too_far_forwards(self):
576 self.run_test(no_jump_too_far_forwards)
577 def test_09_no_jump_too_far_backwards(self):
578 self.run_test(no_jump_too_far_backwards)
579 def test_10_no_jump_to_except_1(self):
580 self.run_test(no_jump_to_except_1)
581 def test_11_no_jump_to_except_2(self):
582 self.run_test(no_jump_to_except_2)
583 def test_12_no_jump_to_except_3(self):
584 self.run_test(no_jump_to_except_3)
585 def test_13_no_jump_to_except_4(self):
586 self.run_test(no_jump_to_except_4)
587 def test_14_no_jump_forwards_into_block(self):
588 self.run_test(no_jump_forwards_into_block)
589 def test_15_no_jump_backwards_into_block(self):
590 self.run_test(no_jump_backwards_into_block)
591 def test_16_no_jump_into_finally_block(self):
592 self.run_test(no_jump_into_finally_block)
593 def test_17_no_jump_out_of_finally_block(self):
594 self.run_test(no_jump_out_of_finally_block)
595 def test_18_no_jump_to_non_integers(self):
596 self.run_test(no_jump_to_non_integers)
597 def test_19_no_jump_without_trace_function(self):
598 no_jump_without_trace_function()
599
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000600def test_main():
Walter Dörwald21d3a322003-05-01 17:45:56 +0000601 test_support.run_unittest(
602 TraceTestCase,
603 RaisingTraceFuncTestCase,
604 JumpTestCase
605 )
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000606
607if __name__ == "__main__":
608 test_main()