blob: 72a104fc1a6790d668e2fb78e1339837351a7b81 [file] [log] [blame]
R David Murraya88da672011-03-16 17:32:27 -04001import timeit
2import unittest
3import sys
4import io
R David Murraya88da672011-03-16 17:32:27 -04005from textwrap import dedent
6
R David Murraya88da672011-03-16 17:32:27 -04007from test.support import captured_stdout
8from test.support import captured_stderr
9
10# timeit's default number of iterations.
11DEFAULT_NUMBER = 1000000
12
13# timeit's default number of repetitions.
Victor Stinner1b901152016-10-18 17:13:22 +020014DEFAULT_REPEAT = 5
R David Murraya88da672011-03-16 17:32:27 -040015
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
22class FakeTimer:
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
44class TestTimeit(unittest.TestCase):
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)
Serhiy Storchaka2bef5852015-01-26 12:09:17 +020074 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 *')
Serhiy Storchaka557b9a52020-09-22 16:16:46 +030080 self.assertRaises(SyntaxError, timeit.Timer, stmt=' pass')
81 self.assertRaises(SyntaxError, timeit.Timer,
82 setup='while False:\n pass', stmt=' break')
R David Murraya88da672011-03-16 17:32:27 -040083
84 def test_timer_invalid_setup(self):
85 self.assertRaises(ValueError, timeit.Timer, setup=None)
Serhiy Storchaka2bef5852015-01-26 12:09:17 +020086 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 *')
Serhiy Storchaka557b9a52020-09-22 16:16:46 +030092 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')
R David Murraya88da672011-03-16 17:32:27 -040098
Serhiy Storchakaced770d2015-07-15 22:11:36 +030099 fake_setup = "import timeit\ntimeit._fake_timer.setup()"
100 fake_stmt = "import timeit\ntimeit._fake_timer.inc()"
R David Murraya88da672011-03-16 17:32:27 -0400101
102 def fake_callable_setup(self):
103 self.fake_timer.setup()
104
105 def fake_callable_stmt(self):
106 self.fake_timer.inc()
107
Antoine Pitrouef3b9ed2014-08-22 23:13:50 -0400108 def timeit(self, stmt, setup, number=None, globals=None):
R David Murraya88da672011-03-16 17:32:27 -0400109 self.fake_timer = FakeTimer()
Antoine Pitrouef3b9ed2014-08-22 23:13:50 -0400110 t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer,
111 globals=globals)
R David Murraya88da672011-03-16 17:32:27 -0400112 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
Serhiy Storchakaf28fa662015-05-30 19:38:26 +0300135 def test_timeit_callable_setup(self):
136 self.timeit(self.fake_stmt, self.fake_callable_setup, number=3)
137
R David Murraya88da672011-03-16 17:32:27 -0400138 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
Antoine Pitrouef3b9ed2014-08-22 23:13:50 -0400153 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
R David Murraya88da672011-03-16 17:32:27 -0400164 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
Serhiy Storchakaf28fa662015-05-30 19:38:26 +0300198 def test_repeat_callable_setup(self):
199 self.repeat(self.fake_stmt, self.fake_callable_setup,
200 repeat=3, number=5)
201
R David Murraya88da672011-03-16 17:32:27 -0400202 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
Victor Stinner1b901152016-10-18 17:13:22 +0200237 MAIN_DEFAULT_OUTPUT = "1 loop, best of 5: 1 sec per loop\n"
R David Murraya88da672011-03-16 17:32:27 -0400238
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)
Victor Stinner1b901152016-10-18 17:13:22 +0200263 self.assertEqual(s, "1 loop, best of 5: 5.5 sec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400264
265 def test_main_milliseconds(self):
266 s = self.run_main(seconds_per_increment=0.0055)
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300267 self.assertEqual(s, "50 loops, best of 5: 5.5 msec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400268
269 def test_main_microseconds(self):
270 s = self.run_main(seconds_per_increment=0.0000025, switches=['-n100'])
Victor Stinner1b901152016-10-18 17:13:22 +0200271 self.assertEqual(s, "100 loops, best of 5: 2.5 usec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400272
273 def test_main_fixed_iters(self):
274 s = self.run_main(seconds_per_increment=2.0, switches=['-n35'])
Victor Stinner1b901152016-10-18 17:13:22 +0200275 self.assertEqual(s, "35 loops, best of 5: 2 sec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400276
277 def test_main_setup(self):
278 s = self.run_main(seconds_per_increment=2.0,
279 switches=['-n35', '-s', 'print("CustomSetup")'])
Victor Stinner1b901152016-10-18 17:13:22 +0200280 self.assertEqual(s, "CustomSetup\n" * DEFAULT_REPEAT +
281 "35 loops, best of 5: 2 sec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400282
Serhiy Storchakaced770d2015-07-15 22:11:36 +0300283 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)'])
Victor Stinner1b901152016-10-18 17:13:22 +0200286 self.assertEqual(s, "CustomSetup\n" * DEFAULT_REPEAT +
287 "35 loops, best of 5: 2 sec per loop\n")
Serhiy Storchakaced770d2015-07-15 22:11:36 +0300288
R David Murraya88da672011-03-16 17:32:27 -0400289 def test_main_fixed_reps(self):
290 s = self.run_main(seconds_per_increment=60.0, switches=['-r9'])
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200291 self.assertEqual(s, "1 loop, best of 9: 60 sec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400292
293 def test_main_negative_reps(self):
294 s = self.run_main(seconds_per_increment=60.0, switches=['-r-5'])
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200295 self.assertEqual(s, "1 loop, best of 1: 60 sec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400296
Benjamin Petersona5c40902012-10-12 11:44:19 -0400297 @unittest.skipIf(sys.flags.optimize >= 2, "need __doc__")
R David Murraya88da672011-03-16 17:32:27 -0400298 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
R David Murraya88da672011-03-16 17:32:27 -0400304 def test_main_verbose(self):
305 s = self.run_main(switches=['-v'])
306 self.assertEqual(s, dedent("""\
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200307 1 loop -> 1 secs
Victor Stinner62cca922016-10-18 17:55:18 +0200308
Victor Stinner61de57f2016-10-18 17:56:42 +0200309 raw times: 1 sec, 1 sec, 1 sec, 1 sec, 1 sec
Victor Stinner62cca922016-10-18 17:55:18 +0200310
Victor Stinner1b901152016-10-18 17:13:22 +0200311 1 loop, best of 5: 1 sec per loop
R David Murraya88da672011-03-16 17:32:27 -0400312 """))
313
314 def test_main_very_verbose(self):
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300315 s = self.run_main(seconds_per_increment=0.000_030, switches=['-vv'])
R David Murraya88da672011-03-16 17:32:27 -0400316 self.assertEqual(s, dedent("""\
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300317 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
Victor Stinner62cca922016-10-18 17:55:18 +0200330
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300331 raw times: 300 msec, 300 msec, 300 msec, 300 msec, 300 msec
Victor Stinner62cca922016-10-18 17:55:18 +0200332
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300333 10000 loops, best of 5: 30 usec per loop
R David Murraya88da672011-03-16 17:32:27 -0400334 """))
335
Robert Collins302dbc62015-03-18 09:54:50 +1300336 def test_main_with_time_unit(self):
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300337 unit_sec = self.run_main(seconds_per_increment=0.003,
Robert Collins302dbc62015-03-18 09:54:50 +1300338 switches=['-u', 'sec'])
339 self.assertEqual(unit_sec,
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300340 "100 loops, best of 5: 0.003 sec per loop\n")
341 unit_msec = self.run_main(seconds_per_increment=0.003,
Robert Collins302dbc62015-03-18 09:54:50 +1300342 switches=['-u', 'msec'])
343 self.assertEqual(unit_msec,
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300344 "100 loops, best of 5: 3 msec per loop\n")
345 unit_usec = self.run_main(seconds_per_increment=0.003,
Robert Collins302dbc62015-03-18 09:54:50 +1300346 switches=['-u', 'usec'])
347 self.assertEqual(unit_usec,
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300348 "100 loops, best of 5: 3e+03 usec per loop\n")
Robert Collins302dbc62015-03-18 09:54:50 +1300349 # Test invalid unit input
350 with captured_stderr() as error_stringio:
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300351 invalid = self.run_main(seconds_per_increment=0.003,
Robert Collins302dbc62015-03-18 09:54:50 +1300352 switches=['-u', 'parsec'])
353 self.assertEqual(error_stringio.getvalue(),
Victor Stinnerc3e40f82016-10-18 17:42:48 +0200354 "Unrecognized unit. Please select nsec, usec, msec, or sec.\n")
Robert Collins302dbc62015-03-18 09:54:50 +1300355
R David Murraya88da672011-03-16 17:32:27 -0400356 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
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300366 def autorange(self, seconds_per_increment=1/1024, callback=None):
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200367 timer = FakeTimer(seconds_per_increment=seconds_per_increment)
Steven D'Aprano09f4f712016-08-15 01:27:03 +1000368 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()
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300373 self.assertEqual(num_loops, 500)
374 self.assertEqual(time_taken, 500/1024)
Steven D'Aprano09f4f712016-08-15 01:27:03 +1000375
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200376 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
Steven D'Aprano09f4f712016-08-15 01:27:03 +1000381 def test_autorange_with_callback(self):
382 def callback(a, b):
383 print("{} {:.3f}".format(a, b))
384 with captured_stdout() as s:
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200385 num_loops, time_taken = self.autorange(callback=callback)
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300386 self.assertEqual(num_loops, 500)
387 self.assertEqual(time_taken, 500/1024)
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200388 expected = ('1 0.001\n'
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300389 '2 0.002\n'
390 '5 0.005\n'
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200391 '10 0.010\n'
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300392 '20 0.020\n'
393 '50 0.049\n'
394 '100 0.098\n'
395 '200 0.195\n'
396 '500 0.488\n')
Steven D'Aprano09f4f712016-08-15 01:27:03 +1000397 self.assertEqual(s.getvalue(), expected)
398
R David Murraya88da672011-03-16 17:32:27 -0400399
R David Murraya88da672011-03-16 17:32:27 -0400400if __name__ == '__main__':
Zachary Ware38c707e2015-04-13 15:00:43 -0500401 unittest.main()