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