blob: 296c997c53ee9702f5ad1d707a3c37feb3ad490d [file] [log] [blame]
Alexander Belopolsky85edadb2010-07-25 15:23:24 +00001import pprint
2import sys
3import unittest
4
5from test import support
6
7class TestGetProfile(unittest.TestCase):
8 def setUp(self):
9 sys.setprofile(None)
10
11 def tearDown(self):
12 sys.setprofile(None)
13
14 def test_empty(self):
15 assert sys.getprofile() == None
16
17 def test_setget(self):
18 def fn(*args):
19 pass
20
21 sys.setprofile(fn)
22 assert sys.getprofile() == fn
23
24class HookWatcher:
25 def __init__(self):
26 self.frames = []
27 self.events = []
28
29 def callback(self, frame, event, arg):
30 if (event == "call"
31 or event == "return"
32 or event == "exception"):
33 self.add_event(event, frame)
34
35 def add_event(self, event, frame=None):
36 """Add an event to the log."""
37 if frame is None:
38 frame = sys._getframe(1)
39
40 try:
41 frameno = self.frames.index(frame)
42 except ValueError:
43 frameno = len(self.frames)
44 self.frames.append(frame)
45
46 self.events.append((frameno, event, ident(frame)))
47
48 def get_events(self):
49 """Remove calls to add_event()."""
50 disallowed = [ident(self.add_event.__func__), ident(ident)]
51 self.frames = None
52
53 return [item for item in self.events if item[2] not in disallowed]
54
55
56class ProfileSimulator(HookWatcher):
57 def __init__(self, testcase):
58 self.testcase = testcase
59 self.stack = []
60 HookWatcher.__init__(self)
61
62 def callback(self, frame, event, arg):
63 # Callback registered with sys.setprofile()/sys.settrace()
64 self.dispatch[event](self, frame)
65
66 def trace_call(self, frame):
67 self.add_event('call', frame)
68 self.stack.append(frame)
69
70 def trace_return(self, frame):
71 self.add_event('return', frame)
72 self.stack.pop()
73
74 def trace_exception(self, frame):
75 self.testcase.fail(
76 "the profiler should never receive exception events")
77
78 def trace_pass(self, frame):
79 pass
80
81 dispatch = {
82 'call': trace_call,
83 'exception': trace_exception,
84 'return': trace_return,
85 'c_call': trace_pass,
86 'c_return': trace_pass,
87 'c_exception': trace_pass,
88 }
89
90
91class TestCaseBase(unittest.TestCase):
92 def check_events(self, callable, expected):
93 events = capture_events(callable, self.new_watcher())
94 if events != expected:
95 self.fail("Expected events:\n%s\nReceived events:\n%s"
96 % (pprint.pformat(expected), pprint.pformat(events)))
97
98
99class ProfileHookTestCase(TestCaseBase):
100 def new_watcher(self):
101 return HookWatcher()
102
103 def test_simple(self):
104 def f(p):
105 pass
106 f_ident = ident(f)
107 self.check_events(f, [(1, 'call', f_ident),
108 (1, 'return', f_ident),
109 ])
110
111 def test_exception(self):
112 def f(p):
113 1/0
114 f_ident = ident(f)
115 self.check_events(f, [(1, 'call', f_ident),
116 (1, 'return', f_ident),
117 ])
118
119 def test_caught_exception(self):
120 def f(p):
121 try: 1/0
122 except: pass
123 f_ident = ident(f)
124 self.check_events(f, [(1, 'call', f_ident),
125 (1, 'return', f_ident),
126 ])
127
128 def test_caught_nested_exception(self):
129 def f(p):
130 try: 1/0
131 except: pass
132 f_ident = ident(f)
133 self.check_events(f, [(1, 'call', f_ident),
134 (1, 'return', f_ident),
135 ])
136
137 def test_nested_exception(self):
138 def f(p):
139 1/0
140 f_ident = ident(f)
141 self.check_events(f, [(1, 'call', f_ident),
142 # This isn't what I expected:
143 # (0, 'exception', protect_ident),
144 # I expected this again:
145 (1, 'return', f_ident),
146 ])
147
148 def test_exception_in_except_clause(self):
149 def f(p):
150 1/0
151 def g(p):
152 try:
153 f(p)
154 except:
155 try: f(p)
156 except: pass
157 f_ident = ident(f)
158 g_ident = ident(g)
159 self.check_events(g, [(1, 'call', g_ident),
160 (2, 'call', f_ident),
161 (2, 'return', f_ident),
162 (3, 'call', f_ident),
163 (3, 'return', f_ident),
164 (1, 'return', g_ident),
165 ])
166
167 def test_exception_propogation(self):
168 def f(p):
169 1/0
170 def g(p):
171 try: f(p)
172 finally: p.add_event("falling through")
173 f_ident = ident(f)
174 g_ident = ident(g)
175 self.check_events(g, [(1, 'call', g_ident),
176 (2, 'call', f_ident),
177 (2, 'return', f_ident),
178 (1, 'falling through', g_ident),
179 (1, 'return', g_ident),
180 ])
181
182 def test_raise_twice(self):
183 def f(p):
184 try: 1/0
185 except: 1/0
186 f_ident = ident(f)
187 self.check_events(f, [(1, 'call', f_ident),
188 (1, 'return', f_ident),
189 ])
190
191 def test_raise_reraise(self):
192 def f(p):
193 try: 1/0
194 except: raise
195 f_ident = ident(f)
196 self.check_events(f, [(1, 'call', f_ident),
197 (1, 'return', f_ident),
198 ])
199
200 def test_raise(self):
201 def f(p):
202 raise Exception()
203 f_ident = ident(f)
204 self.check_events(f, [(1, 'call', f_ident),
205 (1, 'return', f_ident),
206 ])
207
208 def test_distant_exception(self):
209 def f():
210 1/0
211 def g():
212 f()
213 def h():
214 g()
215 def i():
216 h()
217 def j(p):
218 i()
219 f_ident = ident(f)
220 g_ident = ident(g)
221 h_ident = ident(h)
222 i_ident = ident(i)
223 j_ident = ident(j)
224 self.check_events(j, [(1, 'call', j_ident),
225 (2, 'call', i_ident),
226 (3, 'call', h_ident),
227 (4, 'call', g_ident),
228 (5, 'call', f_ident),
229 (5, 'return', f_ident),
230 (4, 'return', g_ident),
231 (3, 'return', h_ident),
232 (2, 'return', i_ident),
233 (1, 'return', j_ident),
234 ])
235
236 def test_generator(self):
237 def f():
238 for i in range(2):
239 yield i
240 def g(p):
241 for i in f():
242 pass
243 f_ident = ident(f)
244 g_ident = ident(g)
245 self.check_events(g, [(1, 'call', g_ident),
246 # call the iterator twice to generate values
247 (2, 'call', f_ident),
248 (2, 'return', f_ident),
249 (2, 'call', f_ident),
250 (2, 'return', f_ident),
251 # once more; returns end-of-iteration with
252 # actually raising an exception
253 (2, 'call', f_ident),
254 (2, 'return', f_ident),
255 (1, 'return', g_ident),
256 ])
257
258 def test_stop_iteration(self):
259 def f():
260 for i in range(2):
261 yield i
262 raise StopIteration
263 def g(p):
264 for i in f():
265 pass
266 f_ident = ident(f)
267 g_ident = ident(g)
268 self.check_events(g, [(1, 'call', g_ident),
269 # call the iterator twice to generate values
270 (2, 'call', f_ident),
271 (2, 'return', f_ident),
272 (2, 'call', f_ident),
273 (2, 'return', f_ident),
274 # once more to hit the raise:
275 (2, 'call', f_ident),
276 (2, 'return', f_ident),
277 (1, 'return', g_ident),
278 ])
279
280
281class ProfileSimulatorTestCase(TestCaseBase):
282 def new_watcher(self):
283 return ProfileSimulator(self)
284
285 def test_simple(self):
286 def f(p):
287 pass
288 f_ident = ident(f)
289 self.check_events(f, [(1, 'call', f_ident),
290 (1, 'return', f_ident),
291 ])
292
293 def test_basic_exception(self):
294 def f(p):
295 1/0
296 f_ident = ident(f)
297 self.check_events(f, [(1, 'call', f_ident),
298 (1, 'return', f_ident),
299 ])
300
301 def test_caught_exception(self):
302 def f(p):
303 try: 1/0
304 except: pass
305 f_ident = ident(f)
306 self.check_events(f, [(1, 'call', f_ident),
307 (1, 'return', f_ident),
308 ])
309
310 def test_distant_exception(self):
311 def f():
312 1/0
313 def g():
314 f()
315 def h():
316 g()
317 def i():
318 h()
319 def j(p):
320 i()
321 f_ident = ident(f)
322 g_ident = ident(g)
323 h_ident = ident(h)
324 i_ident = ident(i)
325 j_ident = ident(j)
326 self.check_events(j, [(1, 'call', j_ident),
327 (2, 'call', i_ident),
328 (3, 'call', h_ident),
329 (4, 'call', g_ident),
330 (5, 'call', f_ident),
331 (5, 'return', f_ident),
332 (4, 'return', g_ident),
333 (3, 'return', h_ident),
334 (2, 'return', i_ident),
335 (1, 'return', j_ident),
336 ])
337
338
339def ident(function):
340 if hasattr(function, "f_code"):
341 code = function.f_code
342 else:
343 code = function.__code__
344 return code.co_firstlineno, code.co_name
345
346
347def protect(f, p):
348 try: f(p)
349 except: pass
350
351protect_ident = ident(protect)
352
353
354def capture_events(callable, p=None):
355 try:
356 sys.setprofile()
357 except TypeError:
358 pass
359 else:
360 raise support.TestFailed(
361 'sys.setprofile() did not raise TypeError')
362
363 if p is None:
364 p = HookWatcher()
365 sys.setprofile(p.callback)
366 protect(callable, p)
367 sys.setprofile(None)
368 return p.get_events()[1:-1]
369
370
371def show_events(callable):
372 import pprint
373 pprint.pprint(capture_events(callable))
374
375
376def test_main():
377 support.run_unittest(
378 TestGetProfile,
379 ProfileHookTestCase,
380 ProfileSimulatorTestCase
381 )
382
383
384if __name__ == "__main__":
385 test_main()