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