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