blob: b2cd728f687519e1d468f02389ef3b87430b8871 [file] [log] [blame]
Alexander Belopolsky4d770172010-09-13 18:14:34 +00001import os
Ezio Melotti23e043f2013-02-15 21:20:50 +02002import io
Alexander Belopolsky4d770172010-09-13 18:14:34 +00003import sys
4from test.support import (run_unittest, TESTFN, rmtree, unlink,
5 captured_stdout)
Ezio Melotti23e043f2013-02-15 21:20:50 +02006import tempfile
Georg Brandl283b1252010-08-02 12:48:46 +00007import unittest
Georg Brandl283b1252010-08-02 12:48:46 +00008
Alexander Belopolsky4d770172010-09-13 18:14:34 +00009import trace
10from trace import CoverageResults, Trace
11
12from test.tracedmodules import testmod
13
14
15#------------------------------- Utilities -----------------------------------#
16
17def fix_ext_py(filename):
18 """Given a .pyc/.pyo filename converts it to the appropriate .py"""
19 if filename.endswith(('.pyc', '.pyo')):
20 filename = filename[:-1]
21 return filename
22
23def my_file_and_modname():
24 """The .py file and module name of this file (__file__)"""
25 modname = os.path.splitext(os.path.basename(__file__))[0]
26 return fix_ext_py(__file__), modname
27
28def get_firstlineno(func):
29 return func.__code__.co_firstlineno
30
31#-------------------- Target functions for tracing ---------------------------#
32#
33# The relative line numbers of lines in these functions matter for verifying
34# tracing. Please modify the appropriate tests if you change one of the
35# functions. Absolute line numbers don't matter.
36#
37
38def traced_func_linear(x, y):
39 a = x
40 b = y
41 c = a + b
42 return c
43
44def traced_func_loop(x, y):
45 c = x
46 for i in range(5):
47 c += y
48 return c
49
50def traced_func_importing(x, y):
51 return x + y + testmod.func(1)
52
53def traced_func_simple_caller(x):
54 c = traced_func_linear(x, x)
55 return c + x
56
57def traced_func_importing_caller(x):
58 k = traced_func_simple_caller(x)
59 k += traced_func_importing(k, x)
60 return k
61
62def traced_func_generator(num):
63 c = 5 # executed once
64 for i in range(num):
65 yield i + c
66
67def traced_func_calling_generator():
68 k = 0
69 for i in traced_func_generator(10):
70 k += i
71
72def traced_doubler(num):
73 return num * 2
74
75def traced_caller_list_comprehension():
76 k = 10
77 mylist = [traced_doubler(i) for i in range(k)]
78 return mylist
79
80
81class TracedClass(object):
82 def __init__(self, x):
83 self.a = x
84
85 def inst_method_linear(self, y):
86 return self.a + y
87
88 def inst_method_calling(self, x):
89 c = self.inst_method_linear(x)
90 return c + traced_func_linear(x, c)
91
92 @classmethod
93 def class_method_linear(cls, y):
94 return y * 2
95
96 @staticmethod
97 def static_method_linear(y):
98 return y * 2
99
100
101#------------------------------ Test cases -----------------------------------#
102
103
104class TestLineCounts(unittest.TestCase):
105 """White-box testing of line-counting, via runfunc"""
106 def setUp(self):
Brett Cannon31f59292011-02-21 19:29:56 +0000107 self.addCleanup(sys.settrace, sys.gettrace())
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000108 self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
109 self.my_py_filename = fix_ext_py(__file__)
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000110
111 def test_traced_func_linear(self):
112 result = self.tracer.runfunc(traced_func_linear, 2, 5)
113 self.assertEqual(result, 7)
114
115 # all lines are executed once
116 expected = {}
117 firstlineno = get_firstlineno(traced_func_linear)
118 for i in range(1, 5):
119 expected[(self.my_py_filename, firstlineno + i)] = 1
120
121 self.assertEqual(self.tracer.results().counts, expected)
122
123 def test_traced_func_loop(self):
124 self.tracer.runfunc(traced_func_loop, 2, 3)
125
126 firstlineno = get_firstlineno(traced_func_loop)
127 expected = {
128 (self.my_py_filename, firstlineno + 1): 1,
129 (self.my_py_filename, firstlineno + 2): 6,
130 (self.my_py_filename, firstlineno + 3): 5,
131 (self.my_py_filename, firstlineno + 4): 1,
132 }
133 self.assertEqual(self.tracer.results().counts, expected)
134
135 def test_traced_func_importing(self):
136 self.tracer.runfunc(traced_func_importing, 2, 5)
137
138 firstlineno = get_firstlineno(traced_func_importing)
139 expected = {
140 (self.my_py_filename, firstlineno + 1): 1,
141 (fix_ext_py(testmod.__file__), 2): 1,
142 (fix_ext_py(testmod.__file__), 3): 1,
143 }
144
145 self.assertEqual(self.tracer.results().counts, expected)
146
147 def test_trace_func_generator(self):
148 self.tracer.runfunc(traced_func_calling_generator)
149
150 firstlineno_calling = get_firstlineno(traced_func_calling_generator)
151 firstlineno_gen = get_firstlineno(traced_func_generator)
152 expected = {
153 (self.my_py_filename, firstlineno_calling + 1): 1,
154 (self.my_py_filename, firstlineno_calling + 2): 11,
155 (self.my_py_filename, firstlineno_calling + 3): 10,
156 (self.my_py_filename, firstlineno_gen + 1): 1,
157 (self.my_py_filename, firstlineno_gen + 2): 11,
158 (self.my_py_filename, firstlineno_gen + 3): 10,
159 }
160 self.assertEqual(self.tracer.results().counts, expected)
161
162 def test_trace_list_comprehension(self):
163 self.tracer.runfunc(traced_caller_list_comprehension)
164
165 firstlineno_calling = get_firstlineno(traced_caller_list_comprehension)
166 firstlineno_called = get_firstlineno(traced_doubler)
167 expected = {
168 (self.my_py_filename, firstlineno_calling + 1): 1,
169 # List compehentions work differently in 3.x, so the count
170 # below changed compared to 2.x.
171 (self.my_py_filename, firstlineno_calling + 2): 12,
172 (self.my_py_filename, firstlineno_calling + 3): 1,
173 (self.my_py_filename, firstlineno_called + 1): 10,
174 }
175 self.assertEqual(self.tracer.results().counts, expected)
176
177
178 def test_linear_methods(self):
179 # XXX todo: later add 'static_method_linear' and 'class_method_linear'
180 # here, once issue1764286 is resolved
181 #
182 for methname in ['inst_method_linear',]:
183 tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
184 traced_obj = TracedClass(25)
185 method = getattr(traced_obj, methname)
186 tracer.runfunc(method, 20)
187
188 firstlineno = get_firstlineno(method)
189 expected = {
190 (self.my_py_filename, firstlineno + 1): 1,
191 }
192 self.assertEqual(tracer.results().counts, expected)
193
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000194class TestRunExecCounts(unittest.TestCase):
195 """A simple sanity test of line-counting, via runctx (exec)"""
196 def setUp(self):
197 self.my_py_filename = fix_ext_py(__file__)
Brett Cannon31f59292011-02-21 19:29:56 +0000198 self.addCleanup(sys.settrace, sys.gettrace())
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000199
200 def test_exec_counts(self):
201 self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
202 code = r'''traced_func_loop(2, 5)'''
203 code = compile(code, __file__, 'exec')
204 self.tracer.runctx(code, globals(), vars())
205
206 firstlineno = get_firstlineno(traced_func_loop)
207 expected = {
208 (self.my_py_filename, firstlineno + 1): 1,
209 (self.my_py_filename, firstlineno + 2): 6,
210 (self.my_py_filename, firstlineno + 3): 5,
211 (self.my_py_filename, firstlineno + 4): 1,
212 }
213
Ezio Melotti13925002011-03-16 11:05:33 +0200214 # When used through 'run', some other spurious counts are produced, like
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000215 # the settrace of threading, which we ignore, just making sure that the
216 # counts fo traced_func_loop were right.
217 #
218 for k in expected.keys():
219 self.assertEqual(self.tracer.results().counts[k], expected[k])
220
221
222class TestFuncs(unittest.TestCase):
223 """White-box testing of funcs tracing"""
224 def setUp(self):
Brett Cannon31f59292011-02-21 19:29:56 +0000225 self.addCleanup(sys.settrace, sys.gettrace())
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000226 self.tracer = Trace(count=0, trace=0, countfuncs=1)
227 self.filemod = my_file_and_modname()
228
229 def test_simple_caller(self):
230 self.tracer.runfunc(traced_func_simple_caller, 1)
231
232 expected = {
233 self.filemod + ('traced_func_simple_caller',): 1,
234 self.filemod + ('traced_func_linear',): 1,
235 }
236 self.assertEqual(self.tracer.results().calledfuncs, expected)
237
238 def test_loop_caller_importing(self):
239 self.tracer.runfunc(traced_func_importing_caller, 1)
240
241 expected = {
242 self.filemod + ('traced_func_simple_caller',): 1,
243 self.filemod + ('traced_func_linear',): 1,
244 self.filemod + ('traced_func_importing_caller',): 1,
245 self.filemod + ('traced_func_importing',): 1,
246 (fix_ext_py(testmod.__file__), 'testmod', 'func'): 1,
247 }
248 self.assertEqual(self.tracer.results().calledfuncs, expected)
249
Brett Cannon7a540732011-02-22 03:04:06 +0000250 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
251 'pre-existing trace function throws off measurements')
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000252 def test_inst_method_calling(self):
253 obj = TracedClass(20)
254 self.tracer.runfunc(obj.inst_method_calling, 1)
255
256 expected = {
257 self.filemod + ('TracedClass.inst_method_calling',): 1,
258 self.filemod + ('TracedClass.inst_method_linear',): 1,
259 self.filemod + ('traced_func_linear',): 1,
260 }
261 self.assertEqual(self.tracer.results().calledfuncs, expected)
262
263
264class TestCallers(unittest.TestCase):
265 """White-box testing of callers tracing"""
266 def setUp(self):
Brett Cannon31f59292011-02-21 19:29:56 +0000267 self.addCleanup(sys.settrace, sys.gettrace())
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000268 self.tracer = Trace(count=0, trace=0, countcallers=1)
269 self.filemod = my_file_and_modname()
270
Brett Cannon7a540732011-02-22 03:04:06 +0000271 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
272 'pre-existing trace function throws off measurements')
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000273 def test_loop_caller_importing(self):
274 self.tracer.runfunc(traced_func_importing_caller, 1)
275
276 expected = {
277 ((os.path.splitext(trace.__file__)[0] + '.py', 'trace', 'Trace.runfunc'),
278 (self.filemod + ('traced_func_importing_caller',))): 1,
279 ((self.filemod + ('traced_func_simple_caller',)),
280 (self.filemod + ('traced_func_linear',))): 1,
281 ((self.filemod + ('traced_func_importing_caller',)),
282 (self.filemod + ('traced_func_simple_caller',))): 1,
283 ((self.filemod + ('traced_func_importing_caller',)),
284 (self.filemod + ('traced_func_importing',))): 1,
285 ((self.filemod + ('traced_func_importing',)),
286 (fix_ext_py(testmod.__file__), 'testmod', 'func')): 1,
287 }
288 self.assertEqual(self.tracer.results().callers, expected)
289
290
291# Created separately for issue #3821
Georg Brandl283b1252010-08-02 12:48:46 +0000292class TestCoverage(unittest.TestCase):
Brett Cannon31f59292011-02-21 19:29:56 +0000293 def setUp(self):
294 self.addCleanup(sys.settrace, sys.gettrace())
295
Georg Brandl283b1252010-08-02 12:48:46 +0000296 def tearDown(self):
297 rmtree(TESTFN)
298 unlink(TESTFN)
299
Alexander Belopolskyff09ce22010-09-24 18:03:12 +0000300 def _coverage(self, tracer,
301 cmd='from test import test_pprint; test_pprint.test_main()'):
302 tracer.run(cmd)
Georg Brandl283b1252010-08-02 12:48:46 +0000303 r = tracer.results()
304 r.write_results(show_missing=True, summary=True, coverdir=TESTFN)
305
306 def test_coverage(self):
307 tracer = trace.Trace(trace=0, count=1)
308 with captured_stdout() as stdout:
309 self._coverage(tracer)
310 stdout = stdout.getvalue()
311 self.assertTrue("pprint.py" in stdout)
312 self.assertTrue("case.py" in stdout) # from unittest
313 files = os.listdir(TESTFN)
314 self.assertTrue("pprint.cover" in files)
315 self.assertTrue("unittest.case.cover" in files)
316
317 def test_coverage_ignore(self):
318 # Ignore all files, nothing should be traced nor printed
319 libpath = os.path.normpath(os.path.dirname(os.__file__))
320 # sys.prefix does not work when running from a checkout
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100321 tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,
322 libpath], trace=0, count=1)
Georg Brandl283b1252010-08-02 12:48:46 +0000323 with captured_stdout() as stdout:
324 self._coverage(tracer)
Georg Brandl283b1252010-08-02 12:48:46 +0000325 if os.path.exists(TESTFN):
326 files = os.listdir(TESTFN)
Brett Cannonfd074152012-04-14 14:10:13 -0400327 self.assertEqual(files, ['_importlib.cover']) # Ignore __import__
Georg Brandl283b1252010-08-02 12:48:46 +0000328
Alexander Belopolskyff09ce22010-09-24 18:03:12 +0000329 def test_issue9936(self):
330 tracer = trace.Trace(trace=0, count=1)
331 modname = 'test.tracedmodules.testmod'
332 # Ensure that the module is executed in import
333 if modname in sys.modules:
334 del sys.modules[modname]
335 cmd = ("import test.tracedmodules.testmod as t;"
336 "t.func(0); t.func2();")
337 with captured_stdout() as stdout:
338 self._coverage(tracer, cmd)
339 stdout.seek(0)
340 stdout.readline()
341 coverage = {}
342 for line in stdout:
343 lines, cov, module = line.split()[:3]
344 coverage[module] = (int(lines), int(cov[:-1]))
Alexander Belopolskya847c812010-09-24 22:04:22 +0000345 # XXX This is needed to run regrtest.py as a script
Alexander Belopolsky1f75f5d2010-11-26 18:51:39 +0000346 modname = trace._fullmodname(sys.modules[modname].__file__)
Alexander Belopolskyff09ce22010-09-24 18:03:12 +0000347 self.assertIn(modname, coverage)
348 self.assertEqual(coverage[modname], (5, 100))
349
Alexander Belopolsky6672ea92010-11-08 18:32:40 +0000350### Tests that don't mess with sys.settrace and can be traced
351### themselves TODO: Skip tests that do mess with sys.settrace when
352### regrtest is invoked with -T option.
353class Test_Ignore(unittest.TestCase):
354 def test_ignored(self):
Alexander Belopolsky18c33732010-11-08 23:10:20 +0000355 jn = os.path.join
Alexander Belopolsky1f75f5d2010-11-26 18:51:39 +0000356 ignore = trace._Ignore(['x', 'y.z'], [jn('foo', 'bar')])
Alexander Belopolsky6672ea92010-11-08 18:32:40 +0000357 self.assertTrue(ignore.names('x.py', 'x'))
358 self.assertFalse(ignore.names('xy.py', 'xy'))
359 self.assertFalse(ignore.names('y.py', 'y'))
Alexander Belopolsky18c33732010-11-08 23:10:20 +0000360 self.assertTrue(ignore.names(jn('foo', 'bar', 'baz.py'), 'baz'))
361 self.assertFalse(ignore.names(jn('bar', 'z.py'), 'z'))
Alexander Belopolsky6672ea92010-11-08 18:32:40 +0000362 # Matched before.
Alexander Belopolsky18c33732010-11-08 23:10:20 +0000363 self.assertTrue(ignore.names(jn('bar', 'baz.py'), 'baz'))
Alexander Belopolsky6672ea92010-11-08 18:32:40 +0000364
Georg Brandl283b1252010-08-02 12:48:46 +0000365
Ezio Melotti23e043f2013-02-15 21:20:50 +0200366class TestDeprecatedMethods(unittest.TestCase):
367
368 def test_deprecated_usage(self):
369 sio = io.StringIO()
370 with self.assertWarns(DeprecationWarning):
371 trace.usage(sio)
372 self.assertIn('Usage:', sio.getvalue())
373
374 def test_deprecated_Ignore(self):
375 with self.assertWarns(DeprecationWarning):
376 trace.Ignore()
377
378 def test_deprecated_modname(self):
379 with self.assertWarns(DeprecationWarning):
380 self.assertEqual("spam", trace.modname("spam"))
381
382 def test_deprecated_fullmodname(self):
383 with self.assertWarns(DeprecationWarning):
384 self.assertEqual("spam", trace.fullmodname("spam"))
385
386 def test_deprecated_find_lines_from_code(self):
387 with self.assertWarns(DeprecationWarning):
388 def foo():
389 pass
390 trace.find_lines_from_code(foo.__code__, ["eggs"])
391
392 def test_deprecated_find_lines(self):
393 with self.assertWarns(DeprecationWarning):
394 def foo():
395 pass
396 trace.find_lines(foo.__code__, ["eggs"])
397
398 def test_deprecated_find_strings(self):
399 with self.assertWarns(DeprecationWarning):
400 with tempfile.NamedTemporaryFile() as fd:
401 trace.find_strings(fd.name)
402
403 def test_deprecated_find_executable_linenos(self):
404 with self.assertWarns(DeprecationWarning):
405 with tempfile.NamedTemporaryFile() as fd:
406 trace.find_executable_linenos(fd.name)
407
408
Georg Brandl283b1252010-08-02 12:48:46 +0000409def test_main():
410 run_unittest(__name__)
411
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000412
413if __name__ == '__main__':
Georg Brandl283b1252010-08-02 12:48:46 +0000414 test_main()