blob: 2bb1f33f7485f88ab7778279c991450f1137734b [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
169
170class TestRunExecCounts(unittest.TestCase):
171 """A simple sanity test of line-counting, via runctx (exec)"""
172 def setUp(self):
173 self.my_py_filename = fix_ext_py(__file__)
174
175 def test_exec_counts(self):
176 self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
177 code = r'''traced_func_loop(2, 5)'''
178 code = compile(code, __file__, 'exec')
179 self.tracer.runctx(code, globals(), vars())
180
181 firstlineno = get_firstlineno(traced_func_loop)
182 expected = {
183 (self.my_py_filename, firstlineno + 1): 1,
184 (self.my_py_filename, firstlineno + 2): 6,
185 (self.my_py_filename, firstlineno + 3): 5,
186 (self.my_py_filename, firstlineno + 4): 1,
187 }
188
189 # When used through 'run', some other spurios counts are produced, like
190 # the settrace of threading, which we ignore, just making sure that the
191 # counts fo traced_func_loop were right.
192 #
193 for k in expected.keys():
194 self.assertEqual(self.tracer.results().counts[k], expected[k])
195
196
197class TestFuncs(unittest.TestCase):
198 """White-box testing of funcs tracing"""
199 def setUp(self):
200 self.tracer = Trace(count=0, trace=0, countfuncs=1)
201 self.filemod = my_file_and_modname()
202
203 def test_simple_caller(self):
204 self.tracer.runfunc(traced_func_simple_caller, 1)
205
206 expected = {
207 self.filemod + ('traced_func_simple_caller',): 1,
208 self.filemod + ('traced_func_linear',): 1,
209 }
210 self.assertEqual(self.tracer.results().calledfuncs, expected)
211
212 def test_loop_caller_importing(self):
213 self.tracer.runfunc(traced_func_importing_caller, 1)
214
215 expected = {
216 self.filemod + ('traced_func_simple_caller',): 1,
217 self.filemod + ('traced_func_linear',): 1,
218 self.filemod + ('traced_func_importing_caller',): 1,
219 self.filemod + ('traced_func_importing',): 1,
220 (fix_ext_py(testmod.__file__), 'testmod', 'func'): 1,
221 }
222 self.assertEqual(self.tracer.results().calledfuncs, expected)
223
224 def test_inst_method_calling(self):
225 obj = TracedClass(20)
226 self.tracer.runfunc(obj.inst_method_calling, 1)
227
228 expected = {
229 self.filemod + ('TracedClass.inst_method_calling',): 1,
230 self.filemod + ('TracedClass.inst_method_linear',): 1,
231 self.filemod + ('traced_func_linear',): 1,
232 }
233 self.assertEqual(self.tracer.results().calledfuncs, expected)
234
235
236class TestCallers(unittest.TestCase):
237 """White-box testing of callers tracing"""
238 def setUp(self):
239 self.tracer = Trace(count=0, trace=0, countcallers=1)
240 self.filemod = my_file_and_modname()
241
242 def test_loop_caller_importing(self):
243 self.tracer.runfunc(traced_func_importing_caller, 1)
244
245 expected = {
246 ((os.path.splitext(trace.__file__)[0] + '.py', 'trace', 'Trace.runfunc'),
247 (self.filemod + ('traced_func_importing_caller',))): 1,
248 ((self.filemod + ('traced_func_simple_caller',)),
249 (self.filemod + ('traced_func_linear',))): 1,
250 ((self.filemod + ('traced_func_importing_caller',)),
251 (self.filemod + ('traced_func_simple_caller',))): 1,
252 ((self.filemod + ('traced_func_importing_caller',)),
253 (self.filemod + ('traced_func_importing',))): 1,
254 ((self.filemod + ('traced_func_importing',)),
255 (fix_ext_py(testmod.__file__), 'testmod', 'func')): 1,
256 }
257 self.assertEqual(self.tracer.results().callers, expected)
258
259
260# Created separately for issue #3821
261class TestCoverage(unittest.TestCase):
262 def tearDown(self):
263 rmtree(TESTFN)
264 unlink(TESTFN)
265
266 def _coverage(self, tracer):
267 tracer.run('from test import test_pprint; test_pprint.test_main()')
268 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)
292 self.assertEquals(files, [])
293
294
295def test_main():
296 run_unittest(__name__)
297
298
299if __name__ == '__main__':
300 test_main()