blob: 8ca9ba4f2099a8e004e787d2be6663ec6062eb02 [file] [log] [blame]
Alexander Belopolsky4d770172010-09-13 18:14:34 +00001import os
2import sys
3from test.support import (run_unittest, TESTFN, rmtree, unlink,
4 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
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
73def traced_caller_list_comprehension():
74 k = 10
75 mylist = [traced_doubler(i) for i in range(k)]
76 return mylist
77
78
79class TracedClass(object):
80 def __init__(self, x):
81 self.a = x
82
83 def inst_method_linear(self, y):
84 return self.a + y
85
86 def inst_method_calling(self, x):
87 c = self.inst_method_linear(x)
88 return c + traced_func_linear(x, c)
89
90 @classmethod
91 def class_method_linear(cls, y):
92 return y * 2
93
94 @staticmethod
95 def static_method_linear(y):
96 return y * 2
97
98
99#------------------------------ Test cases -----------------------------------#
100
101
102class TestLineCounts(unittest.TestCase):
103 """White-box testing of line-counting, via runfunc"""
104 def setUp(self):
105 self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
106 self.my_py_filename = fix_ext_py(__file__)
107 self.maxDiff = None
108
109 def test_traced_func_linear(self):
110 result = self.tracer.runfunc(traced_func_linear, 2, 5)
111 self.assertEqual(result, 7)
112
113 # all lines are executed once
114 expected = {}
115 firstlineno = get_firstlineno(traced_func_linear)
116 for i in range(1, 5):
117 expected[(self.my_py_filename, firstlineno + i)] = 1
118
119 self.assertEqual(self.tracer.results().counts, expected)
120
121 def test_traced_func_loop(self):
122 self.tracer.runfunc(traced_func_loop, 2, 3)
123
124 firstlineno = get_firstlineno(traced_func_loop)
125 expected = {
126 (self.my_py_filename, firstlineno + 1): 1,
127 (self.my_py_filename, firstlineno + 2): 6,
128 (self.my_py_filename, firstlineno + 3): 5,
129 (self.my_py_filename, firstlineno + 4): 1,
130 }
131 self.assertEqual(self.tracer.results().counts, expected)
132
133 def test_traced_func_importing(self):
134 self.tracer.runfunc(traced_func_importing, 2, 5)
135
136 firstlineno = get_firstlineno(traced_func_importing)
137 expected = {
138 (self.my_py_filename, firstlineno + 1): 1,
139 (fix_ext_py(testmod.__file__), 2): 1,
140 (fix_ext_py(testmod.__file__), 3): 1,
141 }
142
143 self.assertEqual(self.tracer.results().counts, expected)
144
145 def test_trace_func_generator(self):
146 self.tracer.runfunc(traced_func_calling_generator)
147
148 firstlineno_calling = get_firstlineno(traced_func_calling_generator)
149 firstlineno_gen = get_firstlineno(traced_func_generator)
150 expected = {
151 (self.my_py_filename, firstlineno_calling + 1): 1,
152 (self.my_py_filename, firstlineno_calling + 2): 11,
153 (self.my_py_filename, firstlineno_calling + 3): 10,
154 (self.my_py_filename, firstlineno_gen + 1): 1,
155 (self.my_py_filename, firstlineno_gen + 2): 11,
156 (self.my_py_filename, firstlineno_gen + 3): 10,
157 }
158 self.assertEqual(self.tracer.results().counts, expected)
159
160 def test_trace_list_comprehension(self):
161 self.tracer.runfunc(traced_caller_list_comprehension)
162
163 firstlineno_calling = get_firstlineno(traced_caller_list_comprehension)
164 firstlineno_called = get_firstlineno(traced_doubler)
165 expected = {
166 (self.my_py_filename, firstlineno_calling + 1): 1,
167 # List compehentions work differently in 3.x, so the count
168 # below changed compared to 2.x.
169 (self.my_py_filename, firstlineno_calling + 2): 12,
170 (self.my_py_filename, firstlineno_calling + 3): 1,
171 (self.my_py_filename, firstlineno_called + 1): 10,
172 }
173 self.assertEqual(self.tracer.results().counts, expected)
174
175
176 def test_linear_methods(self):
177 # XXX todo: later add 'static_method_linear' and 'class_method_linear'
178 # here, once issue1764286 is resolved
179 #
180 for methname in ['inst_method_linear',]:
181 tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
182 traced_obj = TracedClass(25)
183 method = getattr(traced_obj, methname)
184 tracer.runfunc(method, 20)
185
186 firstlineno = get_firstlineno(method)
187 expected = {
188 (self.my_py_filename, firstlineno + 1): 1,
189 }
190 self.assertEqual(tracer.results().counts, expected)
191
192
193class 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__)
197
198 def test_exec_counts(self):
199 self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
200 code = r'''traced_func_loop(2, 5)'''
201 code = compile(code, __file__, 'exec')
202 self.tracer.runctx(code, globals(), vars())
203
204 firstlineno = get_firstlineno(traced_func_loop)
205 expected = {
206 (self.my_py_filename, firstlineno + 1): 1,
207 (self.my_py_filename, firstlineno + 2): 6,
208 (self.my_py_filename, firstlineno + 3): 5,
209 (self.my_py_filename, firstlineno + 4): 1,
210 }
211
212 # When used through 'run', some other spurios counts are produced, like
213 # the settrace of threading, which we ignore, just making sure that the
214 # counts fo traced_func_loop were right.
215 #
216 for k in expected.keys():
217 self.assertEqual(self.tracer.results().counts[k], expected[k])
218
219
220class TestFuncs(unittest.TestCase):
221 """White-box testing of funcs tracing"""
222 def setUp(self):
223 self.tracer = Trace(count=0, trace=0, countfuncs=1)
224 self.filemod = my_file_and_modname()
225
226 def test_simple_caller(self):
227 self.tracer.runfunc(traced_func_simple_caller, 1)
228
229 expected = {
230 self.filemod + ('traced_func_simple_caller',): 1,
231 self.filemod + ('traced_func_linear',): 1,
232 }
233 self.assertEqual(self.tracer.results().calledfuncs, expected)
234
235 def test_loop_caller_importing(self):
236 self.tracer.runfunc(traced_func_importing_caller, 1)
237
238 expected = {
239 self.filemod + ('traced_func_simple_caller',): 1,
240 self.filemod + ('traced_func_linear',): 1,
241 self.filemod + ('traced_func_importing_caller',): 1,
242 self.filemod + ('traced_func_importing',): 1,
243 (fix_ext_py(testmod.__file__), 'testmod', 'func'): 1,
244 }
245 self.assertEqual(self.tracer.results().calledfuncs, expected)
246
247 def test_inst_method_calling(self):
248 obj = TracedClass(20)
249 self.tracer.runfunc(obj.inst_method_calling, 1)
250
251 expected = {
252 self.filemod + ('TracedClass.inst_method_calling',): 1,
253 self.filemod + ('TracedClass.inst_method_linear',): 1,
254 self.filemod + ('traced_func_linear',): 1,
255 }
256 self.assertEqual(self.tracer.results().calledfuncs, expected)
257
258
259class TestCallers(unittest.TestCase):
260 """White-box testing of callers tracing"""
261 def setUp(self):
262 self.tracer = Trace(count=0, trace=0, countcallers=1)
263 self.filemod = my_file_and_modname()
264
265 def test_loop_caller_importing(self):
266 self.tracer.runfunc(traced_func_importing_caller, 1)
267
268 expected = {
269 ((os.path.splitext(trace.__file__)[0] + '.py', 'trace', 'Trace.runfunc'),
270 (self.filemod + ('traced_func_importing_caller',))): 1,
271 ((self.filemod + ('traced_func_simple_caller',)),
272 (self.filemod + ('traced_func_linear',))): 1,
273 ((self.filemod + ('traced_func_importing_caller',)),
274 (self.filemod + ('traced_func_simple_caller',))): 1,
275 ((self.filemod + ('traced_func_importing_caller',)),
276 (self.filemod + ('traced_func_importing',))): 1,
277 ((self.filemod + ('traced_func_importing',)),
278 (fix_ext_py(testmod.__file__), 'testmod', 'func')): 1,
279 }
280 self.assertEqual(self.tracer.results().callers, expected)
281
282
283# Created separately for issue #3821
Georg Brandl283b1252010-08-02 12:48:46 +0000284class TestCoverage(unittest.TestCase):
285 def tearDown(self):
286 rmtree(TESTFN)
287 unlink(TESTFN)
288
289 def _coverage(self, tracer):
290 tracer.run('from test import test_pprint; test_pprint.test_main()')
291 r = tracer.results()
292 r.write_results(show_missing=True, summary=True, coverdir=TESTFN)
293
294 def test_coverage(self):
295 tracer = trace.Trace(trace=0, count=1)
296 with captured_stdout() as stdout:
297 self._coverage(tracer)
298 stdout = stdout.getvalue()
299 self.assertTrue("pprint.py" in stdout)
300 self.assertTrue("case.py" in stdout) # from unittest
301 files = os.listdir(TESTFN)
302 self.assertTrue("pprint.cover" in files)
303 self.assertTrue("unittest.case.cover" in files)
304
305 def test_coverage_ignore(self):
306 # Ignore all files, nothing should be traced nor printed
307 libpath = os.path.normpath(os.path.dirname(os.__file__))
308 # sys.prefix does not work when running from a checkout
309 tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix, libpath],
310 trace=0, count=1)
311 with captured_stdout() as stdout:
312 self._coverage(tracer)
Georg Brandl283b1252010-08-02 12:48:46 +0000313 if os.path.exists(TESTFN):
314 files = os.listdir(TESTFN)
315 self.assertEquals(files, [])
316
317
318def test_main():
319 run_unittest(__name__)
320
Alexander Belopolsky4d770172010-09-13 18:14:34 +0000321
322if __name__ == '__main__':
Georg Brandl283b1252010-08-02 12:48:46 +0000323 test_main()