1 import timeit
2 import unittest
3 import sys
4 import io
5 from textwrap import dedent
6
7 from test.support import captured_stdout
8 from test.support import captured_stderr
9
10 # timeit's default number of iterations.
11 DEFAULT_NUMBER = 1000000
12
13 # timeit's default number of repetitions.
14 DEFAULT_REPEAT = 5
15
16 # XXX: some tests are commented out that would improve the coverage but take a
17 # long time to run because they test the default number of loops, which is
18 # large. The tests could be enabled if there was a way to override the default
19 # number of loops during testing, but this would require changing the signature
20 # of some functions that use the default as a default argument.
21
22 class ESC[4;38;5;81mFakeTimer:
23 BASE_TIME = 42.0
24 def __init__(self, seconds_per_increment=1.0):
25 self.count = 0
26 self.setup_calls = 0
27 self.seconds_per_increment=seconds_per_increment
28 timeit._fake_timer = self
29
30 def __call__(self):
31 return self.BASE_TIME + self.count * self.seconds_per_increment
32
33 def inc(self):
34 self.count += 1
35
36 def setup(self):
37 self.setup_calls += 1
38
39 def wrap_timer(self, timer):
40 """Records 'timer' and returns self as callable timer."""
41 self.saved_timer = timer
42 return self
43
44 class ESC[4;38;5;81mTestTimeit(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
45
46 def tearDown(self):
47 try:
48 del timeit._fake_timer
49 except AttributeError:
50 pass
51
52 def test_reindent_empty(self):
53 self.assertEqual(timeit.reindent("", 0), "")
54 self.assertEqual(timeit.reindent("", 4), "")
55
56 def test_reindent_single(self):
57 self.assertEqual(timeit.reindent("pass", 0), "pass")
58 self.assertEqual(timeit.reindent("pass", 4), "pass")
59
60 def test_reindent_multi_empty(self):
61 self.assertEqual(timeit.reindent("\n\n", 0), "\n\n")
62 self.assertEqual(timeit.reindent("\n\n", 4), "\n \n ")
63
64 def test_reindent_multi(self):
65 self.assertEqual(timeit.reindent(
66 "print()\npass\nbreak", 0),
67 "print()\npass\nbreak")
68 self.assertEqual(timeit.reindent(
69 "print()\npass\nbreak", 4),
70 "print()\n pass\n break")
71
72 def test_timer_invalid_stmt(self):
73 self.assertRaises(ValueError, timeit.Timer, stmt=None)
74 self.assertRaises(SyntaxError, timeit.Timer, stmt='return')
75 self.assertRaises(SyntaxError, timeit.Timer, stmt='yield')
76 self.assertRaises(SyntaxError, timeit.Timer, stmt='yield from ()')
77 self.assertRaises(SyntaxError, timeit.Timer, stmt='break')
78 self.assertRaises(SyntaxError, timeit.Timer, stmt='continue')
79 self.assertRaises(SyntaxError, timeit.Timer, stmt='from timeit import *')
80 self.assertRaises(SyntaxError, timeit.Timer, stmt=' pass')
81 self.assertRaises(SyntaxError, timeit.Timer,
82 setup='while False:\n pass', stmt=' break')
83
84 def test_timer_invalid_setup(self):
85 self.assertRaises(ValueError, timeit.Timer, setup=None)
86 self.assertRaises(SyntaxError, timeit.Timer, setup='return')
87 self.assertRaises(SyntaxError, timeit.Timer, setup='yield')
88 self.assertRaises(SyntaxError, timeit.Timer, setup='yield from ()')
89 self.assertRaises(SyntaxError, timeit.Timer, setup='break')
90 self.assertRaises(SyntaxError, timeit.Timer, setup='continue')
91 self.assertRaises(SyntaxError, timeit.Timer, setup='from timeit import *')
92 self.assertRaises(SyntaxError, timeit.Timer, setup=' pass')
93
94 def test_timer_empty_stmt(self):
95 timeit.Timer(stmt='')
96 timeit.Timer(stmt=' \n\t\f')
97 timeit.Timer(stmt='# comment')
98
99 fake_setup = "import timeit\ntimeit._fake_timer.setup()"
100 fake_stmt = "import timeit\ntimeit._fake_timer.inc()"
101
102 def fake_callable_setup(self):
103 self.fake_timer.setup()
104
105 def fake_callable_stmt(self):
106 self.fake_timer.inc()
107
108 def timeit(self, stmt, setup, number=None, globals=None):
109 self.fake_timer = FakeTimer()
110 t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer,
111 globals=globals)
112 kwargs = {}
113 if number is None:
114 number = DEFAULT_NUMBER
115 else:
116 kwargs['number'] = number
117 delta_time = t.timeit(**kwargs)
118 self.assertEqual(self.fake_timer.setup_calls, 1)
119 self.assertEqual(self.fake_timer.count, number)
120 self.assertEqual(delta_time, number)
121
122 # Takes too long to run in debug build.
123 #def test_timeit_default_iters(self):
124 # self.timeit(self.fake_stmt, self.fake_setup)
125
126 def test_timeit_zero_iters(self):
127 self.timeit(self.fake_stmt, self.fake_setup, number=0)
128
129 def test_timeit_few_iters(self):
130 self.timeit(self.fake_stmt, self.fake_setup, number=3)
131
132 def test_timeit_callable_stmt(self):
133 self.timeit(self.fake_callable_stmt, self.fake_setup, number=3)
134
135 def test_timeit_callable_setup(self):
136 self.timeit(self.fake_stmt, self.fake_callable_setup, number=3)
137
138 def test_timeit_callable_stmt_and_setup(self):
139 self.timeit(self.fake_callable_stmt,
140 self.fake_callable_setup, number=3)
141
142 # Takes too long to run in debug build.
143 #def test_timeit_function(self):
144 # delta_time = timeit.timeit(self.fake_stmt, self.fake_setup,
145 # timer=FakeTimer())
146 # self.assertEqual(delta_time, DEFAULT_NUMBER)
147
148 def test_timeit_function_zero_iters(self):
149 delta_time = timeit.timeit(self.fake_stmt, self.fake_setup, number=0,
150 timer=FakeTimer())
151 self.assertEqual(delta_time, 0)
152
153 def test_timeit_globals_args(self):
154 global _global_timer
155 _global_timer = FakeTimer()
156 t = timeit.Timer(stmt='_global_timer.inc()', timer=_global_timer)
157 self.assertRaises(NameError, t.timeit, number=3)
158 timeit.timeit(stmt='_global_timer.inc()', timer=_global_timer,
159 globals=globals(), number=3)
160 local_timer = FakeTimer()
161 timeit.timeit(stmt='local_timer.inc()', timer=local_timer,
162 globals=locals(), number=3)
163
164 def repeat(self, stmt, setup, repeat=None, number=None):
165 self.fake_timer = FakeTimer()
166 t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer)
167 kwargs = {}
168 if repeat is None:
169 repeat = DEFAULT_REPEAT
170 else:
171 kwargs['repeat'] = repeat
172 if number is None:
173 number = DEFAULT_NUMBER
174 else:
175 kwargs['number'] = number
176 delta_times = t.repeat(**kwargs)
177 self.assertEqual(self.fake_timer.setup_calls, repeat)
178 self.assertEqual(self.fake_timer.count, repeat * number)
179 self.assertEqual(delta_times, repeat * [float(number)])
180
181 # Takes too long to run in debug build.
182 #def test_repeat_default(self):
183 # self.repeat(self.fake_stmt, self.fake_setup)
184
185 def test_repeat_zero_reps(self):
186 self.repeat(self.fake_stmt, self.fake_setup, repeat=0)
187
188 def test_repeat_zero_iters(self):
189 self.repeat(self.fake_stmt, self.fake_setup, number=0)
190
191 def test_repeat_few_reps_and_iters(self):
192 self.repeat(self.fake_stmt, self.fake_setup, repeat=3, number=5)
193
194 def test_repeat_callable_stmt(self):
195 self.repeat(self.fake_callable_stmt, self.fake_setup,
196 repeat=3, number=5)
197
198 def test_repeat_callable_setup(self):
199 self.repeat(self.fake_stmt, self.fake_callable_setup,
200 repeat=3, number=5)
201
202 def test_repeat_callable_stmt_and_setup(self):
203 self.repeat(self.fake_callable_stmt, self.fake_callable_setup,
204 repeat=3, number=5)
205
206 # Takes too long to run in debug build.
207 #def test_repeat_function(self):
208 # delta_times = timeit.repeat(self.fake_stmt, self.fake_setup,
209 # timer=FakeTimer())
210 # self.assertEqual(delta_times, DEFAULT_REPEAT * [float(DEFAULT_NUMBER)])
211
212 def test_repeat_function_zero_reps(self):
213 delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, repeat=0,
214 timer=FakeTimer())
215 self.assertEqual(delta_times, [])
216
217 def test_repeat_function_zero_iters(self):
218 delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, number=0,
219 timer=FakeTimer())
220 self.assertEqual(delta_times, DEFAULT_REPEAT * [0.0])
221
222 def assert_exc_string(self, exc_string, expected_exc_name):
223 exc_lines = exc_string.splitlines()
224 self.assertGreater(len(exc_lines), 2)
225 self.assertTrue(exc_lines[0].startswith('Traceback'))
226 self.assertTrue(exc_lines[-1].startswith(expected_exc_name))
227
228 def test_print_exc(self):
229 s = io.StringIO()
230 t = timeit.Timer("1/0")
231 try:
232 t.timeit()
233 except:
234 t.print_exc(s)
235 self.assert_exc_string(s.getvalue(), 'ZeroDivisionError')
236
237 MAIN_DEFAULT_OUTPUT = "1 loop, best of 5: 1 sec per loop\n"
238
239 def run_main(self, seconds_per_increment=1.0, switches=None, timer=None):
240 if timer is None:
241 timer = FakeTimer(seconds_per_increment=seconds_per_increment)
242 if switches is None:
243 args = []
244 else:
245 args = switches[:]
246 args.append(self.fake_stmt)
247 # timeit.main() modifies sys.path, so save and restore it.
248 orig_sys_path = sys.path[:]
249 with captured_stdout() as s:
250 timeit.main(args=args, _wrap_timer=timer.wrap_timer)
251 sys.path[:] = orig_sys_path[:]
252 return s.getvalue()
253
254 def test_main_bad_switch(self):
255 s = self.run_main(switches=['--bad-switch'])
256 self.assertEqual(s, dedent("""\
257 option --bad-switch not recognized
258 use -h/--help for command line help
259 """))
260
261 def test_main_seconds(self):
262 s = self.run_main(seconds_per_increment=5.5)
263 self.assertEqual(s, "1 loop, best of 5: 5.5 sec per loop\n")
264
265 def test_main_milliseconds(self):
266 s = self.run_main(seconds_per_increment=0.0055)
267 self.assertEqual(s, "50 loops, best of 5: 5.5 msec per loop\n")
268
269 def test_main_microseconds(self):
270 s = self.run_main(seconds_per_increment=0.0000025, switches=['-n100'])
271 self.assertEqual(s, "100 loops, best of 5: 2.5 usec per loop\n")
272
273 def test_main_fixed_iters(self):
274 s = self.run_main(seconds_per_increment=2.0, switches=['-n35'])
275 self.assertEqual(s, "35 loops, best of 5: 2 sec per loop\n")
276
277 def test_main_setup(self):
278 s = self.run_main(seconds_per_increment=2.0,
279 switches=['-n35', '-s', 'print("CustomSetup")'])
280 self.assertEqual(s, "CustomSetup\n" * DEFAULT_REPEAT +
281 "35 loops, best of 5: 2 sec per loop\n")
282
283 def test_main_multiple_setups(self):
284 s = self.run_main(seconds_per_increment=2.0,
285 switches=['-n35', '-s', 'a = "CustomSetup"', '-s', 'print(a)'])
286 self.assertEqual(s, "CustomSetup\n" * DEFAULT_REPEAT +
287 "35 loops, best of 5: 2 sec per loop\n")
288
289 def test_main_fixed_reps(self):
290 s = self.run_main(seconds_per_increment=60.0, switches=['-r9'])
291 self.assertEqual(s, "1 loop, best of 9: 60 sec per loop\n")
292
293 def test_main_negative_reps(self):
294 s = self.run_main(seconds_per_increment=60.0, switches=['-r-5'])
295 self.assertEqual(s, "1 loop, best of 1: 60 sec per loop\n")
296
297 @unittest.skipIf(sys.flags.optimize >= 2, "need __doc__")
298 def test_main_help(self):
299 s = self.run_main(switches=['-h'])
300 # Note: It's not clear that the trailing space was intended as part of
301 # the help text, but since it's there, check for it.
302 self.assertEqual(s, timeit.__doc__ + ' ')
303
304 def test_main_verbose(self):
305 s = self.run_main(switches=['-v'])
306 self.assertEqual(s, dedent("""\
307 1 loop -> 1 secs
308
309 raw times: 1 sec, 1 sec, 1 sec, 1 sec, 1 sec
310
311 1 loop, best of 5: 1 sec per loop
312 """))
313
314 def test_main_very_verbose(self):
315 s = self.run_main(seconds_per_increment=0.000_030, switches=['-vv'])
316 self.assertEqual(s, dedent("""\
317 1 loop -> 3e-05 secs
318 2 loops -> 6e-05 secs
319 5 loops -> 0.00015 secs
320 10 loops -> 0.0003 secs
321 20 loops -> 0.0006 secs
322 50 loops -> 0.0015 secs
323 100 loops -> 0.003 secs
324 200 loops -> 0.006 secs
325 500 loops -> 0.015 secs
326 1000 loops -> 0.03 secs
327 2000 loops -> 0.06 secs
328 5000 loops -> 0.15 secs
329 10000 loops -> 0.3 secs
330
331 raw times: 300 msec, 300 msec, 300 msec, 300 msec, 300 msec
332
333 10000 loops, best of 5: 30 usec per loop
334 """))
335
336 def test_main_with_time_unit(self):
337 unit_sec = self.run_main(seconds_per_increment=0.003,
338 switches=['-u', 'sec'])
339 self.assertEqual(unit_sec,
340 "100 loops, best of 5: 0.003 sec per loop\n")
341 unit_msec = self.run_main(seconds_per_increment=0.003,
342 switches=['-u', 'msec'])
343 self.assertEqual(unit_msec,
344 "100 loops, best of 5: 3 msec per loop\n")
345 unit_usec = self.run_main(seconds_per_increment=0.003,
346 switches=['-u', 'usec'])
347 self.assertEqual(unit_usec,
348 "100 loops, best of 5: 3e+03 usec per loop\n")
349 # Test invalid unit input
350 with captured_stderr() as error_stringio:
351 invalid = self.run_main(seconds_per_increment=0.003,
352 switches=['-u', 'parsec'])
353 self.assertEqual(error_stringio.getvalue(),
354 "Unrecognized unit. Please select nsec, usec, msec, or sec.\n")
355
356 def test_main_exception(self):
357 with captured_stderr() as error_stringio:
358 s = self.run_main(switches=['1/0'])
359 self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError')
360
361 def test_main_exception_fixed_reps(self):
362 with captured_stderr() as error_stringio:
363 s = self.run_main(switches=['-n1', '1/0'])
364 self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError')
365
366 def autorange(self, seconds_per_increment=1/1024, callback=None):
367 timer = FakeTimer(seconds_per_increment=seconds_per_increment)
368 t = timeit.Timer(stmt=self.fake_stmt, setup=self.fake_setup, timer=timer)
369 return t.autorange(callback)
370
371 def test_autorange(self):
372 num_loops, time_taken = self.autorange()
373 self.assertEqual(num_loops, 500)
374 self.assertEqual(time_taken, 500/1024)
375
376 def test_autorange_second(self):
377 num_loops, time_taken = self.autorange(seconds_per_increment=1.0)
378 self.assertEqual(num_loops, 1)
379 self.assertEqual(time_taken, 1.0)
380
381 def test_autorange_with_callback(self):
382 def callback(a, b):
383 print("{} {:.3f}".format(a, b))
384 with captured_stdout() as s:
385 num_loops, time_taken = self.autorange(callback=callback)
386 self.assertEqual(num_loops, 500)
387 self.assertEqual(time_taken, 500/1024)
388 expected = ('1 0.001\n'
389 '2 0.002\n'
390 '5 0.005\n'
391 '10 0.010\n'
392 '20 0.020\n'
393 '50 0.049\n'
394 '100 0.098\n'
395 '200 0.195\n'
396 '500 0.488\n')
397 self.assertEqual(s.getvalue(), expected)
398
399
400 if __name__ == '__main__':
401 unittest.main()