Update version number and release date.
[python/dscho.git] / Lib / test / test_trace.py
blobf973a1939350983c64dd4b8701a8895517801d2c
1 # Testing the line trace facility.
3 from test import test_support
4 import unittest
5 import sys
6 import difflib
8 # A very basic example. If this fails, we're in deep trouble.
9 def basic():
10 return 1
12 basic.events = [(0, 'call'),
13 (1, 'line'),
14 (1, 'return')]
16 # Armin Rigo's failing example:
17 def arigo_example():
18 x = 1
19 del x
20 while 0:
21 pass
22 x = 1
24 arigo_example.events = [(0, 'call'),
25 (1, 'line'),
26 (2, 'line'),
27 (3, 'line'),
28 (5, 'line'),
29 (5, 'return')]
31 # check that lines consisting of just one instruction get traced:
32 def one_instr_line():
33 x = 1
34 del x
35 x = 1
37 one_instr_line.events = [(0, 'call'),
38 (1, 'line'),
39 (2, 'line'),
40 (3, 'line'),
41 (3, 'return')]
43 def 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
51 no_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'),
60 (2, 'return')]
62 def no_pop_blocks():
63 while 0:
64 bla
65 x = 1
67 no_pop_blocks.events = [(0, 'call'),
68 (1, 'line'),
69 (3, 'line'),
70 (3, 'return')]
72 def called(): # line -3
73 x = 1
75 def call(): # line 0
76 called()
78 call.events = [(0, 'call'),
79 (1, 'line'),
80 (-3, 'call'),
81 (-2, 'line'),
82 (-2, 'return'),
83 (1, 'return')]
85 def raises():
86 raise Exception
88 def test_raise():
89 try:
90 raises()
91 except Exception, exc:
92 x = 1
94 test_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')]
105 def _settrace_and_return(tracefunc):
106 sys.settrace(tracefunc)
107 sys._getframe().f_back.f_trace = tracefunc
108 def settrace_and_return(tracefunc):
109 _settrace_and_return(tracefunc)
111 settrace_and_return.events = [(1, 'return')]
113 def _settrace_and_raise(tracefunc):
114 sys.settrace(tracefunc)
115 sys._getframe().f_back.f_trace = tracefunc
116 raise RuntimeError
117 def settrace_and_raise(tracefunc):
118 try:
119 _settrace_and_raise(tracefunc)
120 except RuntimeError, exc:
121 pass
123 settrace_and_raise.events = [(2, 'exception'),
124 (3, 'line'),
125 (4, 'line'),
126 (4, 'return')]
128 class 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
135 class TraceTestCase(unittest.TestCase):
136 def compare_events(self, line_offset, events, expected_events):
137 events = [(l - line_offset, e) for (l, e) in events]
138 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))))
145 def run_test(self, func):
146 tracer = Tracer()
147 sys.settrace(tracer.trace)
148 func()
149 sys.settrace(None)
150 self.compare_events(func.func_code.co_firstlineno,
151 tracer.events, func.events)
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)
160 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)
170 def test_6_call(self):
171 self.run_test(call)
172 def test_7_raise(self):
173 self.run_test(test_raise)
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)
180 class RaisingTraceFuncTestCase(unittest.TestCase):
181 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:
185 raise ValueError # just something that isn't RuntimeError
186 else:
187 return self.trace
189 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:
196 return 1
198 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
202 try:
203 for i in xrange(sys.getrecursionlimit() + 1):
204 sys.settrace(self.trace)
205 try:
206 self.f()
207 except ValueError:
208 pass
209 else:
210 self.fail("exception not thrown!")
211 except RuntimeError:
212 self.fail("recursion counter not reset")
214 # 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')
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").
229 class 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."""
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
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
253 # The first set of 'jump' tests are for things that are allowed:
255 def jump_simple_forwards(output):
256 output.append(1)
257 output.append(2)
258 output.append(3)
260 jump_simple_forwards.jump = (1, 3)
261 jump_simple_forwards.output = [3]
263 def jump_simple_backwards(output):
264 output.append(1)
265 output.append(2)
267 jump_simple_backwards.jump = (2, 1)
268 jump_simple_backwards.output = [1, 1, 2]
270 def 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)
277 jump_out_of_block_forwards.jump = (3, 5)
278 jump_out_of_block_forwards.output = [2, 5]
280 def 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)
289 jump_out_of_block_backwards.jump = (6, 1)
290 jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7]
292 def jump_to_codeless_line(output):
293 output.append(1)
294 # Jumping to this line should skip to the next one.
295 output.append(3)
297 jump_to_codeless_line.jump = (1, 2)
298 jump_to_codeless_line.output = [3]
300 def jump_to_same_line(output):
301 output.append(1)
302 output.append(2)
303 output.append(3)
305 jump_to_same_line.jump = (2, 2)
306 jump_to_same_line.output = [1, 2, 3]
308 # Tests jumping within a finally block, and over one.
309 def 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)
320 jump_in_nested_finally.jump = (4, 9)
321 jump_in_nested_finally.output = [2, 9]
323 # The second set of 'jump' tests are for things that are not allowed:
325 def 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))
332 no_jump_too_far_forwards.jump = (3, 6)
333 no_jump_too_far_forwards.output = [2, True]
335 def 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))
342 no_jump_too_far_backwards.jump = (3, -1)
343 no_jump_too_far_backwards.output = [2, True]
345 # Test each kind of 'except' line.
346 def 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))
353 no_jump_to_except_1.jump = (2, 3)
354 no_jump_to_except_1.output = [True]
356 def 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))
363 no_jump_to_except_2.jump = (2, 3)
364 no_jump_to_except_2.output = [True]
366 def no_jump_to_except_3(output):
367 try:
368 output.append(2)
369 except ValueError, e:
370 output.append('except' in str(e))
372 no_jump_to_except_3.jump = (2, 3)
373 no_jump_to_except_3.output = [True]
375 def no_jump_to_except_4(output):
376 try:
377 output.append(2)
378 except (ValueError, RuntimeError), e:
379 output.append('except' in str(e))
381 no_jump_to_except_4.jump = (2, 3)
382 no_jump_to_except_4.output = [True]
384 def 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))
392 no_jump_forwards_into_block.jump = (2, 4)
393 no_jump_forwards_into_block.output = [True]
395 def 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))
403 no_jump_backwards_into_block.jump = (4, 3)
404 no_jump_backwards_into_block.output = [3, 3, True]
406 def 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))
416 no_jump_into_finally_block.jump = (4, 6)
417 no_jump_into_finally_block.output = [3, 6, True] # The 'finally' still runs
419 def 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))
429 no_jump_out_of_finally_block.jump = (5, 1)
430 no_jump_out_of_finally_block.output = [3, True]
432 # This verifies the line-numbers-must-be-integers rule.
433 def no_jump_to_non_integers(output):
434 try:
435 output.append(2)
436 except ValueError, e:
437 output.append('integer' in str(e))
439 no_jump_to_non_integers.jump = (2, "Spam")
440 no_jump_to_non_integers.output = [True]
442 # This verifies that you can't set f_lineno via _getframe or similar
443 # trickery.
444 def 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"
458 class 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))
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)
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()
512 def test_main():
513 test_support.run_unittest(TraceTestCase)
514 test_support.run_unittest(RaisingTraceFuncTestCase)
515 test_support.run_unittest(JumpTestCase)
517 if __name__ == "__main__":
518 test_main()