blob: 3697cbf5bb3ea68d0752102a17817c017c0020ba [file] [log] [blame]
Alexander Belopolsky96656372010-09-13 18:38:54 +00001import os
2import sys
3from test.support import (run_unittest, TESTFN, rmtree, unlink,
4 captured_stdout)
5import unittest
6
7import trace
8from trace import CoverageResults, Trace
9
10from test.tracedmodules import testmod
11
12
13#------------------------------- Utilities -----------------------------------#
14
15def 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
21def 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
26def 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
36def traced_func_linear(x, y):
37 a = x
38 b = y
39 c = a + b
40 return c
41
42def traced_func_loop(x, y):
43 c = x
44 for i in range(5):
45 c += y
46 return c
47
48def traced_func_importing(x, y):
49 return x + y + testmod.func(1)
50
51def traced_func_simple_caller(x):
52 c = traced_func_linear(x, x)
53 return c + x
54
55def traced_func_importing_caller(x):
56 k = traced_func_simple_caller(x)
57 k += traced_func_importing(k, x)
58 return k
59
60def traced_func_generator(num):
61 c = 5 # executed once
62 for i in range(num):
63 yield i + c
64
65def traced_func_calling_generator():
66 k = 0
67 for i in traced_func_generator(10):
68 k += i
69
70def traced_doubler(num):
71 return num * 2
72
Alexander Belopolsky96656372010-09-13 18:38:54 +000073class TracedClass(object):
74 def __init__(self, x):
75 self.a = x
76
77 def inst_method_linear(self, y):
78 return self.a + y
79
80 def inst_method_calling(self, x):
81 c = self.inst_method_linear(x)
82 return c + traced_func_linear(x, c)
83
84 @classmethod
85 def class_method_linear(cls, y):
86 return y * 2
87
88 @staticmethod
89 def static_method_linear(y):
90 return y * 2
91
92
93#------------------------------ Test cases -----------------------------------#
94
95
96class TestLineCounts(unittest.TestCase):
97 """White-box testing of line-counting, via runfunc"""
98 def setUp(self):
99 self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
100 self.my_py_filename = fix_ext_py(__file__)
101
102 def test_traced_func_linear(self):
103 result = self.tracer.runfunc(traced_func_linear, 2, 5)
104 self.assertEqual(result, 7)
105
106 # all lines are executed once
107 expected = {}
108 firstlineno = get_firstlineno(traced_func_linear)
109 for i in range(1, 5):
110 expected[(self.my_py_filename, firstlineno + i)] = 1
111
112 self.assertEqual(self.tracer.results().counts, expected)
113
114 def test_traced_func_loop(self):
115 self.tracer.runfunc(traced_func_loop, 2, 3)
116
117 firstlineno = get_firstlineno(traced_func_loop)
118 expected = {
119 (self.my_py_filename, firstlineno + 1): 1,
120 (self.my_py_filename, firstlineno + 2): 6,
121 (self.my_py_filename, firstlineno + 3): 5,
122 (self.my_py_filename, firstlineno + 4): 1,
123 }
124 self.assertEqual(self.tracer.results().counts, expected)
125
126 def test_traced_func_importing(self):
127 self.tracer.runfunc(traced_func_importing, 2, 5)
128
129 firstlineno = get_firstlineno(traced_func_importing)
130 expected = {
131 (self.my_py_filename, firstlineno + 1): 1,
132 (fix_ext_py(testmod.__file__), 2): 1,
133 (fix_ext_py(testmod.__file__), 3): 1,
134 }
135
136 self.assertEqual(self.tracer.results().counts, expected)
137
138 def test_trace_func_generator(self):
139 self.tracer.runfunc(traced_func_calling_generator)
140
141 firstlineno_calling = get_firstlineno(traced_func_calling_generator)
142 firstlineno_gen = get_firstlineno(traced_func_generator)
143 expected = {
144 (self.my_py_filename, firstlineno_calling + 1): 1,
145 (self.my_py_filename, firstlineno_calling + 2): 11,
146 (self.my_py_filename, firstlineno_calling + 3): 10,
147 (self.my_py_filename, firstlineno_gen + 1): 1,
148 (self.my_py_filename, firstlineno_gen + 2): 11,
149 (self.my_py_filename, firstlineno_gen + 3): 10,
150 }
151 self.assertEqual(self.tracer.results().counts, expected)
152
Alexander Belopolsky96656372010-09-13 18:38:54 +0000153 def test_linear_methods(self):
154 # XXX todo: later add 'static_method_linear' and 'class_method_linear'
155 # here, once issue1764286 is resolved
156 #
157 for methname in ['inst_method_linear',]:
158 tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
159 traced_obj = TracedClass(25)
160 method = getattr(traced_obj, methname)
161 tracer.runfunc(method, 20)
162
163 firstlineno = get_firstlineno(method)
164 expected = {
165 (self.my_py_filename, firstlineno + 1): 1,
166 }
167 self.assertEqual(tracer.results().counts, expected)
168
Alexander Belopolsky96656372010-09-13 18:38:54 +0000169class TestRunExecCounts(unittest.TestCase):
170 """A simple sanity test of line-counting, via runctx (exec)"""
171 def setUp(self):
172 self.my_py_filename = fix_ext_py(__file__)
173
174 def test_exec_counts(self):
175 self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
176 code = r'''traced_func_loop(2, 5)'''
177 code = compile(code, __file__, 'exec')
178 self.tracer.runctx(code, globals(), vars())
179
180 firstlineno = get_firstlineno(traced_func_loop)
181 expected = {
182 (self.my_py_filename, firstlineno + 1): 1,
183 (self.my_py_filename, firstlineno + 2): 6,
184 (self.my_py_filename, firstlineno + 3): 5,
185 (self.my_py_filename, firstlineno + 4): 1,
186 }
187
Ezio Melotti13925002011-03-16 11:05:33 +0200188 # When used through 'run', some other spurious counts are produced, like
Alexander Belopolsky96656372010-09-13 18:38:54 +0000189 # the settrace of threading, which we ignore, just making sure that the
190 # counts fo traced_func_loop were right.
191 #
192 for k in expected.keys():
193 self.assertEqual(self.tracer.results().counts[k], expected[k])
194
195
196class TestFuncs(unittest.TestCase):
197 """White-box testing of funcs tracing"""
198 def setUp(self):
199 self.tracer = Trace(count=0, trace=0, countfuncs=1)
200 self.filemod = my_file_and_modname()
201
202 def test_simple_caller(self):
203 self.tracer.runfunc(traced_func_simple_caller, 1)
204
205 expected = {
206 self.filemod + ('traced_func_simple_caller',): 1,
207 self.filemod + ('traced_func_linear',): 1,
208 }
209 self.assertEqual(self.tracer.results().calledfuncs, expected)
210
211 def test_loop_caller_importing(self):
212 self.tracer.runfunc(traced_func_importing_caller, 1)
213
214 expected = {
215 self.filemod + ('traced_func_simple_caller',): 1,
216 self.filemod + ('traced_func_linear',): 1,
217 self.filemod + ('traced_func_importing_caller',): 1,
218 self.filemod + ('traced_func_importing',): 1,
219 (fix_ext_py(testmod.__file__), 'testmod', 'func'): 1,
220 }
221 self.assertEqual(self.tracer.results().calledfuncs, expected)
222
223 def test_inst_method_calling(self):
224 obj = TracedClass(20)
225 self.tracer.runfunc(obj.inst_method_calling, 1)
226
227 expected = {
228 self.filemod + ('TracedClass.inst_method_calling',): 1,
229 self.filemod + ('TracedClass.inst_method_linear',): 1,
230 self.filemod + ('traced_func_linear',): 1,
231 }
232 self.assertEqual(self.tracer.results().calledfuncs, expected)
233
234
235class TestCallers(unittest.TestCase):
236 """White-box testing of callers tracing"""
237 def setUp(self):
238 self.tracer = Trace(count=0, trace=0, countcallers=1)
239 self.filemod = my_file_and_modname()
240
241 def test_loop_caller_importing(self):
242 self.tracer.runfunc(traced_func_importing_caller, 1)
243
244 expected = {
245 ((os.path.splitext(trace.__file__)[0] + '.py', 'trace', 'Trace.runfunc'),
246 (self.filemod + ('traced_func_importing_caller',))): 1,
247 ((self.filemod + ('traced_func_simple_caller',)),
248 (self.filemod + ('traced_func_linear',))): 1,
249 ((self.filemod + ('traced_func_importing_caller',)),
250 (self.filemod + ('traced_func_simple_caller',))): 1,
251 ((self.filemod + ('traced_func_importing_caller',)),
252 (self.filemod + ('traced_func_importing',))): 1,
253 ((self.filemod + ('traced_func_importing',)),
254 (fix_ext_py(testmod.__file__), 'testmod', 'func')): 1,
255 }
256 self.assertEqual(self.tracer.results().callers, expected)
257
258
259# Created separately for issue #3821
260class TestCoverage(unittest.TestCase):
261 def tearDown(self):
262 rmtree(TESTFN)
263 unlink(TESTFN)
264
Alexander Belopolsky402392b2010-09-24 18:08:24 +0000265 def _coverage(self, tracer,
266 cmd='from test import test_pprint; test_pprint.test_main()'):
267 tracer.run(cmd)
Alexander Belopolsky96656372010-09-13 18:38:54 +0000268 r = tracer.results()
269 r.write_results(show_missing=True, summary=True, coverdir=TESTFN)
270
271 def test_coverage(self):
272 tracer = trace.Trace(trace=0, count=1)
273 with captured_stdout() as stdout:
274 self._coverage(tracer)
275 stdout = stdout.getvalue()
276 self.assertTrue("pprint.py" in stdout)
277 self.assertTrue("unittest.py" in stdout)
278 files = os.listdir(TESTFN)
279 self.assertTrue("pprint.cover" in files)
280 self.assertTrue("unittest.cover" in files)
281
282 def test_coverage_ignore(self):
283 # Ignore all files, nothing should be traced nor printed
284 libpath = os.path.normpath(os.path.dirname(os.__file__))
285 # sys.prefix does not work when running from a checkout
286 tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix, libpath],
287 trace=0, count=1)
288 with captured_stdout() as stdout:
289 self._coverage(tracer)
290 if os.path.exists(TESTFN):
291 files = os.listdir(TESTFN)
Ezio Melotti19f2aeb2010-11-21 01:30:29 +0000292 self.assertEqual(files, [])
Alexander Belopolsky96656372010-09-13 18:38:54 +0000293
Alexander Belopolsky402392b2010-09-24 18:08:24 +0000294 def test_issue9936(self):
295 tracer = trace.Trace(trace=0, count=1)
296 modname = 'test.tracedmodules.testmod'
297 # Ensure that the module is executed in import
298 if modname in sys.modules:
299 del sys.modules[modname]
300 cmd = ("import test.tracedmodules.testmod as t;"
301 "t.func(0); t.func2();")
302 with captured_stdout() as stdout:
303 self._coverage(tracer, cmd)
304 stdout.seek(0)
305 stdout.readline()
306 coverage = {}
307 for line in stdout:
308 lines, cov, module = line.split()[:3]
309 coverage[module] = (int(lines), int(cov[:-1]))
Alexander Belopolsky1a0eb982010-09-24 22:14:13 +0000310 # XXX This is needed to run regrtest.py as a script
311 modname = trace.fullmodname(sys.modules[modname].__file__)
Alexander Belopolsky402392b2010-09-24 18:08:24 +0000312 self.assertIn(modname, coverage)
313 self.assertEqual(coverage[modname], (5, 100))
314
Alexander Belopolsky96656372010-09-13 18:38:54 +0000315
316def test_main():
317 run_unittest(__name__)
318
319
320if __name__ == '__main__':
321 test_main()