blob: e02d4a71a9ba7c9b32b411f95d76de91d90b92bb [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 *')
R David Murraya88da672011-03-16 17:32:27 -040080
81 def test_timer_invalid_setup(self):
82 self.assertRaises(ValueError, timeit.Timer, setup=None)
Serhiy Storchaka2bef5852015-01-26 12:09:17 +020083 self.assertRaises(SyntaxError, timeit.Timer, setup='return')
84 self.assertRaises(SyntaxError, timeit.Timer, setup='yield')
85 self.assertRaises(SyntaxError, timeit.Timer, setup='yield from ()')
86 self.assertRaises(SyntaxError, timeit.Timer, setup='break')
87 self.assertRaises(SyntaxError, timeit.Timer, setup='continue')
88 self.assertRaises(SyntaxError, timeit.Timer, setup='from timeit import *')
R David Murraya88da672011-03-16 17:32:27 -040089
Serhiy Storchakaced770d2015-07-15 22:11:36 +030090 fake_setup = "import timeit\ntimeit._fake_timer.setup()"
91 fake_stmt = "import timeit\ntimeit._fake_timer.inc()"
R David Murraya88da672011-03-16 17:32:27 -040092
93 def fake_callable_setup(self):
94 self.fake_timer.setup()
95
96 def fake_callable_stmt(self):
97 self.fake_timer.inc()
98
Antoine Pitrouef3b9ed2014-08-22 23:13:50 -040099 def timeit(self, stmt, setup, number=None, globals=None):
R David Murraya88da672011-03-16 17:32:27 -0400100 self.fake_timer = FakeTimer()
Antoine Pitrouef3b9ed2014-08-22 23:13:50 -0400101 t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer,
102 globals=globals)
R David Murraya88da672011-03-16 17:32:27 -0400103 kwargs = {}
104 if number is None:
105 number = DEFAULT_NUMBER
106 else:
107 kwargs['number'] = number
108 delta_time = t.timeit(**kwargs)
109 self.assertEqual(self.fake_timer.setup_calls, 1)
110 self.assertEqual(self.fake_timer.count, number)
111 self.assertEqual(delta_time, number)
112
113 # Takes too long to run in debug build.
114 #def test_timeit_default_iters(self):
115 # self.timeit(self.fake_stmt, self.fake_setup)
116
117 def test_timeit_zero_iters(self):
118 self.timeit(self.fake_stmt, self.fake_setup, number=0)
119
120 def test_timeit_few_iters(self):
121 self.timeit(self.fake_stmt, self.fake_setup, number=3)
122
123 def test_timeit_callable_stmt(self):
124 self.timeit(self.fake_callable_stmt, self.fake_setup, number=3)
125
Serhiy Storchakaf28fa662015-05-30 19:38:26 +0300126 def test_timeit_callable_setup(self):
127 self.timeit(self.fake_stmt, self.fake_callable_setup, number=3)
128
R David Murraya88da672011-03-16 17:32:27 -0400129 def test_timeit_callable_stmt_and_setup(self):
130 self.timeit(self.fake_callable_stmt,
131 self.fake_callable_setup, number=3)
132
133 # Takes too long to run in debug build.
134 #def test_timeit_function(self):
135 # delta_time = timeit.timeit(self.fake_stmt, self.fake_setup,
136 # timer=FakeTimer())
137 # self.assertEqual(delta_time, DEFAULT_NUMBER)
138
139 def test_timeit_function_zero_iters(self):
140 delta_time = timeit.timeit(self.fake_stmt, self.fake_setup, number=0,
141 timer=FakeTimer())
142 self.assertEqual(delta_time, 0)
143
Antoine Pitrouef3b9ed2014-08-22 23:13:50 -0400144 def test_timeit_globals_args(self):
145 global _global_timer
146 _global_timer = FakeTimer()
147 t = timeit.Timer(stmt='_global_timer.inc()', timer=_global_timer)
148 self.assertRaises(NameError, t.timeit, number=3)
149 timeit.timeit(stmt='_global_timer.inc()', timer=_global_timer,
150 globals=globals(), number=3)
151 local_timer = FakeTimer()
152 timeit.timeit(stmt='local_timer.inc()', timer=local_timer,
153 globals=locals(), number=3)
154
R David Murraya88da672011-03-16 17:32:27 -0400155 def repeat(self, stmt, setup, repeat=None, number=None):
156 self.fake_timer = FakeTimer()
157 t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer)
158 kwargs = {}
159 if repeat is None:
160 repeat = DEFAULT_REPEAT
161 else:
162 kwargs['repeat'] = repeat
163 if number is None:
164 number = DEFAULT_NUMBER
165 else:
166 kwargs['number'] = number
167 delta_times = t.repeat(**kwargs)
168 self.assertEqual(self.fake_timer.setup_calls, repeat)
169 self.assertEqual(self.fake_timer.count, repeat * number)
170 self.assertEqual(delta_times, repeat * [float(number)])
171
172 # Takes too long to run in debug build.
173 #def test_repeat_default(self):
174 # self.repeat(self.fake_stmt, self.fake_setup)
175
176 def test_repeat_zero_reps(self):
177 self.repeat(self.fake_stmt, self.fake_setup, repeat=0)
178
179 def test_repeat_zero_iters(self):
180 self.repeat(self.fake_stmt, self.fake_setup, number=0)
181
182 def test_repeat_few_reps_and_iters(self):
183 self.repeat(self.fake_stmt, self.fake_setup, repeat=3, number=5)
184
185 def test_repeat_callable_stmt(self):
186 self.repeat(self.fake_callable_stmt, self.fake_setup,
187 repeat=3, number=5)
188
Serhiy Storchakaf28fa662015-05-30 19:38:26 +0300189 def test_repeat_callable_setup(self):
190 self.repeat(self.fake_stmt, self.fake_callable_setup,
191 repeat=3, number=5)
192
R David Murraya88da672011-03-16 17:32:27 -0400193 def test_repeat_callable_stmt_and_setup(self):
194 self.repeat(self.fake_callable_stmt, self.fake_callable_setup,
195 repeat=3, number=5)
196
197 # Takes too long to run in debug build.
198 #def test_repeat_function(self):
199 # delta_times = timeit.repeat(self.fake_stmt, self.fake_setup,
200 # timer=FakeTimer())
201 # self.assertEqual(delta_times, DEFAULT_REPEAT * [float(DEFAULT_NUMBER)])
202
203 def test_repeat_function_zero_reps(self):
204 delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, repeat=0,
205 timer=FakeTimer())
206 self.assertEqual(delta_times, [])
207
208 def test_repeat_function_zero_iters(self):
209 delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, number=0,
210 timer=FakeTimer())
211 self.assertEqual(delta_times, DEFAULT_REPEAT * [0.0])
212
213 def assert_exc_string(self, exc_string, expected_exc_name):
214 exc_lines = exc_string.splitlines()
215 self.assertGreater(len(exc_lines), 2)
216 self.assertTrue(exc_lines[0].startswith('Traceback'))
217 self.assertTrue(exc_lines[-1].startswith(expected_exc_name))
218
219 def test_print_exc(self):
220 s = io.StringIO()
221 t = timeit.Timer("1/0")
222 try:
223 t.timeit()
224 except:
225 t.print_exc(s)
226 self.assert_exc_string(s.getvalue(), 'ZeroDivisionError')
227
Victor Stinner1b901152016-10-18 17:13:22 +0200228 MAIN_DEFAULT_OUTPUT = "1 loop, best of 5: 1 sec per loop\n"
R David Murraya88da672011-03-16 17:32:27 -0400229
230 def run_main(self, seconds_per_increment=1.0, switches=None, timer=None):
231 if timer is None:
232 timer = FakeTimer(seconds_per_increment=seconds_per_increment)
233 if switches is None:
234 args = []
235 else:
236 args = switches[:]
237 args.append(self.fake_stmt)
238 # timeit.main() modifies sys.path, so save and restore it.
239 orig_sys_path = sys.path[:]
240 with captured_stdout() as s:
241 timeit.main(args=args, _wrap_timer=timer.wrap_timer)
242 sys.path[:] = orig_sys_path[:]
243 return s.getvalue()
244
245 def test_main_bad_switch(self):
246 s = self.run_main(switches=['--bad-switch'])
247 self.assertEqual(s, dedent("""\
248 option --bad-switch not recognized
249 use -h/--help for command line help
250 """))
251
252 def test_main_seconds(self):
253 s = self.run_main(seconds_per_increment=5.5)
Victor Stinner1b901152016-10-18 17:13:22 +0200254 self.assertEqual(s, "1 loop, best of 5: 5.5 sec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400255
256 def test_main_milliseconds(self):
257 s = self.run_main(seconds_per_increment=0.0055)
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300258 self.assertEqual(s, "50 loops, best of 5: 5.5 msec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400259
260 def test_main_microseconds(self):
261 s = self.run_main(seconds_per_increment=0.0000025, switches=['-n100'])
Victor Stinner1b901152016-10-18 17:13:22 +0200262 self.assertEqual(s, "100 loops, best of 5: 2.5 usec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400263
264 def test_main_fixed_iters(self):
265 s = self.run_main(seconds_per_increment=2.0, switches=['-n35'])
Victor Stinner1b901152016-10-18 17:13:22 +0200266 self.assertEqual(s, "35 loops, best of 5: 2 sec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400267
268 def test_main_setup(self):
269 s = self.run_main(seconds_per_increment=2.0,
270 switches=['-n35', '-s', 'print("CustomSetup")'])
Victor Stinner1b901152016-10-18 17:13:22 +0200271 self.assertEqual(s, "CustomSetup\n" * DEFAULT_REPEAT +
272 "35 loops, best of 5: 2 sec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400273
Serhiy Storchakaced770d2015-07-15 22:11:36 +0300274 def test_main_multiple_setups(self):
275 s = self.run_main(seconds_per_increment=2.0,
276 switches=['-n35', '-s', 'a = "CustomSetup"', '-s', 'print(a)'])
Victor Stinner1b901152016-10-18 17:13:22 +0200277 self.assertEqual(s, "CustomSetup\n" * DEFAULT_REPEAT +
278 "35 loops, best of 5: 2 sec per loop\n")
Serhiy Storchakaced770d2015-07-15 22:11:36 +0300279
R David Murraya88da672011-03-16 17:32:27 -0400280 def test_main_fixed_reps(self):
281 s = self.run_main(seconds_per_increment=60.0, switches=['-r9'])
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200282 self.assertEqual(s, "1 loop, best of 9: 60 sec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400283
284 def test_main_negative_reps(self):
285 s = self.run_main(seconds_per_increment=60.0, switches=['-r-5'])
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200286 self.assertEqual(s, "1 loop, best of 1: 60 sec per loop\n")
R David Murraya88da672011-03-16 17:32:27 -0400287
Benjamin Petersona5c40902012-10-12 11:44:19 -0400288 @unittest.skipIf(sys.flags.optimize >= 2, "need __doc__")
R David Murraya88da672011-03-16 17:32:27 -0400289 def test_main_help(self):
290 s = self.run_main(switches=['-h'])
291 # Note: It's not clear that the trailing space was intended as part of
292 # the help text, but since it's there, check for it.
293 self.assertEqual(s, timeit.__doc__ + ' ')
294
R David Murraya88da672011-03-16 17:32:27 -0400295 def test_main_verbose(self):
296 s = self.run_main(switches=['-v'])
297 self.assertEqual(s, dedent("""\
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200298 1 loop -> 1 secs
Victor Stinner62cca922016-10-18 17:55:18 +0200299
Victor Stinner61de57f2016-10-18 17:56:42 +0200300 raw times: 1 sec, 1 sec, 1 sec, 1 sec, 1 sec
Victor Stinner62cca922016-10-18 17:55:18 +0200301
Victor Stinner1b901152016-10-18 17:13:22 +0200302 1 loop, best of 5: 1 sec per loop
R David Murraya88da672011-03-16 17:32:27 -0400303 """))
304
305 def test_main_very_verbose(self):
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300306 s = self.run_main(seconds_per_increment=0.000_030, switches=['-vv'])
R David Murraya88da672011-03-16 17:32:27 -0400307 self.assertEqual(s, dedent("""\
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300308 1 loop -> 3e-05 secs
309 2 loops -> 6e-05 secs
310 5 loops -> 0.00015 secs
311 10 loops -> 0.0003 secs
312 20 loops -> 0.0006 secs
313 50 loops -> 0.0015 secs
314 100 loops -> 0.003 secs
315 200 loops -> 0.006 secs
316 500 loops -> 0.015 secs
317 1000 loops -> 0.03 secs
318 2000 loops -> 0.06 secs
319 5000 loops -> 0.15 secs
320 10000 loops -> 0.3 secs
Victor Stinner62cca922016-10-18 17:55:18 +0200321
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300322 raw times: 300 msec, 300 msec, 300 msec, 300 msec, 300 msec
Victor Stinner62cca922016-10-18 17:55:18 +0200323
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300324 10000 loops, best of 5: 30 usec per loop
R David Murraya88da672011-03-16 17:32:27 -0400325 """))
326
Robert Collins302dbc62015-03-18 09:54:50 +1300327 def test_main_with_time_unit(self):
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300328 unit_sec = self.run_main(seconds_per_increment=0.003,
Robert Collins302dbc62015-03-18 09:54:50 +1300329 switches=['-u', 'sec'])
330 self.assertEqual(unit_sec,
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300331 "100 loops, best of 5: 0.003 sec per loop\n")
332 unit_msec = self.run_main(seconds_per_increment=0.003,
Robert Collins302dbc62015-03-18 09:54:50 +1300333 switches=['-u', 'msec'])
334 self.assertEqual(unit_msec,
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300335 "100 loops, best of 5: 3 msec per loop\n")
336 unit_usec = self.run_main(seconds_per_increment=0.003,
Robert Collins302dbc62015-03-18 09:54:50 +1300337 switches=['-u', 'usec'])
338 self.assertEqual(unit_usec,
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300339 "100 loops, best of 5: 3e+03 usec per loop\n")
Robert Collins302dbc62015-03-18 09:54:50 +1300340 # Test invalid unit input
341 with captured_stderr() as error_stringio:
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300342 invalid = self.run_main(seconds_per_increment=0.003,
Robert Collins302dbc62015-03-18 09:54:50 +1300343 switches=['-u', 'parsec'])
344 self.assertEqual(error_stringio.getvalue(),
Victor Stinnerc3e40f82016-10-18 17:42:48 +0200345 "Unrecognized unit. Please select nsec, usec, msec, or sec.\n")
Robert Collins302dbc62015-03-18 09:54:50 +1300346
R David Murraya88da672011-03-16 17:32:27 -0400347 def test_main_exception(self):
348 with captured_stderr() as error_stringio:
349 s = self.run_main(switches=['1/0'])
350 self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError')
351
352 def test_main_exception_fixed_reps(self):
353 with captured_stderr() as error_stringio:
354 s = self.run_main(switches=['-n1', '1/0'])
355 self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError')
356
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300357 def autorange(self, seconds_per_increment=1/1024, callback=None):
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200358 timer = FakeTimer(seconds_per_increment=seconds_per_increment)
Steven D'Aprano09f4f712016-08-15 01:27:03 +1000359 t = timeit.Timer(stmt=self.fake_stmt, setup=self.fake_setup, timer=timer)
360 return t.autorange(callback)
361
362 def test_autorange(self):
363 num_loops, time_taken = self.autorange()
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300364 self.assertEqual(num_loops, 500)
365 self.assertEqual(time_taken, 500/1024)
Steven D'Aprano09f4f712016-08-15 01:27:03 +1000366
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200367 def test_autorange_second(self):
368 num_loops, time_taken = self.autorange(seconds_per_increment=1.0)
369 self.assertEqual(num_loops, 1)
370 self.assertEqual(time_taken, 1.0)
371
Steven D'Aprano09f4f712016-08-15 01:27:03 +1000372 def test_autorange_with_callback(self):
373 def callback(a, b):
374 print("{} {:.3f}".format(a, b))
375 with captured_stdout() as s:
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200376 num_loops, time_taken = self.autorange(callback=callback)
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300377 self.assertEqual(num_loops, 500)
378 self.assertEqual(time_taken, 500/1024)
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200379 expected = ('1 0.001\n'
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300380 '2 0.002\n'
381 '5 0.005\n'
Victor Stinnerf8fb82c2016-10-18 17:06:56 +0200382 '10 0.010\n'
Serhiy Storchakad3ff7842016-10-23 15:17:05 +0300383 '20 0.020\n'
384 '50 0.049\n'
385 '100 0.098\n'
386 '200 0.195\n'
387 '500 0.488\n')
Steven D'Aprano09f4f712016-08-15 01:27:03 +1000388 self.assertEqual(s.getvalue(), expected)
389
R David Murraya88da672011-03-16 17:32:27 -0400390
R David Murraya88da672011-03-16 17:32:27 -0400391if __name__ == '__main__':
Zachary Ware38c707e2015-04-13 15:00:43 -0500392 unittest.main()