Mercurial > hg
comparison tests/test-util.py @ 38797:8751d1e2a7ff
util: create a context manager to handle timing
The context manager is pulled out of the timed decorator function, and
refactored to provide a stats instance, with added tests.
author | Martijn Pieters <mj@zopatista.com> |
---|---|
date | Wed, 01 Aug 2018 16:05:41 +0200 |
parents | |
children | 9d49bb117dde |
comparison
equal
deleted
inserted
replaced
38796:f956dc7217fc | 38797:8751d1e2a7ff |
---|---|
1 # unit tests for mercuril.util utilities | |
2 from __future__ import absolute_import | |
3 | |
4 import contextlib | |
5 import itertools | |
6 import unittest | |
7 | |
8 from mercurial import pycompat, util, utils | |
9 | |
10 @contextlib.contextmanager | |
11 def mocktimer(incr=0.1, *additional_targets): | |
12 """Replaces util.timer and additional_targets with a mock | |
13 | |
14 The timer starts at 0. On each call the time incremented by the value | |
15 of incr. If incr is an iterable, then the time is incremented by the | |
16 next value from that iterable, looping in a cycle when reaching the end. | |
17 | |
18 additional_targets must be a sequence of (object, attribute_name) tuples; | |
19 the mock is set with setattr(object, attribute_name, mock). | |
20 | |
21 """ | |
22 time = [0] | |
23 try: | |
24 incr = itertools.cycle(incr) | |
25 except TypeError: | |
26 incr = itertools.repeat(incr) | |
27 | |
28 def timer(): | |
29 time[0] += next(incr) | |
30 return time[0] | |
31 | |
32 # record original values | |
33 orig = util.timer | |
34 additional_origs = [(o, a, getattr(o, a)) for o, a in additional_targets] | |
35 | |
36 # mock out targets | |
37 util.timer = timer | |
38 for obj, attr in additional_targets: | |
39 setattr(obj, attr, timer) | |
40 | |
41 try: | |
42 yield | |
43 finally: | |
44 # restore originals | |
45 util.timer = orig | |
46 for args in additional_origs: | |
47 setattr(*args) | |
48 | |
49 # attr.s default factory for util.timedstats.start binds the timer we | |
50 # need to mock out. | |
51 _start_default = (util.timedcmstats.start.default, 'factory') | |
52 | |
53 @contextlib.contextmanager | |
54 def capturestderr(): | |
55 """Replace utils.procutil.stderr with a pycompat.bytesio instance | |
56 | |
57 The instance is made available as the return value of __enter__. | |
58 | |
59 This contextmanager is reentrant. | |
60 | |
61 """ | |
62 orig = utils.procutil.stderr | |
63 utils.procutil.stderr = pycompat.bytesio() | |
64 try: | |
65 yield utils.procutil.stderr | |
66 finally: | |
67 utils.procutil.stderr = orig | |
68 | |
69 class timedtests(unittest.TestCase): | |
70 def testtimedcmstatsstr(self): | |
71 stats = util.timedcmstats() | |
72 self.assertEqual(str(stats), '<unknown>') | |
73 stats.elapsed = 12.34 | |
74 self.assertEqual(str(stats), util.timecount(12.34)) | |
75 | |
76 def testtimedcmcleanexit(self): | |
77 # timestamps 1, 4, elapsed time of 4 - 1 = 3 | |
78 with mocktimer([1, 3], _start_default): | |
79 with util.timedcm() as stats: | |
80 # actual context doesn't matter | |
81 pass | |
82 | |
83 self.assertEqual(stats.start, 1) | |
84 self.assertEqual(stats.elapsed, 3) | |
85 self.assertEqual(stats.level, 1) | |
86 | |
87 def testtimedcmnested(self): | |
88 # timestamps 1, 3, 6, 10, elapsed times of 6 - 3 = 3 and 10 - 1 = 9 | |
89 with mocktimer([1, 2, 3, 4], _start_default): | |
90 with util.timedcm() as outer_stats: | |
91 with util.timedcm() as inner_stats: | |
92 # actual context doesn't matter | |
93 pass | |
94 | |
95 self.assertEqual(outer_stats.start, 1) | |
96 self.assertEqual(outer_stats.elapsed, 9) | |
97 self.assertEqual(outer_stats.level, 1) | |
98 | |
99 self.assertEqual(inner_stats.start, 3) | |
100 self.assertEqual(inner_stats.elapsed, 3) | |
101 self.assertEqual(inner_stats.level, 2) | |
102 | |
103 def testtimedcmexception(self): | |
104 # timestamps 1, 4, elapsed time of 4 - 1 = 3 | |
105 with mocktimer([1, 3], _start_default): | |
106 try: | |
107 with util.timedcm() as stats: | |
108 raise ValueError() | |
109 except ValueError: | |
110 pass | |
111 | |
112 self.assertEqual(stats.start, 1) | |
113 self.assertEqual(stats.elapsed, 3) | |
114 self.assertEqual(stats.level, 1) | |
115 | |
116 def testtimeddecorator(self): | |
117 @util.timed | |
118 def testfunc(callcount=1): | |
119 callcount -= 1 | |
120 if callcount: | |
121 testfunc(callcount) | |
122 | |
123 # timestamps 1, 2, 3, 4, elapsed time of 3 - 2 = 1 and 4 - 1 = 3 | |
124 with mocktimer(1, _start_default): | |
125 with capturestderr() as out: | |
126 testfunc(2) | |
127 | |
128 self.assertEqual(out.getvalue(), ( | |
129 b' testfunc: 1.000 s\n' | |
130 b' testfunc: 3.000 s\n' | |
131 )) | |
132 | |
133 if __name__ == '__main__': | |
134 import silenttestrunner | |
135 silenttestrunner.main(__name__) |