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