1- # Testing the trace module
2-
3- from test .support import run_unittest , TESTFN , rmtree , unlink , captured_stdout
1+ import os
2+ import sys
3+ from test .support import (run_unittest , TESTFN , rmtree , unlink ,
4+ captured_stdout )
45import unittest
6+
57import trace
6- import os , sys
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+ 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 )
7120
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+
193+ class 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+
220+ class 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+
259+ class 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
8284class TestCoverage (unittest .TestCase ):
9285 def tearDown (self ):
10286 rmtree (TESTFN )
@@ -34,7 +310,6 @@ def test_coverage_ignore(self):
34310 trace = 0 , count = 1 )
35311 with captured_stdout () as stdout :
36312 self ._coverage (tracer )
37- self .assertEquals (stdout .getvalue (), "" )
38313 if os .path .exists (TESTFN ):
39314 files = os .listdir (TESTFN )
40315 self .assertEquals (files , [])
@@ -43,5 +318,6 @@ def test_coverage_ignore(self):
43318def test_main ():
44319 run_unittest (__name__ )
45320
46- if __name__ == "__main__" :
321+
322+ if __name__ == '__main__' :
47323 test_main ()
0 commit comments