blob: f973a1939350983c64dd4b8701a8895517801d2c [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. Hudsoncfd38842002-12-17 16:15:34 +0000224
225# 'Jump' tests: assigning to frame.f_lineno within a trace function
226# moves the execution position - it's how debuggers implement a Jump
227# command (aka. "Set next statement").
228
229class JumpTracer:
230 """Defines a trace function that jumps from one place to another,
231 with the source and destination lines of the jump being defined by
232 the 'jump' property of the function under test."""
233
234 def __init__(self, function):
235 self.function = function
236 self.jumpFrom = function.jump[0]
237 self.jumpTo = function.jump[1]
238 self.done = False
239
240 def trace(self, frame, event, arg):
241 if not self.done and frame.f_code == self.function.func_code:
242 firstLine = frame.f_code.co_firstlineno
243 if frame.f_lineno == firstLine + self.jumpFrom:
244 # Cope with non-integer self.jumpTo (because of
245 # no_jump_to_non_integers below).
246 try:
247 frame.f_lineno = firstLine + self.jumpTo
248 except TypeError:
249 frame.f_lineno = self.jumpTo
250 self.done = True
251 return self.trace
252
253# The first set of 'jump' tests are for things that are allowed:
254
255def jump_simple_forwards(output):
256 output.append(1)
257 output.append(2)
258 output.append(3)
259
260jump_simple_forwards.jump = (1, 3)
261jump_simple_forwards.output = [3]
262
263def jump_simple_backwards(output):
264 output.append(1)
265 output.append(2)
266
267jump_simple_backwards.jump = (2, 1)
268jump_simple_backwards.output = [1, 1, 2]
269
270def jump_out_of_block_forwards(output):
271 for i in 1, 2:
272 output.append(2)
273 for j in [3]: # Also tests jumping over a block
274 output.append(4)
275 output.append(5)
276
277jump_out_of_block_forwards.jump = (3, 5)
278jump_out_of_block_forwards.output = [2, 5]
279
280def jump_out_of_block_backwards(output):
281 output.append(1)
282 for i in [1]:
283 output.append(3)
284 for j in [2]: # Also tests jumping over a block
285 output.append(5)
286 output.append(6)
287 output.append(7)
288
289jump_out_of_block_backwards.jump = (6, 1)
290jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7]
291
292def jump_to_codeless_line(output):
293 output.append(1)
294 # Jumping to this line should skip to the next one.
295 output.append(3)
296
297jump_to_codeless_line.jump = (1, 2)
298jump_to_codeless_line.output = [3]
299
300def jump_to_same_line(output):
301 output.append(1)
302 output.append(2)
303 output.append(3)
304
305jump_to_same_line.jump = (2, 2)
306jump_to_same_line.output = [1, 2, 3]
307
308# Tests jumping within a finally block, and over one.
309def jump_in_nested_finally(output):
310 try:
311 output.append(2)
312 finally:
313 output.append(4)
314 try:
315 output.append(6)
316 finally:
317 output.append(8)
318 output.append(9)
319
320jump_in_nested_finally.jump = (4, 9)
321jump_in_nested_finally.output = [2, 9]
322
323# The second set of 'jump' tests are for things that are not allowed:
324
325def no_jump_too_far_forwards(output):
326 try:
327 output.append(2)
328 output.append(3)
329 except ValueError, e:
330 output.append('after' in str(e))
331
332no_jump_too_far_forwards.jump = (3, 6)
333no_jump_too_far_forwards.output = [2, True]
334
335def no_jump_too_far_backwards(output):
336 try:
337 output.append(2)
338 output.append(3)
339 except ValueError, e:
340 output.append('before' in str(e))
341
342no_jump_too_far_backwards.jump = (3, -1)
343no_jump_too_far_backwards.output = [2, True]
344
345# Test each kind of 'except' line.
346def no_jump_to_except_1(output):
347 try:
348 output.append(2)
349 except:
350 e = sys.exc_info()[1]
351 output.append('except' in str(e))
352
353no_jump_to_except_1.jump = (2, 3)
354no_jump_to_except_1.output = [True]
355
356def no_jump_to_except_2(output):
357 try:
358 output.append(2)
359 except ValueError:
360 e = sys.exc_info()[1]
361 output.append('except' in str(e))
362
363no_jump_to_except_2.jump = (2, 3)
364no_jump_to_except_2.output = [True]
365
366def no_jump_to_except_3(output):
367 try:
368 output.append(2)
369 except ValueError, e:
370 output.append('except' in str(e))
371
372no_jump_to_except_3.jump = (2, 3)
373no_jump_to_except_3.output = [True]
374
375def no_jump_to_except_4(output):
376 try:
377 output.append(2)
378 except (ValueError, RuntimeError), e:
379 output.append('except' in str(e))
380
381no_jump_to_except_4.jump = (2, 3)
382no_jump_to_except_4.output = [True]
383
384def no_jump_forwards_into_block(output):
385 try:
386 output.append(2)
387 for i in 1, 2:
388 output.append(4)
389 except ValueError, e:
390 output.append('into' in str(e))
391
392no_jump_forwards_into_block.jump = (2, 4)
393no_jump_forwards_into_block.output = [True]
394
395def no_jump_backwards_into_block(output):
396 try:
397 for i in 1, 2:
398 output.append(3)
399 output.append(4)
400 except ValueError, e:
401 output.append('into' in str(e))
402
403no_jump_backwards_into_block.jump = (4, 3)
404no_jump_backwards_into_block.output = [3, 3, True]
405
406def no_jump_into_finally_block(output):
407 try:
408 try:
409 output.append(3)
410 x = 1
411 finally:
412 output.append(6)
413 except ValueError, e:
414 output.append('finally' in str(e))
415
416no_jump_into_finally_block.jump = (4, 6)
417no_jump_into_finally_block.output = [3, 6, True] # The 'finally' still runs
418
419def no_jump_out_of_finally_block(output):
420 try:
421 try:
422 output.append(3)
423 finally:
424 output.append(5)
425 output.append(6)
426 except ValueError, e:
427 output.append('finally' in str(e))
428
429no_jump_out_of_finally_block.jump = (5, 1)
430no_jump_out_of_finally_block.output = [3, True]
431
432# This verifies the line-numbers-must-be-integers rule.
433def no_jump_to_non_integers(output):
434 try:
435 output.append(2)
436 except ValueError, e:
437 output.append('integer' in str(e))
438
439no_jump_to_non_integers.jump = (2, "Spam")
440no_jump_to_non_integers.output = [True]
441
442# This verifies that you can't set f_lineno via _getframe or similar
443# trickery.
444def no_jump_without_trace_function():
445 try:
446 previous_frame = sys._getframe().f_back
447 previous_frame.f_lineno = previous_frame.f_lineno
448 except ValueError, e:
449 # This is the exception we wanted; make sure the error message
450 # talks about trace functions.
451 if 'trace' not in str(e):
452 raise
453 else:
454 # Something's wrong - the expected exception wasn't raised.
455 raise RuntimeError, "Trace-function-less jump failed to fail"
456
457
458class JumpTestCase(unittest.TestCase):
459 def compare_jump_output(self, expected, received):
460 if received != expected:
461 self.fail( "Outputs don't match:\n" +
462 "Expected: " + repr(expected) + "\n" +
463 "Received: " + repr(received))
464
465 def run_test(self, func):
466 tracer = JumpTracer(func)
467 sys.settrace(tracer.trace)
468 output = []
469 func(output)
470 sys.settrace(None)
471 self.compare_jump_output(func.output, output)
472
473 def test_01_jump_simple_forwards(self):
474 self.run_test(jump_simple_forwards)
475 def test_02_jump_simple_backwards(self):
476 self.run_test(jump_simple_backwards)
477 def test_03_jump_out_of_block_forwards(self):
478 self.run_test(jump_out_of_block_forwards)
479 def test_04_jump_out_of_block_backwards(self):
480 self.run_test(jump_out_of_block_backwards)
481 def test_05_jump_to_codeless_line(self):
482 self.run_test(jump_to_codeless_line)
483 def test_06_jump_to_same_line(self):
484 self.run_test(jump_to_same_line)
485 def test_07_jump_in_nested_finally(self):
486 self.run_test(jump_in_nested_finally)
487 def test_08_no_jump_too_far_forwards(self):
488 self.run_test(no_jump_too_far_forwards)
489 def test_09_no_jump_too_far_backwards(self):
490 self.run_test(no_jump_too_far_backwards)
491 def test_10_no_jump_to_except_1(self):
492 self.run_test(no_jump_to_except_1)
493 def test_11_no_jump_to_except_2(self):
494 self.run_test(no_jump_to_except_2)
495 def test_12_no_jump_to_except_3(self):
496 self.run_test(no_jump_to_except_3)
497 def test_13_no_jump_to_except_4(self):
498 self.run_test(no_jump_to_except_4)
499 def test_14_no_jump_forwards_into_block(self):
500 self.run_test(no_jump_forwards_into_block)
501 def test_15_no_jump_backwards_into_block(self):
502 self.run_test(no_jump_backwards_into_block)
503 def test_16_no_jump_into_finally_block(self):
504 self.run_test(no_jump_into_finally_block)
505 def test_17_no_jump_out_of_finally_block(self):
506 self.run_test(no_jump_out_of_finally_block)
507 def test_18_no_jump_to_non_integers(self):
508 self.run_test(no_jump_to_non_integers)
509 def test_19_no_jump_without_trace_function(self):
510 no_jump_without_trace_function()
511
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000512def test_main():
513 test_support.run_unittest(TraceTestCase)
Michael W. Hudsonfb4d6ec2002-10-02 13:13:45 +0000514 test_support.run_unittest(RaisingTraceFuncTestCase)
Michael W. Hudsoncfd38842002-12-17 16:15:34 +0000515 test_support.run_unittest(JumpTestCase)
Michael W. Hudson53d58bb2002-08-30 13:09:51 +0000516
517if __name__ == "__main__":
518 test_main()