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