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