test_trace.py 12 KB

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