Changeset 391 for python/trunk/Lib/test/test_trace.py
- Timestamp:
- Mar 19, 2014, 11:31:01 PM (11 years ago)
- Location:
- python/trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
python/trunk
-
Property svn:mergeinfo
set to
/python/vendor/Python-2.7.6 merged eligible /python/vendor/current merged eligible
-
Property svn:mergeinfo
set to
-
python/trunk/Lib/test/test_trace.py
r2 r391 1 # Testing the line trace facility. 2 3 from test import test_support 1 import os 2 import sys 3 from test.test_support import (run_unittest, TESTFN, rmtree, unlink, 4 captured_stdout) 4 5 import unittest 5 import sys 6 import difflib 7 import gc 8 9 # A very basic example. If this fails, we're in deep trouble. 10 def basic(): 11 return 1 12 13 basic.events = [(0, 'call'), 14 (1, 'line'), 15 (1, 'return')] 16 17 # Many of the tests below are tricky because they involve pass statements. 18 # If there is implicit control flow around a pass statement (in an except 19 # clause or else caluse) under what conditions do you set a line number 20 # following that clause? 21 22 23 # The entire "while 0:" statement is optimized away. No code 24 # exists for it, so the line numbers skip directly from "del x" 25 # to "x = 1". 26 def arigo_example(): 27 x = 1 28 del x 29 while 0: 30 pass 31 x = 1 32 33 arigo_example.events = [(0, 'call'), 34 (1, 'line'), 35 (2, 'line'), 36 (5, 'line'), 37 (5, 'return')] 38 39 # check that lines consisting of just one instruction get traced: 40 def one_instr_line(): 41 x = 1 42 del x 43 x = 1 44 45 one_instr_line.events = [(0, 'call'), 46 (1, 'line'), 47 (2, 'line'), 48 (3, 'line'), 49 (3, 'return')] 50 51 def no_pop_tops(): # 0 52 x = 1 # 1 53 for a in range(2): # 2 54 if a: # 3 55 x = 1 # 4 56 else: # 5 57 x = 1 # 6 58 59 no_pop_tops.events = [(0, 'call'), 60 (1, 'line'), 61 (2, 'line'), 62 (3, 'line'), 63 (6, 'line'), 64 (2, 'line'), 65 (3, 'line'), 66 (4, 'line'), 67 (2, 'line'), 68 (2, 'return')] 69 70 def no_pop_blocks(): 71 y = 1 72 while not y: 73 bla 74 x = 1 75 76 no_pop_blocks.events = [(0, 'call'), 77 (1, 'line'), 78 (2, 'line'), 79 (4, 'line'), 80 (4, 'return')] 81 82 def called(): # line -3 83 x = 1 84 85 def call(): # line 0 86 called() 87 88 call.events = [(0, 'call'), 89 (1, 'line'), 90 (-3, 'call'), 91 (-2, 'line'), 92 (-2, 'return'), 93 (1, 'return')] 94 95 def raises(): 96 raise Exception 97 98 def test_raise(): 99 try: 100 raises() 101 except Exception, exc: 102 x = 1 103 104 test_raise.events = [(0, 'call'), 105 (1, 'line'), 106 (2, 'line'), 107 (-3, 'call'), 108 (-2, 'line'), 109 (-2, 'exception'), 110 (-2, 'return'), 111 (2, 'exception'), 112 (3, 'line'), 113 (4, 'line'), 114 (4, 'return')] 115 116 def _settrace_and_return(tracefunc): 117 sys.settrace(tracefunc) 118 sys._getframe().f_back.f_trace = tracefunc 119 def settrace_and_return(tracefunc): 120 _settrace_and_return(tracefunc) 121 122 settrace_and_return.events = [(1, 'return')] 123 124 def _settrace_and_raise(tracefunc): 125 sys.settrace(tracefunc) 126 sys._getframe().f_back.f_trace = tracefunc 127 raise RuntimeError 128 def settrace_and_raise(tracefunc): 129 try: 130 _settrace_and_raise(tracefunc) 131 except RuntimeError, exc: 132 pass 133 134 settrace_and_raise.events = [(2, 'exception'), 135 (3, 'line'), 136 (4, 'line'), 137 (4, 'return')] 138 139 # implicit return example 140 # This test is interesting because of the else: pass 141 # part of the code. The code generate for the true 142 # part of the if contains a jump past the else branch. 143 # The compiler then generates an implicit "return None" 144 # Internally, the compiler visits the pass statement 145 # and stores its line number for use on the next instruction. 146 # The next instruction is the implicit return None. 147 def ireturn_example(): 148 a = 5 149 b = 5 150 if a == b: 151 b = a+1 152 else: 153 pass 154 155 ireturn_example.events = [(0, 'call'), 156 (1, 'line'), 157 (2, 'line'), 158 (3, 'line'), 159 (4, 'line'), 160 (6, 'line'), 161 (6, 'return')] 162 163 # Tight loop with while(1) example (SF #765624) 164 def tightloop_example(): 165 items = range(0, 3) 166 try: 167 i = 0 168 while 1: 169 b = items[i]; i+=1 170 except IndexError: 171 pass 172 173 tightloop_example.events = [(0, 'call'), 174 (1, 'line'), 175 (2, 'line'), 176 (3, 'line'), 177 (4, 'line'), 178 (5, 'line'), 179 (5, 'line'), 180 (5, 'line'), 181 (5, 'line'), 182 (5, 'exception'), 183 (6, 'line'), 184 (7, 'line'), 185 (7, 'return')] 186 187 def tighterloop_example(): 188 items = range(1, 4) 189 try: 190 i = 0 191 while 1: i = items[i] 192 except IndexError: 193 pass 194 195 tighterloop_example.events = [(0, 'call'), 196 (1, 'line'), 197 (2, 'line'), 198 (3, 'line'), 199 (4, 'line'), 200 (4, 'line'), 201 (4, 'line'), 202 (4, 'line'), 203 (4, 'exception'), 204 (5, 'line'), 205 (6, 'line'), 206 (6, 'return')] 207 208 def generator_function(): 209 try: 210 yield True 211 "continued" 212 finally: 213 "finally" 214 def generator_example(): 215 # any() will leave the generator before its end 216 x = any(generator_function()) 217 218 # the following lines were not traced 219 for x in range(10): 220 y = x 221 222 generator_example.events = ([(0, 'call'), 223 (2, 'line'), 224 (-6, 'call'), 225 (-5, 'line'), 226 (-4, 'line'), 227 (-4, 'return'), 228 (-4, 'call'), 229 (-4, 'exception'), 230 (-1, 'line'), 231 (-1, 'return')] + 232 [(5, 'line'), (6, 'line')] * 10 + 233 [(5, 'line'), (5, 'return')]) 234 235 236 class Tracer: 237 def __init__(self): 238 self.events = [] 239 def trace(self, frame, event, arg): 240 self.events.append((frame.f_lineno, event)) 241 return self.trace 242 def traceWithGenexp(self, frame, event, arg): 243 (o for o in [1]) 244 self.events.append((frame.f_lineno, event)) 245 return self.trace 246 247 class TraceTestCase(unittest.TestCase): 248 249 # Disable gc collection when tracing, otherwise the 250 # deallocators may be traced as well. 6 7 import trace 8 from trace import CoverageResults, Trace 9 10 from test.tracedmodules import testmod 11 12 13 #------------------------------- Utilities -----------------------------------# 14 15 def fix_ext_py(filename): 16 """Given a .pyc/.pyo filename converts it to the appropriate .py""" 17 if filename.endswith(('.pyc', '.pyo')): 18 filename = filename[:-1] 19 return filename 20 21 def my_file_and_modname(): 22 """The .py file and module name of this file (__file__)""" 23 modname = os.path.splitext(os.path.basename(__file__))[0] 24 return fix_ext_py(__file__), modname 25 26 def get_firstlineno(func): 27 return func.__code__.co_firstlineno 28 29 #-------------------- Target functions for tracing ---------------------------# 30 # 31 # The relative line numbers of lines in these functions matter for verifying 32 # tracing. Please modify the appropriate tests if you change one of the 33 # functions. Absolute line numbers don't matter. 34 # 35 36 def traced_func_linear(x, y): 37 a = x 38 b = y 39 c = a + b 40 return c 41 42 def traced_func_loop(x, y): 43 c = x 44 for i in range(5): 45 c += y 46 return c 47 48 def traced_func_importing(x, y): 49 return x + y + testmod.func(1) 50 51 def traced_func_simple_caller(x): 52 c = traced_func_linear(x, x) 53 return c + x 54 55 def traced_func_importing_caller(x): 56 k = traced_func_simple_caller(x) 57 k += traced_func_importing(k, x) 58 return k 59 60 def traced_func_generator(num): 61 c = 5 # executed once 62 for i in range(num): 63 yield i + c 64 65 def traced_func_calling_generator(): 66 k = 0 67 for i in traced_func_generator(10): 68 k += i 69 70 def traced_doubler(num): 71 return num * 2 72 73 def traced_caller_list_comprehension(): 74 k = 10 75 mylist = [traced_doubler(i) for i in range(k)] 76 return mylist 77 78 79 class TracedClass(object): 80 def __init__(self, x): 81 self.a = x 82 83 def inst_method_linear(self, y): 84 return self.a + y 85 86 def inst_method_calling(self, x): 87 c = self.inst_method_linear(x) 88 return c + traced_func_linear(x, c) 89 90 @classmethod 91 def class_method_linear(cls, y): 92 return y * 2 93 94 @staticmethod 95 def static_method_linear(y): 96 return y * 2 97 98 99 #------------------------------ Test cases -----------------------------------# 100 101 102 class TestLineCounts(unittest.TestCase): 103 """White-box testing of line-counting, via runfunc""" 251 104 def setUp(self): 252 self.using_gc = gc.isenabled() 253 gc.disable() 254 105 self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) 106 self.my_py_filename = fix_ext_py(__file__) 107 108 def test_traced_func_linear(self): 109 result = self.tracer.runfunc(traced_func_linear, 2, 5) 110 self.assertEqual(result, 7) 111 112 # all lines are executed once 113 expected = {} 114 firstlineno = get_firstlineno(traced_func_linear) 115 for i in range(1, 5): 116 expected[(self.my_py_filename, firstlineno + i)] = 1 117 118 self.assertEqual(self.tracer.results().counts, expected) 119 120 def test_traced_func_loop(self): 121 self.tracer.runfunc(traced_func_loop, 2, 3) 122 123 firstlineno = get_firstlineno(traced_func_loop) 124 expected = { 125 (self.my_py_filename, firstlineno + 1): 1, 126 (self.my_py_filename, firstlineno + 2): 6, 127 (self.my_py_filename, firstlineno + 3): 5, 128 (self.my_py_filename, firstlineno + 4): 1, 129 } 130 self.assertEqual(self.tracer.results().counts, expected) 131 132 def test_traced_func_importing(self): 133 self.tracer.runfunc(traced_func_importing, 2, 5) 134 135 firstlineno = get_firstlineno(traced_func_importing) 136 expected = { 137 (self.my_py_filename, firstlineno + 1): 1, 138 (fix_ext_py(testmod.__file__), 2): 1, 139 (fix_ext_py(testmod.__file__), 3): 1, 140 } 141 142 self.assertEqual(self.tracer.results().counts, expected) 143 144 def test_trace_func_generator(self): 145 self.tracer.runfunc(traced_func_calling_generator) 146 147 firstlineno_calling = get_firstlineno(traced_func_calling_generator) 148 firstlineno_gen = get_firstlineno(traced_func_generator) 149 expected = { 150 (self.my_py_filename, firstlineno_calling + 1): 1, 151 (self.my_py_filename, firstlineno_calling + 2): 11, 152 (self.my_py_filename, firstlineno_calling + 3): 10, 153 (self.my_py_filename, firstlineno_gen + 1): 1, 154 (self.my_py_filename, firstlineno_gen + 2): 11, 155 (self.my_py_filename, firstlineno_gen + 3): 10, 156 } 157 self.assertEqual(self.tracer.results().counts, expected) 158 159 def test_trace_list_comprehension(self): 160 self.tracer.runfunc(traced_caller_list_comprehension) 161 162 firstlineno_calling = get_firstlineno(traced_caller_list_comprehension) 163 firstlineno_called = get_firstlineno(traced_doubler) 164 expected = { 165 (self.my_py_filename, firstlineno_calling + 1): 1, 166 (self.my_py_filename, firstlineno_calling + 2): 11, 167 (self.my_py_filename, firstlineno_calling + 3): 1, 168 (self.my_py_filename, firstlineno_called + 1): 10, 169 } 170 self.assertEqual(self.tracer.results().counts, expected) 171 172 173 def test_linear_methods(self): 174 # XXX todo: later add 'static_method_linear' and 'class_method_linear' 175 # here, once issue1764286 is resolved 176 # 177 for methname in ['inst_method_linear',]: 178 tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) 179 traced_obj = TracedClass(25) 180 method = getattr(traced_obj, methname) 181 tracer.runfunc(method, 20) 182 183 firstlineno = get_firstlineno(method) 184 expected = { 185 (self.my_py_filename, firstlineno + 1): 1, 186 } 187 self.assertEqual(tracer.results().counts, expected) 188 189 class TestRunExecCounts(unittest.TestCase): 190 """A simple sanity test of line-counting, via runctx (exec)""" 191 def setUp(self): 192 self.my_py_filename = fix_ext_py(__file__) 193 194 def test_exec_counts(self): 195 self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0) 196 code = r'''traced_func_loop(2, 5)''' 197 code = compile(code, __file__, 'exec') 198 self.tracer.runctx(code, globals(), vars()) 199 200 firstlineno = get_firstlineno(traced_func_loop) 201 expected = { 202 (self.my_py_filename, firstlineno + 1): 1, 203 (self.my_py_filename, firstlineno + 2): 6, 204 (self.my_py_filename, firstlineno + 3): 5, 205 (self.my_py_filename, firstlineno + 4): 1, 206 } 207 208 # When used through 'run', some other spurious counts are produced, like 209 # the settrace of threading, which we ignore, just making sure that the 210 # counts fo traced_func_loop were right. 211 # 212 for k in expected.keys(): 213 self.assertEqual(self.tracer.results().counts[k], expected[k]) 214 215 216 class TestFuncs(unittest.TestCase): 217 """White-box testing of funcs tracing""" 218 def setUp(self): 219 self.tracer = Trace(count=0, trace=0, countfuncs=1) 220 self.filemod = my_file_and_modname() 221 222 def test_simple_caller(self): 223 self.tracer.runfunc(traced_func_simple_caller, 1) 224 225 expected = { 226 self.filemod + ('traced_func_simple_caller',): 1, 227 self.filemod + ('traced_func_linear',): 1, 228 } 229 self.assertEqual(self.tracer.results().calledfuncs, expected) 230 231 def test_loop_caller_importing(self): 232 self.tracer.runfunc(traced_func_importing_caller, 1) 233 234 expected = { 235 self.filemod + ('traced_func_simple_caller',): 1, 236 self.filemod + ('traced_func_linear',): 1, 237 self.filemod + ('traced_func_importing_caller',): 1, 238 self.filemod + ('traced_func_importing',): 1, 239 (fix_ext_py(testmod.__file__), 'testmod', 'func'): 1, 240 } 241 self.assertEqual(self.tracer.results().calledfuncs, expected) 242 243 def test_inst_method_calling(self): 244 obj = TracedClass(20) 245 self.tracer.runfunc(obj.inst_method_calling, 1) 246 247 expected = { 248 self.filemod + ('TracedClass.inst_method_calling',): 1, 249 self.filemod + ('TracedClass.inst_method_linear',): 1, 250 self.filemod + ('traced_func_linear',): 1, 251 } 252 self.assertEqual(self.tracer.results().calledfuncs, expected) 253 254 255 class TestCallers(unittest.TestCase): 256 """White-box testing of callers tracing""" 257 def setUp(self): 258 self.tracer = Trace(count=0, trace=0, countcallers=1) 259 self.filemod = my_file_and_modname() 260 261 def test_loop_caller_importing(self): 262 self.tracer.runfunc(traced_func_importing_caller, 1) 263 264 expected = { 265 ((os.path.splitext(trace.__file__)[0] + '.py', 'trace', 'Trace.runfunc'), 266 (self.filemod + ('traced_func_importing_caller',))): 1, 267 ((self.filemod + ('traced_func_simple_caller',)), 268 (self.filemod + ('traced_func_linear',))): 1, 269 ((self.filemod + ('traced_func_importing_caller',)), 270 (self.filemod + ('traced_func_simple_caller',))): 1, 271 ((self.filemod + ('traced_func_importing_caller',)), 272 (self.filemod + ('traced_func_importing',))): 1, 273 ((self.filemod + ('traced_func_importing',)), 274 (fix_ext_py(testmod.__file__), 'testmod', 'func')): 1, 275 } 276 self.assertEqual(self.tracer.results().callers, expected) 277 278 279 # Created separately for issue #3821 280 class TestCoverage(unittest.TestCase): 255 281 def tearDown(self): 256 if self.using_gc: 257 gc.enable() 258 259 def compare_events(self, line_offset, events, expected_events): 260 events = [(l - line_offset, e) for (l, e) in events] 261 if events != expected_events: 262 self.fail( 263 "events did not match expectation:\n" + 264 "\n".join(difflib.ndiff([str(x) for x in expected_events], 265 [str(x) for x in events]))) 266 267 def run_and_compare(self, func, events): 268 tracer = Tracer() 269 sys.settrace(tracer.trace) 270 func() 271 sys.settrace(None) 272 self.compare_events(func.func_code.co_firstlineno, 273 tracer.events, events) 274 275 def run_test(self, func): 276 self.run_and_compare(func, func.events) 277 278 def run_test2(self, func): 279 tracer = Tracer() 280 func(tracer.trace) 281 sys.settrace(None) 282 self.compare_events(func.func_code.co_firstlineno, 283 tracer.events, func.events) 284 285 def set_and_retrieve_none(self): 286 sys.settrace(None) 287 assert sys.gettrace() is None 288 289 def set_and_retrieve_func(self): 290 def fn(*args): 291 pass 292 293 sys.settrace(fn) 294 try: 295 assert sys.gettrace() is fn 296 finally: 297 sys.settrace(None) 298 299 def test_01_basic(self): 300 self.run_test(basic) 301 def test_02_arigo(self): 302 self.run_test(arigo_example) 303 def test_03_one_instr(self): 304 self.run_test(one_instr_line) 305 def test_04_no_pop_blocks(self): 306 self.run_test(no_pop_blocks) 307 def test_05_no_pop_tops(self): 308 self.run_test(no_pop_tops) 309 def test_06_call(self): 310 self.run_test(call) 311 def test_07_raise(self): 312 self.run_test(test_raise) 313 314 def test_08_settrace_and_return(self): 315 self.run_test2(settrace_and_return) 316 def test_09_settrace_and_raise(self): 317 self.run_test2(settrace_and_raise) 318 def test_10_ireturn(self): 319 self.run_test(ireturn_example) 320 def test_11_tightloop(self): 321 self.run_test(tightloop_example) 322 def test_12_tighterloop(self): 323 self.run_test(tighterloop_example) 324 325 def test_13_genexp(self): 326 self.run_test(generator_example) 327 # issue1265: if the trace function contains a generator, 328 # and if the traced function contains another generator 329 # that is not completely exhausted, the trace stopped. 330 # Worse: the 'finally' clause was not invoked. 331 tracer = Tracer() 332 sys.settrace(tracer.traceWithGenexp) 333 generator_example() 334 sys.settrace(None) 335 self.compare_events(generator_example.__code__.co_firstlineno, 336 tracer.events, generator_example.events) 337 338 def test_14_onliner_if(self): 339 def onliners(): 340 if True: False 341 else: True 342 return 0 343 self.run_and_compare( 344 onliners, 345 [(0, 'call'), 346 (1, 'line'), 347 (3, 'line'), 348 (3, 'return')]) 349 350 def test_15_loops(self): 351 # issue1750076: "while" expression is skipped by debugger 352 def for_example(): 353 for x in range(2): 354 pass 355 self.run_and_compare( 356 for_example, 357 [(0, 'call'), 358 (1, 'line'), 359 (2, 'line'), 360 (1, 'line'), 361 (2, 'line'), 362 (1, 'line'), 363 (1, 'return')]) 364 365 def while_example(): 366 # While expression should be traced on every loop 367 x = 2 368 while x > 0: 369 x -= 1 370 self.run_and_compare( 371 while_example, 372 [(0, 'call'), 373 (2, 'line'), 374 (3, 'line'), 375 (4, 'line'), 376 (3, 'line'), 377 (4, 'line'), 378 (3, 'line'), 379 (3, 'return')]) 380 381 def test_16_blank_lines(self): 382 exec("def f():\n" + "\n" * 256 + " pass") 383 self.run_and_compare( 384 f, 385 [(0, 'call'), 386 (257, 'line'), 387 (257, 'return')]) 388 389 390 class RaisingTraceFuncTestCase(unittest.TestCase): 391 def trace(self, frame, event, arg): 392 """A trace function that raises an exception in response to a 393 specific trace event.""" 394 if event == self.raiseOnEvent: 395 raise ValueError # just something that isn't RuntimeError 396 else: 397 return self.trace 398 399 def f(self): 400 """The function to trace; raises an exception if that's the case 401 we're testing, so that the 'exception' trace event fires.""" 402 if self.raiseOnEvent == 'exception': 403 x = 0 404 y = 1/x 405 else: 406 return 1 407 408 def run_test_for_event(self, event): 409 """Tests that an exception raised in response to the given event is 410 handled OK.""" 411 self.raiseOnEvent = event 412 try: 413 for i in xrange(sys.getrecursionlimit() + 1): 414 sys.settrace(self.trace) 415 try: 416 self.f() 417 except ValueError: 418 pass 419 else: 420 self.fail("exception not thrown!") 421 except RuntimeError: 422 self.fail("recursion counter not reset") 423 424 # Test the handling of exceptions raised by each kind of trace event. 425 def test_call(self): 426 self.run_test_for_event('call') 427 def test_line(self): 428 self.run_test_for_event('line') 429 def test_return(self): 430 self.run_test_for_event('return') 431 def test_exception(self): 432 self.run_test_for_event('exception') 433 434 def test_trash_stack(self): 435 def f(): 436 for i in range(5): 437 print i # line tracing will raise an exception at this line 438 439 def g(frame, why, extra): 440 if (why == 'line' and 441 frame.f_lineno == f.func_code.co_firstlineno + 2): 442 raise RuntimeError, "i am crashing" 443 return g 444 445 sys.settrace(g) 446 try: 447 f() 448 except RuntimeError: 449 # the test is really that this doesn't segfault: 450 import gc 451 gc.collect() 452 else: 453 self.fail("exception not propagated") 454 455 456 # 'Jump' tests: assigning to frame.f_lineno within a trace function 457 # moves the execution position - it's how debuggers implement a Jump 458 # command (aka. "Set next statement"). 459 460 class JumpTracer: 461 """Defines a trace function that jumps from one place to another, 462 with the source and destination lines of the jump being defined by 463 the 'jump' property of the function under test.""" 464 465 def __init__(self, function): 466 self.function = function 467 self.jumpFrom = function.jump[0] 468 self.jumpTo = function.jump[1] 469 self.done = False 470 471 def trace(self, frame, event, arg): 472 if not self.done and frame.f_code == self.function.func_code: 473 firstLine = frame.f_code.co_firstlineno 474 if frame.f_lineno == firstLine + self.jumpFrom: 475 # Cope with non-integer self.jumpTo (because of 476 # no_jump_to_non_integers below). 477 try: 478 frame.f_lineno = firstLine + self.jumpTo 479 except TypeError: 480 frame.f_lineno = self.jumpTo 481 self.done = True 482 return self.trace 483 484 # The first set of 'jump' tests are for things that are allowed: 485 486 def jump_simple_forwards(output): 487 output.append(1) 488 output.append(2) 489 output.append(3) 490 491 jump_simple_forwards.jump = (1, 3) 492 jump_simple_forwards.output = [3] 493 494 def jump_simple_backwards(output): 495 output.append(1) 496 output.append(2) 497 498 jump_simple_backwards.jump = (2, 1) 499 jump_simple_backwards.output = [1, 1, 2] 500 501 def jump_out_of_block_forwards(output): 502 for i in 1, 2: 503 output.append(2) 504 for j in [3]: # Also tests jumping over a block 505 output.append(4) 506 output.append(5) 507 508 jump_out_of_block_forwards.jump = (3, 5) 509 jump_out_of_block_forwards.output = [2, 5] 510 511 def jump_out_of_block_backwards(output): 512 output.append(1) 513 for i in [1]: 514 output.append(3) 515 for j in [2]: # Also tests jumping over a block 516 output.append(5) 517 output.append(6) 518 output.append(7) 519 520 jump_out_of_block_backwards.jump = (6, 1) 521 jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7] 522 523 def jump_to_codeless_line(output): 524 output.append(1) 525 # Jumping to this line should skip to the next one. 526 output.append(3) 527 528 jump_to_codeless_line.jump = (1, 2) 529 jump_to_codeless_line.output = [3] 530 531 def jump_to_same_line(output): 532 output.append(1) 533 output.append(2) 534 output.append(3) 535 536 jump_to_same_line.jump = (2, 2) 537 jump_to_same_line.output = [1, 2, 3] 538 539 # Tests jumping within a finally block, and over one. 540 def jump_in_nested_finally(output): 541 try: 542 output.append(2) 543 finally: 544 output.append(4) 545 try: 546 output.append(6) 547 finally: 548 output.append(8) 549 output.append(9) 550 551 jump_in_nested_finally.jump = (4, 9) 552 jump_in_nested_finally.output = [2, 9] 553 554 # The second set of 'jump' tests are for things that are not allowed: 555 556 def no_jump_too_far_forwards(output): 557 try: 558 output.append(2) 559 output.append(3) 560 except ValueError, e: 561 output.append('after' in str(e)) 562 563 no_jump_too_far_forwards.jump = (3, 6) 564 no_jump_too_far_forwards.output = [2, True] 565 566 def no_jump_too_far_backwards(output): 567 try: 568 output.append(2) 569 output.append(3) 570 except ValueError, e: 571 output.append('before' in str(e)) 572 573 no_jump_too_far_backwards.jump = (3, -1) 574 no_jump_too_far_backwards.output = [2, True] 575 576 # Test each kind of 'except' line. 577 def no_jump_to_except_1(output): 578 try: 579 output.append(2) 580 except: 581 e = sys.exc_info()[1] 582 output.append('except' in str(e)) 583 584 no_jump_to_except_1.jump = (2, 3) 585 no_jump_to_except_1.output = [True] 586 587 def no_jump_to_except_2(output): 588 try: 589 output.append(2) 590 except ValueError: 591 e = sys.exc_info()[1] 592 output.append('except' in str(e)) 593 594 no_jump_to_except_2.jump = (2, 3) 595 no_jump_to_except_2.output = [True] 596 597 def no_jump_to_except_3(output): 598 try: 599 output.append(2) 600 except ValueError, e: 601 output.append('except' in str(e)) 602 603 no_jump_to_except_3.jump = (2, 3) 604 no_jump_to_except_3.output = [True] 605 606 def no_jump_to_except_4(output): 607 try: 608 output.append(2) 609 except (ValueError, RuntimeError), e: 610 output.append('except' in str(e)) 611 612 no_jump_to_except_4.jump = (2, 3) 613 no_jump_to_except_4.output = [True] 614 615 def no_jump_forwards_into_block(output): 616 try: 617 output.append(2) 618 for i in 1, 2: 619 output.append(4) 620 except ValueError, e: 621 output.append('into' in str(e)) 622 623 no_jump_forwards_into_block.jump = (2, 4) 624 no_jump_forwards_into_block.output = [True] 625 626 def no_jump_backwards_into_block(output): 627 try: 628 for i in 1, 2: 629 output.append(3) 630 output.append(4) 631 except ValueError, e: 632 output.append('into' in str(e)) 633 634 no_jump_backwards_into_block.jump = (4, 3) 635 no_jump_backwards_into_block.output = [3, 3, True] 636 637 def no_jump_into_finally_block(output): 638 try: 639 try: 640 output.append(3) 641 x = 1 642 finally: 643 output.append(6) 644 except ValueError, e: 645 output.append('finally' in str(e)) 646 647 no_jump_into_finally_block.jump = (4, 6) 648 no_jump_into_finally_block.output = [3, 6, True] # The 'finally' still runs 649 650 def no_jump_out_of_finally_block(output): 651 try: 652 try: 653 output.append(3) 654 finally: 655 output.append(5) 656 output.append(6) 657 except ValueError, e: 658 output.append('finally' in str(e)) 659 660 no_jump_out_of_finally_block.jump = (5, 1) 661 no_jump_out_of_finally_block.output = [3, True] 662 663 # This verifies the line-numbers-must-be-integers rule. 664 def no_jump_to_non_integers(output): 665 try: 666 output.append(2) 667 except ValueError, e: 668 output.append('integer' in str(e)) 669 670 no_jump_to_non_integers.jump = (2, "Spam") 671 no_jump_to_non_integers.output = [True] 672 673 # This verifies that you can't set f_lineno via _getframe or similar 674 # trickery. 675 def no_jump_without_trace_function(): 676 try: 677 previous_frame = sys._getframe().f_back 678 previous_frame.f_lineno = previous_frame.f_lineno 679 except ValueError, e: 680 # This is the exception we wanted; make sure the error message 681 # talks about trace functions. 682 if 'trace' not in str(e): 683 raise 684 else: 685 # Something's wrong - the expected exception wasn't raised. 686 raise RuntimeError, "Trace-function-less jump failed to fail" 687 688 689 class JumpTestCase(unittest.TestCase): 690 def compare_jump_output(self, expected, received): 691 if received != expected: 692 self.fail( "Outputs don't match:\n" + 693 "Expected: " + repr(expected) + "\n" + 694 "Received: " + repr(received)) 695 696 def run_test(self, func): 697 tracer = JumpTracer(func) 698 sys.settrace(tracer.trace) 699 output = [] 700 func(output) 701 sys.settrace(None) 702 self.compare_jump_output(func.output, output) 703 704 def test_01_jump_simple_forwards(self): 705 self.run_test(jump_simple_forwards) 706 def test_02_jump_simple_backwards(self): 707 self.run_test(jump_simple_backwards) 708 def test_03_jump_out_of_block_forwards(self): 709 self.run_test(jump_out_of_block_forwards) 710 def test_04_jump_out_of_block_backwards(self): 711 self.run_test(jump_out_of_block_backwards) 712 def test_05_jump_to_codeless_line(self): 713 self.run_test(jump_to_codeless_line) 714 def test_06_jump_to_same_line(self): 715 self.run_test(jump_to_same_line) 716 def test_07_jump_in_nested_finally(self): 717 self.run_test(jump_in_nested_finally) 718 def test_08_no_jump_too_far_forwards(self): 719 self.run_test(no_jump_too_far_forwards) 720 def test_09_no_jump_too_far_backwards(self): 721 self.run_test(no_jump_too_far_backwards) 722 def test_10_no_jump_to_except_1(self): 723 self.run_test(no_jump_to_except_1) 724 def test_11_no_jump_to_except_2(self): 725 self.run_test(no_jump_to_except_2) 726 def test_12_no_jump_to_except_3(self): 727 self.run_test(no_jump_to_except_3) 728 def test_13_no_jump_to_except_4(self): 729 self.run_test(no_jump_to_except_4) 730 def test_14_no_jump_forwards_into_block(self): 731 self.run_test(no_jump_forwards_into_block) 732 def test_15_no_jump_backwards_into_block(self): 733 self.run_test(no_jump_backwards_into_block) 734 def test_16_no_jump_into_finally_block(self): 735 self.run_test(no_jump_into_finally_block) 736 def test_17_no_jump_out_of_finally_block(self): 737 self.run_test(no_jump_out_of_finally_block) 738 def test_18_no_jump_to_non_integers(self): 739 self.run_test(no_jump_to_non_integers) 740 def test_19_no_jump_without_trace_function(self): 741 no_jump_without_trace_function() 742 743 def test_20_large_function(self): 744 d = {} 745 exec("""def f(output): # line 0 746 x = 0 # line 1 747 y = 1 # line 2 748 ''' # line 3 749 %s # lines 4-1004 750 ''' # line 1005 751 x += 1 # line 1006 752 output.append(x) # line 1007 753 return""" % ('\n' * 1000,), d) 754 f = d['f'] 755 756 f.jump = (2, 1007) 757 f.output = [0] 758 self.run_test(f) 282 rmtree(TESTFN) 283 unlink(TESTFN) 284 285 def _coverage(self, tracer, 286 cmd='from test import test_pprint; test_pprint.test_main()'): 287 tracer.run(cmd) 288 r = tracer.results() 289 r.write_results(show_missing=True, summary=True, coverdir=TESTFN) 290 291 def test_coverage(self): 292 tracer = trace.Trace(trace=0, count=1) 293 with captured_stdout() as stdout: 294 self._coverage(tracer) 295 stdout = stdout.getvalue() 296 self.assertTrue("pprint.py" in stdout) 297 self.assertTrue("case.py" in stdout) # from unittest 298 files = os.listdir(TESTFN) 299 self.assertTrue("pprint.cover" in files) 300 self.assertTrue("unittest.case.cover" in files) 301 302 def test_coverage_ignore(self): 303 # Ignore all files, nothing should be traced nor printed 304 libpath = os.path.normpath(os.path.dirname(os.__file__)) 305 # sys.prefix does not work when running from a checkout 306 tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix, libpath], 307 trace=0, count=1) 308 with captured_stdout() as stdout: 309 self._coverage(tracer) 310 if os.path.exists(TESTFN): 311 files = os.listdir(TESTFN) 312 self.assertEqual(files, []) 313 314 def test_issue9936(self): 315 tracer = trace.Trace(trace=0, count=1) 316 modname = 'test.tracedmodules.testmod' 317 # Ensure that the module is executed in import 318 if modname in sys.modules: 319 del sys.modules[modname] 320 cmd = ("import test.tracedmodules.testmod as t;" 321 "t.func(0); t.func2();") 322 with captured_stdout() as stdout: 323 self._coverage(tracer, cmd) 324 stdout.seek(0) 325 stdout.readline() 326 coverage = {} 327 for line in stdout: 328 lines, cov, module = line.split()[:3] 329 coverage[module] = (int(lines), int(cov[:-1])) 330 # XXX This is needed to run regrtest.py as a script 331 modname = trace.fullmodname(sys.modules[modname].__file__) 332 self.assertIn(modname, coverage) 333 self.assertEqual(coverage[modname], (5, 100)) 334 759 335 760 336 def test_main(): 761 test_support.run_unittest( 762 TraceTestCase, 763 RaisingTraceFuncTestCase, 764 JumpTestCase 765 ) 766 767 if __name__ == "__main__": 337 run_unittest(__name__) 338 339 340 if __name__ == '__main__': 768 341 test_main()
Note:
See TracChangeset
for help on using the changeset viewer.