ยปCore Development>Code coverage>Lib/test/test_trace.py

Python code coverage for Lib/test/test_trace.py

#countcontent
1n/aimport os
2n/aimport sys
3n/afrom test.support import TESTFN, rmtree, unlink, captured_stdout
4n/afrom test.support.script_helper import assert_python_ok, assert_python_failure
5n/aimport unittest
6n/a
7n/aimport trace
8n/afrom trace import Trace
9n/a
10n/afrom test.tracedmodules import testmod
11n/a
12n/a#------------------------------- Utilities -----------------------------------#
13n/a
14n/adef fix_ext_py(filename):
15n/a """Given a .pyc filename converts it to the appropriate .py"""
16n/a if filename.endswith('.pyc'):
17n/a filename = filename[:-1]
18n/a return filename
19n/a
20n/adef my_file_and_modname():
21n/a """The .py file and module name of this file (__file__)"""
22n/a modname = os.path.splitext(os.path.basename(__file__))[0]
23n/a return fix_ext_py(__file__), modname
24n/a
25n/adef get_firstlineno(func):
26n/a return func.__code__.co_firstlineno
27n/a
28n/a#-------------------- Target functions for tracing ---------------------------#
29n/a#
30n/a# The relative line numbers of lines in these functions matter for verifying
31n/a# tracing. Please modify the appropriate tests if you change one of the
32n/a# functions. Absolute line numbers don't matter.
33n/a#
34n/a
35n/adef traced_func_linear(x, y):
36n/a a = x
37n/a b = y
38n/a c = a + b
39n/a return c
40n/a
41n/adef traced_func_loop(x, y):
42n/a c = x
43n/a for i in range(5):
44n/a c += y
45n/a return c
46n/a
47n/adef traced_func_importing(x, y):
48n/a return x + y + testmod.func(1)
49n/a
50n/adef traced_func_simple_caller(x):
51n/a c = traced_func_linear(x, x)
52n/a return c + x
53n/a
54n/adef traced_func_importing_caller(x):
55n/a k = traced_func_simple_caller(x)
56n/a k += traced_func_importing(k, x)
57n/a return k
58n/a
59n/adef traced_func_generator(num):
60n/a c = 5 # executed once
61n/a for i in range(num):
62n/a yield i + c
63n/a
64n/adef traced_func_calling_generator():
65n/a k = 0
66n/a for i in traced_func_generator(10):
67n/a k += i
68n/a
69n/adef traced_doubler(num):
70n/a return num * 2
71n/a
72n/adef traced_caller_list_comprehension():
73n/a k = 10
74n/a mylist = [traced_doubler(i) for i in range(k)]
75n/a return mylist
76n/a
77n/a
78n/aclass TracedClass(object):
79n/a def __init__(self, x):
80n/a self.a = x
81n/a
82n/a def inst_method_linear(self, y):
83n/a return self.a + y
84n/a
85n/a def inst_method_calling(self, x):
86n/a c = self.inst_method_linear(x)
87n/a return c + traced_func_linear(x, c)
88n/a
89n/a @classmethod
90n/a def class_method_linear(cls, y):
91n/a return y * 2
92n/a
93n/a @staticmethod
94n/a def static_method_linear(y):
95n/a return y * 2
96n/a
97n/a
98n/a#------------------------------ Test cases -----------------------------------#
99n/a
100n/a
101n/aclass TestLineCounts(unittest.TestCase):
102n/a """White-box testing of line-counting, via runfunc"""
103n/a def setUp(self):
104n/a self.addCleanup(sys.settrace, sys.gettrace())
105n/a self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
106n/a self.my_py_filename = fix_ext_py(__file__)
107n/a
108n/a def test_traced_func_linear(self):
109n/a result = self.tracer.runfunc(traced_func_linear, 2, 5)
110n/a self.assertEqual(result, 7)
111n/a
112n/a # all lines are executed once
113n/a expected = {}
114n/a firstlineno = get_firstlineno(traced_func_linear)
115n/a for i in range(1, 5):
116n/a expected[(self.my_py_filename, firstlineno + i)] = 1
117n/a
118n/a self.assertEqual(self.tracer.results().counts, expected)
119n/a
120n/a def test_traced_func_loop(self):
121n/a self.tracer.runfunc(traced_func_loop, 2, 3)
122n/a
123n/a firstlineno = get_firstlineno(traced_func_loop)
124n/a expected = {
125n/a (self.my_py_filename, firstlineno + 1): 1,
126n/a (self.my_py_filename, firstlineno + 2): 6,
127n/a (self.my_py_filename, firstlineno + 3): 5,
128n/a (self.my_py_filename, firstlineno + 4): 1,
129n/a }
130n/a self.assertEqual(self.tracer.results().counts, expected)
131n/a
132n/a def test_traced_func_importing(self):
133n/a self.tracer.runfunc(traced_func_importing, 2, 5)
134n/a
135n/a firstlineno = get_firstlineno(traced_func_importing)
136n/a expected = {
137n/a (self.my_py_filename, firstlineno + 1): 1,
138n/a (fix_ext_py(testmod.__file__), 2): 1,
139n/a (fix_ext_py(testmod.__file__), 3): 1,
140n/a }
141n/a
142n/a self.assertEqual(self.tracer.results().counts, expected)
143n/a
144n/a def test_trace_func_generator(self):
145n/a self.tracer.runfunc(traced_func_calling_generator)
146n/a
147n/a firstlineno_calling = get_firstlineno(traced_func_calling_generator)
148n/a firstlineno_gen = get_firstlineno(traced_func_generator)
149n/a expected = {
150n/a (self.my_py_filename, firstlineno_calling + 1): 1,
151n/a (self.my_py_filename, firstlineno_calling + 2): 11,
152n/a (self.my_py_filename, firstlineno_calling + 3): 10,
153n/a (self.my_py_filename, firstlineno_gen + 1): 1,
154n/a (self.my_py_filename, firstlineno_gen + 2): 11,
155n/a (self.my_py_filename, firstlineno_gen + 3): 10,
156n/a }
157n/a self.assertEqual(self.tracer.results().counts, expected)
158n/a
159n/a def test_trace_list_comprehension(self):
160n/a self.tracer.runfunc(traced_caller_list_comprehension)
161n/a
162n/a firstlineno_calling = get_firstlineno(traced_caller_list_comprehension)
163n/a firstlineno_called = get_firstlineno(traced_doubler)
164n/a expected = {
165n/a (self.my_py_filename, firstlineno_calling + 1): 1,
166n/a # List compehentions work differently in 3.x, so the count
167n/a # below changed compared to 2.x.
168n/a (self.my_py_filename, firstlineno_calling + 2): 12,
169n/a (self.my_py_filename, firstlineno_calling + 3): 1,
170n/a (self.my_py_filename, firstlineno_called + 1): 10,
171n/a }
172n/a self.assertEqual(self.tracer.results().counts, expected)
173n/a
174n/a
175n/a def test_linear_methods(self):
176n/a # XXX todo: later add 'static_method_linear' and 'class_method_linear'
177n/a # here, once issue1764286 is resolved
178n/a #
179n/a for methname in ['inst_method_linear',]:
180n/a tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
181n/a traced_obj = TracedClass(25)
182n/a method = getattr(traced_obj, methname)
183n/a tracer.runfunc(method, 20)
184n/a
185n/a firstlineno = get_firstlineno(method)
186n/a expected = {
187n/a (self.my_py_filename, firstlineno + 1): 1,
188n/a }
189n/a self.assertEqual(tracer.results().counts, expected)
190n/a
191n/aclass TestRunExecCounts(unittest.TestCase):
192n/a """A simple sanity test of line-counting, via runctx (exec)"""
193n/a def setUp(self):
194n/a self.my_py_filename = fix_ext_py(__file__)
195n/a self.addCleanup(sys.settrace, sys.gettrace())
196n/a
197n/a def test_exec_counts(self):
198n/a self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
199n/a code = r'''traced_func_loop(2, 5)'''
200n/a code = compile(code, __file__, 'exec')
201n/a self.tracer.runctx(code, globals(), vars())
202n/a
203n/a firstlineno = get_firstlineno(traced_func_loop)
204n/a expected = {
205n/a (self.my_py_filename, firstlineno + 1): 1,
206n/a (self.my_py_filename, firstlineno + 2): 6,
207n/a (self.my_py_filename, firstlineno + 3): 5,
208n/a (self.my_py_filename, firstlineno + 4): 1,
209n/a }
210n/a
211n/a # When used through 'run', some other spurious counts are produced, like
212n/a # the settrace of threading, which we ignore, just making sure that the
213n/a # counts fo traced_func_loop were right.
214n/a #
215n/a for k in expected.keys():
216n/a self.assertEqual(self.tracer.results().counts[k], expected[k])
217n/a
218n/a
219n/aclass TestFuncs(unittest.TestCase):
220n/a """White-box testing of funcs tracing"""
221n/a def setUp(self):
222n/a self.addCleanup(sys.settrace, sys.gettrace())
223n/a self.tracer = Trace(count=0, trace=0, countfuncs=1)
224n/a self.filemod = my_file_and_modname()
225n/a self._saved_tracefunc = sys.gettrace()
226n/a
227n/a def tearDown(self):
228n/a if self._saved_tracefunc is not None:
229n/a sys.settrace(self._saved_tracefunc)
230n/a
231n/a def test_simple_caller(self):
232n/a self.tracer.runfunc(traced_func_simple_caller, 1)
233n/a
234n/a expected = {
235n/a self.filemod + ('traced_func_simple_caller',): 1,
236n/a self.filemod + ('traced_func_linear',): 1,
237n/a }
238n/a self.assertEqual(self.tracer.results().calledfuncs, expected)
239n/a
240n/a def test_loop_caller_importing(self):
241n/a self.tracer.runfunc(traced_func_importing_caller, 1)
242n/a
243n/a expected = {
244n/a self.filemod + ('traced_func_simple_caller',): 1,
245n/a self.filemod + ('traced_func_linear',): 1,
246n/a self.filemod + ('traced_func_importing_caller',): 1,
247n/a self.filemod + ('traced_func_importing',): 1,
248n/a (fix_ext_py(testmod.__file__), 'testmod', 'func'): 1,
249n/a }
250n/a self.assertEqual(self.tracer.results().calledfuncs, expected)
251n/a
252n/a @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
253n/a 'pre-existing trace function throws off measurements')
254n/a def test_inst_method_calling(self):
255n/a obj = TracedClass(20)
256n/a self.tracer.runfunc(obj.inst_method_calling, 1)
257n/a
258n/a expected = {
259n/a self.filemod + ('TracedClass.inst_method_calling',): 1,
260n/a self.filemod + ('TracedClass.inst_method_linear',): 1,
261n/a self.filemod + ('traced_func_linear',): 1,
262n/a }
263n/a self.assertEqual(self.tracer.results().calledfuncs, expected)
264n/a
265n/a
266n/aclass TestCallers(unittest.TestCase):
267n/a """White-box testing of callers tracing"""
268n/a def setUp(self):
269n/a self.addCleanup(sys.settrace, sys.gettrace())
270n/a self.tracer = Trace(count=0, trace=0, countcallers=1)
271n/a self.filemod = my_file_and_modname()
272n/a
273n/a @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
274n/a 'pre-existing trace function throws off measurements')
275n/a def test_loop_caller_importing(self):
276n/a self.tracer.runfunc(traced_func_importing_caller, 1)
277n/a
278n/a expected = {
279n/a ((os.path.splitext(trace.__file__)[0] + '.py', 'trace', 'Trace.runfunc'),
280n/a (self.filemod + ('traced_func_importing_caller',))): 1,
281n/a ((self.filemod + ('traced_func_simple_caller',)),
282n/a (self.filemod + ('traced_func_linear',))): 1,
283n/a ((self.filemod + ('traced_func_importing_caller',)),
284n/a (self.filemod + ('traced_func_simple_caller',))): 1,
285n/a ((self.filemod + ('traced_func_importing_caller',)),
286n/a (self.filemod + ('traced_func_importing',))): 1,
287n/a ((self.filemod + ('traced_func_importing',)),
288n/a (fix_ext_py(testmod.__file__), 'testmod', 'func')): 1,
289n/a }
290n/a self.assertEqual(self.tracer.results().callers, expected)
291n/a
292n/a
293n/a# Created separately for issue #3821
294n/aclass TestCoverage(unittest.TestCase):
295n/a def setUp(self):
296n/a self.addCleanup(sys.settrace, sys.gettrace())
297n/a
298n/a def tearDown(self):
299n/a rmtree(TESTFN)
300n/a unlink(TESTFN)
301n/a
302n/a def _coverage(self, tracer,
303n/a cmd='import test.support, test.test_pprint;'
304n/a 'test.support.run_unittest(test.test_pprint.QueryTestCase)'):
305n/a tracer.run(cmd)
306n/a r = tracer.results()
307n/a r.write_results(show_missing=True, summary=True, coverdir=TESTFN)
308n/a
309n/a def test_coverage(self):
310n/a tracer = trace.Trace(trace=0, count=1)
311n/a with captured_stdout() as stdout:
312n/a self._coverage(tracer)
313n/a stdout = stdout.getvalue()
314n/a self.assertIn("pprint.py", stdout)
315n/a self.assertIn("case.py", stdout) # from unittest
316n/a files = os.listdir(TESTFN)
317n/a self.assertIn("pprint.cover", files)
318n/a self.assertIn("unittest.case.cover", files)
319n/a
320n/a def test_coverage_ignore(self):
321n/a # Ignore all files, nothing should be traced nor printed
322n/a libpath = os.path.normpath(os.path.dirname(os.__file__))
323n/a # sys.prefix does not work when running from a checkout
324n/a tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,
325n/a libpath], trace=0, count=1)
326n/a with captured_stdout() as stdout:
327n/a self._coverage(tracer)
328n/a if os.path.exists(TESTFN):
329n/a files = os.listdir(TESTFN)
330n/a self.assertEqual(files, ['_importlib.cover']) # Ignore __import__
331n/a
332n/a def test_issue9936(self):
333n/a tracer = trace.Trace(trace=0, count=1)
334n/a modname = 'test.tracedmodules.testmod'
335n/a # Ensure that the module is executed in import
336n/a if modname in sys.modules:
337n/a del sys.modules[modname]
338n/a cmd = ("import test.tracedmodules.testmod as t;"
339n/a "t.func(0); t.func2();")
340n/a with captured_stdout() as stdout:
341n/a self._coverage(tracer, cmd)
342n/a stdout.seek(0)
343n/a stdout.readline()
344n/a coverage = {}
345n/a for line in stdout:
346n/a lines, cov, module = line.split()[:3]
347n/a coverage[module] = (int(lines), int(cov[:-1]))
348n/a # XXX This is needed to run regrtest.py as a script
349n/a modname = trace._fullmodname(sys.modules[modname].__file__)
350n/a self.assertIn(modname, coverage)
351n/a self.assertEqual(coverage[modname], (5, 100))
352n/a
353n/a### Tests that don't mess with sys.settrace and can be traced
354n/a### themselves TODO: Skip tests that do mess with sys.settrace when
355n/a### regrtest is invoked with -T option.
356n/aclass Test_Ignore(unittest.TestCase):
357n/a def test_ignored(self):
358n/a jn = os.path.join
359n/a ignore = trace._Ignore(['x', 'y.z'], [jn('foo', 'bar')])
360n/a self.assertTrue(ignore.names('x.py', 'x'))
361n/a self.assertFalse(ignore.names('xy.py', 'xy'))
362n/a self.assertFalse(ignore.names('y.py', 'y'))
363n/a self.assertTrue(ignore.names(jn('foo', 'bar', 'baz.py'), 'baz'))
364n/a self.assertFalse(ignore.names(jn('bar', 'z.py'), 'z'))
365n/a # Matched before.
366n/a self.assertTrue(ignore.names(jn('bar', 'baz.py'), 'baz'))
367n/a
368n/aclass TestCommandLine(unittest.TestCase):
369n/a
370n/a def test_failures(self):
371n/a _errors = (
372n/a (b'filename is missing: required with the main options', '-l', '-T'),
373n/a (b'cannot specify both --listfuncs and (--trace or --count)', '-lc'),
374n/a (b'argument -R/--no-report: not allowed with argument -r/--report', '-rR'),
375n/a (b'must specify one of --trace, --count, --report, --listfuncs, or --trackcalls', '-g'),
376n/a (b'-r/--report requires -f/--file', '-r'),
377n/a (b'--summary can only be used with --count or --report', '-sT'),
378n/a (b'unrecognized arguments: -y', '-y'))
379n/a for message, *args in _errors:
380n/a *_, stderr = assert_python_failure('-m', 'trace', *args)
381n/a self.assertIn(message, stderr)
382n/a
383n/a def test_listfuncs_flag_success(self):
384n/a with open(TESTFN, 'w') as fd:
385n/a self.addCleanup(unlink, TESTFN)
386n/a fd.write("a = 1\n")
387n/a status, stdout, stderr = assert_python_ok('-m', 'trace', '-l', TESTFN)
388n/a self.assertIn(b'functions called:', stdout)
389n/a
390n/aif __name__ == '__main__':
391n/a unittest.main()