blob: 03dff8432dad4e9452a08a119f8b2f1fbbe2a8b4 [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
Serhiy Storchakacbfe07e2015-05-20 19:37:10 +03004from test.support import TESTFN, rmtree, unlink, captured_stdout
Georg Brandl283b1252010-08-02 12:48:46 +00005import unittest
Georg Brandl283b1252010-08-02 12:48:46 +00006
Alexander Belopolsky4d770172010-09-13 18:14:34 +00007import trace
8from trace import CoverageResults, Trace
9
10from test.tracedmodules import testmod
11
Alexander Belopolsky4d770172010-09-13 18:14:34 +000012#------------------------------- Utilities -----------------------------------#
13
14def fix_ext_py(filename):
Brett Cannonf299abd2015-04-13 14:21:02 -040015 """Given a .pyc filename converts it to the appropriate .py"""
16 if filename.endswith('.pyc'):
Alexander Belopolsky4d770172010-09-13 18:14:34 +000017 filename = filename[:-1]
18 return filename
19
20def my_file_and_modname():
21 """The .py file and module name of this file (__file__)"""
22 modname = os.path.splitext(os.path.basename(__file__))[0]
23 return fix_ext_py(__file__), modname
24
25def get_firstlineno(func):
26 return func.__code__.co_firstlineno
27
28#-------------------- Target functions for tracing ---------------------------#
29#
30# The relative line numbers of lines in these functions matter for verifying
31# tracing. Please modify the appropriate tests if you change one of the
32# functions. Absolute line numbers don't matter.
33#
34
35def traced_func_linear(x, y):
36 a = x
37 b = y
38 c = a + b
39 return c
40
41def traced_func_loop(x, y):
42 c = x
43 for i in range(5):
44 c += y
45 return c
46
47def traced_func_importing(x, y):
48 return x + y + testmod.func(1)
49
50def traced_func_simple_caller(x):
51 c = traced_func_linear(x, x)
52 return c + x
53
54def traced_func_importing_caller(x):
55 k = traced_func_simple_caller(x)
56 k += traced_func_importing(k, x)
57 return k
58
59def traced_func_generator(num):
60 c = 5 # executed once
61 for i in range(num):
62 yield i + c
63
64def traced_func_calling_generator():
65 k = 0
66 for i in traced_func_generator(10):
67 k += i
68
69def traced_doubler(num):
70 return num * 2
71
72def traced_caller_list_comprehension():
73 k = 10
74 mylist = [traced_doubler(i) for i in range(k)]
75 return mylist
76
77
78class TracedClass(object):
79 def __init__(self, x):
80 self.a = x
81
82 def inst_method_linear(self, y):
83 return self.a + y
84
85 def inst_method_calling(self, x):
86 c = self.inst_method_linear(x)
87 return c + traced_func_linear(x, c)
88
89 @classmethod
90 def class_method_linear(cls, y):
91 return y * 2
92
93 @staticmethod
94 def static_method_linear(y):
95 return y * 2
96
97
98#------------------------------ Test cases -----------------------------------#
99
100
101class TestLineCounts(unittest.TestCase):
102 """White-box testing of line-counting, via runfunc"""
103 def setUp(self):
Brett Cannon31f59292011-02-21 19:29:56 +0000104 self.addCleanup(sys.settrace, sys.gettrace())
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000105 self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
106 self.my_py_filename = fix_ext_py(__file__)
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000107
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 # List compehentions work differently in 3.x, so the count
167 # below changed compared to 2.x.
168 (self.my_py_filename, firstlineno_calling + 2): 12,
169 (self.my_py_filename, firstlineno_calling + 3): 1,
170 (self.my_py_filename, firstlineno_called + 1): 10,
171 }
172 self.assertEqual(self.tracer.results().counts, expected)
173
174
175 def test_linear_methods(self):
176 # XXX todo: later add 'static_method_linear' and 'class_method_linear'
177 # here, once issue1764286 is resolved
178 #
179 for methname in ['inst_method_linear',]:
180 tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
181 traced_obj = TracedClass(25)
182 method = getattr(traced_obj, methname)
183 tracer.runfunc(method, 20)
184
185 firstlineno = get_firstlineno(method)
186 expected = {
187 (self.my_py_filename, firstlineno + 1): 1,
188 }
189 self.assertEqual(tracer.results().counts, expected)
190
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000191class TestRunExecCounts(unittest.TestCase):
192 """A simple sanity test of line-counting, via runctx (exec)"""
193 def setUp(self):
194 self.my_py_filename = fix_ext_py(__file__)
Brett Cannon31f59292011-02-21 19:29:56 +0000195 self.addCleanup(sys.settrace, sys.gettrace())
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000196
197 def test_exec_counts(self):
198 self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
199 code = r'''traced_func_loop(2, 5)'''
200 code = compile(code, __file__, 'exec')
201 self.tracer.runctx(code, globals(), vars())
202
203 firstlineno = get_firstlineno(traced_func_loop)
204 expected = {
205 (self.my_py_filename, firstlineno + 1): 1,
206 (self.my_py_filename, firstlineno + 2): 6,
207 (self.my_py_filename, firstlineno + 3): 5,
208 (self.my_py_filename, firstlineno + 4): 1,
209 }
210
Ezio Melotti13925002011-03-16 11:05:33 +0200211 # When used through 'run', some other spurious counts are produced, like
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000212 # the settrace of threading, which we ignore, just making sure that the
213 # counts fo traced_func_loop were right.
214 #
215 for k in expected.keys():
216 self.assertEqual(self.tracer.results().counts[k], expected[k])
217
218
219class TestFuncs(unittest.TestCase):
220 """White-box testing of funcs tracing"""
221 def setUp(self):
Brett Cannon31f59292011-02-21 19:29:56 +0000222 self.addCleanup(sys.settrace, sys.gettrace())
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000223 self.tracer = Trace(count=0, trace=0, countfuncs=1)
224 self.filemod = my_file_and_modname()
Alexander Belopolskyf026dae2014-06-29 17:44:05 -0400225 self._saved_tracefunc = sys.gettrace()
226
227 def tearDown(self):
228 if self._saved_tracefunc is not None:
229 sys.settrace(self._saved_tracefunc)
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000230
231 def test_simple_caller(self):
232 self.tracer.runfunc(traced_func_simple_caller, 1)
233
234 expected = {
235 self.filemod + ('traced_func_simple_caller',): 1,
236 self.filemod + ('traced_func_linear',): 1,
237 }
238 self.assertEqual(self.tracer.results().calledfuncs, expected)
239
240 def test_loop_caller_importing(self):
241 self.tracer.runfunc(traced_func_importing_caller, 1)
242
243 expected = {
244 self.filemod + ('traced_func_simple_caller',): 1,
245 self.filemod + ('traced_func_linear',): 1,
246 self.filemod + ('traced_func_importing_caller',): 1,
247 self.filemod + ('traced_func_importing',): 1,
248 (fix_ext_py(testmod.__file__), 'testmod', 'func'): 1,
249 }
250 self.assertEqual(self.tracer.results().calledfuncs, expected)
251
Brett Cannon7a540732011-02-22 03:04:06 +0000252 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
253 'pre-existing trace function throws off measurements')
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000254 def test_inst_method_calling(self):
255 obj = TracedClass(20)
256 self.tracer.runfunc(obj.inst_method_calling, 1)
257
258 expected = {
259 self.filemod + ('TracedClass.inst_method_calling',): 1,
260 self.filemod + ('TracedClass.inst_method_linear',): 1,
261 self.filemod + ('traced_func_linear',): 1,
262 }
263 self.assertEqual(self.tracer.results().calledfuncs, expected)
264
265
266class TestCallers(unittest.TestCase):
267 """White-box testing of callers tracing"""
268 def setUp(self):
Brett Cannon31f59292011-02-21 19:29:56 +0000269 self.addCleanup(sys.settrace, sys.gettrace())
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000270 self.tracer = Trace(count=0, trace=0, countcallers=1)
271 self.filemod = my_file_and_modname()
272
Brett Cannon7a540732011-02-22 03:04:06 +0000273 @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
274 'pre-existing trace function throws off measurements')
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000275 def test_loop_caller_importing(self):
276 self.tracer.runfunc(traced_func_importing_caller, 1)
277
278 expected = {
279 ((os.path.splitext(trace.__file__)[0] + '.py', 'trace', 'Trace.runfunc'),
280 (self.filemod + ('traced_func_importing_caller',))): 1,
281 ((self.filemod + ('traced_func_simple_caller',)),
282 (self.filemod + ('traced_func_linear',))): 1,
283 ((self.filemod + ('traced_func_importing_caller',)),
284 (self.filemod + ('traced_func_simple_caller',))): 1,
285 ((self.filemod + ('traced_func_importing_caller',)),
286 (self.filemod + ('traced_func_importing',))): 1,
287 ((self.filemod + ('traced_func_importing',)),
288 (fix_ext_py(testmod.__file__), 'testmod', 'func')): 1,
289 }
290 self.assertEqual(self.tracer.results().callers, expected)
291
292
293# Created separately for issue #3821
Georg Brandl283b1252010-08-02 12:48:46 +0000294class TestCoverage(unittest.TestCase):
Brett Cannon31f59292011-02-21 19:29:56 +0000295 def setUp(self):
296 self.addCleanup(sys.settrace, sys.gettrace())
297
Georg Brandl283b1252010-08-02 12:48:46 +0000298 def tearDown(self):
299 rmtree(TESTFN)
300 unlink(TESTFN)
301
Alexander Belopolskyff09ce22010-09-24 18:03:12 +0000302 def _coverage(self, tracer,
Serhiy Storchakacbfe07e2015-05-20 19:37:10 +0300303 cmd='import test.support, test.test_pprint;'
304 'test.support.run_unittest(test.test_pprint.QueryTestCase)'):
Alexander Belopolskyff09ce22010-09-24 18:03:12 +0000305 tracer.run(cmd)
Georg Brandl283b1252010-08-02 12:48:46 +0000306 r = tracer.results()
307 r.write_results(show_missing=True, summary=True, coverdir=TESTFN)
308
309 def test_coverage(self):
310 tracer = trace.Trace(trace=0, count=1)
311 with captured_stdout() as stdout:
312 self._coverage(tracer)
313 stdout = stdout.getvalue()
314 self.assertTrue("pprint.py" in stdout)
315 self.assertTrue("case.py" in stdout) # from unittest
316 files = os.listdir(TESTFN)
317 self.assertTrue("pprint.cover" in files)
318 self.assertTrue("unittest.case.cover" in files)
319
320 def test_coverage_ignore(self):
321 # Ignore all files, nothing should be traced nor printed
322 libpath = os.path.normpath(os.path.dirname(os.__file__))
323 # sys.prefix does not work when running from a checkout
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100324 tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,
325 libpath], trace=0, count=1)
Georg Brandl283b1252010-08-02 12:48:46 +0000326 with captured_stdout() as stdout:
327 self._coverage(tracer)
Georg Brandl283b1252010-08-02 12:48:46 +0000328 if os.path.exists(TESTFN):
329 files = os.listdir(TESTFN)
Brett Cannonfd074152012-04-14 14:10:13 -0400330 self.assertEqual(files, ['_importlib.cover']) # Ignore __import__
Georg Brandl283b1252010-08-02 12:48:46 +0000331
Alexander Belopolskyff09ce22010-09-24 18:03:12 +0000332 def test_issue9936(self):
333 tracer = trace.Trace(trace=0, count=1)
334 modname = 'test.tracedmodules.testmod'
335 # Ensure that the module is executed in import
336 if modname in sys.modules:
337 del sys.modules[modname]
338 cmd = ("import test.tracedmodules.testmod as t;"
339 "t.func(0); t.func2();")
340 with captured_stdout() as stdout:
341 self._coverage(tracer, cmd)
342 stdout.seek(0)
343 stdout.readline()
344 coverage = {}
345 for line in stdout:
346 lines, cov, module = line.split()[:3]
347 coverage[module] = (int(lines), int(cov[:-1]))
Alexander Belopolskya847c812010-09-24 22:04:22 +0000348 # XXX This is needed to run regrtest.py as a script
Alexander Belopolsky1f75f5d2010-11-26 18:51:39 +0000349 modname = trace._fullmodname(sys.modules[modname].__file__)
Alexander Belopolskyff09ce22010-09-24 18:03:12 +0000350 self.assertIn(modname, coverage)
351 self.assertEqual(coverage[modname], (5, 100))
352
Alexander Belopolsky6672ea92010-11-08 18:32:40 +0000353### Tests that don't mess with sys.settrace and can be traced
354### themselves TODO: Skip tests that do mess with sys.settrace when
355### regrtest is invoked with -T option.
356class Test_Ignore(unittest.TestCase):
357 def test_ignored(self):
Alexander Belopolsky18c33732010-11-08 23:10:20 +0000358 jn = os.path.join
Alexander Belopolsky1f75f5d2010-11-26 18:51:39 +0000359 ignore = trace._Ignore(['x', 'y.z'], [jn('foo', 'bar')])
Alexander Belopolsky6672ea92010-11-08 18:32:40 +0000360 self.assertTrue(ignore.names('x.py', 'x'))
361 self.assertFalse(ignore.names('xy.py', 'xy'))
362 self.assertFalse(ignore.names('y.py', 'y'))
Alexander Belopolsky18c33732010-11-08 23:10:20 +0000363 self.assertTrue(ignore.names(jn('foo', 'bar', 'baz.py'), 'baz'))
364 self.assertFalse(ignore.names(jn('bar', 'z.py'), 'z'))
Alexander Belopolsky6672ea92010-11-08 18:32:40 +0000365 # Matched before.
Alexander Belopolsky18c33732010-11-08 23:10:20 +0000366 self.assertTrue(ignore.names(jn('bar', 'baz.py'), 'baz'))
Alexander Belopolsky6672ea92010-11-08 18:32:40 +0000367
Georg Brandl283b1252010-08-02 12:48:46 +0000368
Ezio Melotti23e043f2013-02-15 21:20:50 +0200369class TestDeprecatedMethods(unittest.TestCase):
370
371 def test_deprecated_usage(self):
372 sio = io.StringIO()
373 with self.assertWarns(DeprecationWarning):
374 trace.usage(sio)
375 self.assertIn('Usage:', sio.getvalue())
376
377 def test_deprecated_Ignore(self):
378 with self.assertWarns(DeprecationWarning):
379 trace.Ignore()
380
381 def test_deprecated_modname(self):
382 with self.assertWarns(DeprecationWarning):
383 self.assertEqual("spam", trace.modname("spam"))
384
385 def test_deprecated_fullmodname(self):
386 with self.assertWarns(DeprecationWarning):
387 self.assertEqual("spam", trace.fullmodname("spam"))
388
389 def test_deprecated_find_lines_from_code(self):
390 with self.assertWarns(DeprecationWarning):
391 def foo():
392 pass
393 trace.find_lines_from_code(foo.__code__, ["eggs"])
394
395 def test_deprecated_find_lines(self):
396 with self.assertWarns(DeprecationWarning):
397 def foo():
398 pass
399 trace.find_lines(foo.__code__, ["eggs"])
400
401 def test_deprecated_find_strings(self):
Ezio Melotti5a9ce072013-02-20 02:52:49 +0200402 with open(TESTFN, 'w') as fd:
403 self.addCleanup(unlink, TESTFN)
Ezio Melotti23e043f2013-02-15 21:20:50 +0200404 with self.assertWarns(DeprecationWarning):
Ezio Melotti5a9ce072013-02-20 02:52:49 +0200405 trace.find_strings(fd.name)
Ezio Melotti23e043f2013-02-15 21:20:50 +0200406
407 def test_deprecated_find_executable_linenos(self):
Ezio Melotti5a9ce072013-02-20 02:52:49 +0200408 with open(TESTFN, 'w') as fd:
409 self.addCleanup(unlink, TESTFN)
Ezio Melotti23e043f2013-02-15 21:20:50 +0200410 with self.assertWarns(DeprecationWarning):
Ezio Melotti5a9ce072013-02-20 02:52:49 +0200411 trace.find_executable_linenos(fd.name)
Ezio Melotti23e043f2013-02-15 21:20:50 +0200412
413
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000414if __name__ == '__main__':
Serhiy Storchakacbfe07e2015-05-20 19:37:10 +0300415 unittest.main()