annotate tests/test-util.py @ 39561:d06834e0f48e

wireprotov2peer: stream decoded responses Previously, wire protocol version 2 would buffer all response data. Only once all data was received did we CBOR decode it and resolve the future associated with the command. This was obviously not desirable. In future commits that introduce large response payloads, this caused significant memory bloat and slowed down client operations due to waiting on the server. This commit refactors the response handling code so that response data can be streamed. Command response objects now contain a buffered CBOR decoder. As new data arrives, it is fed into the decoder. Decoded objects are made available to the generator as they are decoded. Because there is a separate thread processing incoming frames and feeding data into the response object, there is the potential for race conditions when mutating response objects. So a lock has been added to guard access to critical state variables. Because the generator emitting decoded objects needs to wait on those objects to become available, we've added an Event for the generator to wait on so it doesn't busy loop. This does mean there is the potential for deadlocks. And I'm pretty sure they can occur in some scenarios. We already have a handful of TODOs around this. But I've added some more. Fixing this will likely require moving the background thread receiving frames into clienthandler. We likely would have done this anyway when implementing the client bits for the SSH transport. Test output changes because the initial CBOR map holding the overall response state is now always handled internally by the response object. Differential Revision: https://phab.mercurial-scm.org/D4474
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 29 Aug 2018 15:17:11 -0700
parents 331ab85e910b
children 2372284d9457
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
38797
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
1 # unit tests for mercuril.util utilities
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
2 from __future__ import absolute_import
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
3
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
4 import contextlib
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
5 import itertools
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
6 import unittest
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
7
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
8 from mercurial import pycompat, util, utils
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
9
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
10 @contextlib.contextmanager
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
11 def mocktimer(incr=0.1, *additional_targets):
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
12 """Replaces util.timer and additional_targets with a mock
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
13
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
14 The timer starts at 0. On each call the time incremented by the value
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
15 of incr. If incr is an iterable, then the time is incremented by the
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
16 next value from that iterable, looping in a cycle when reaching the end.
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
17
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
18 additional_targets must be a sequence of (object, attribute_name) tuples;
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
19 the mock is set with setattr(object, attribute_name, mock).
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
20
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
21 """
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
22 time = [0]
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
23 try:
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
24 incr = itertools.cycle(incr)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
25 except TypeError:
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
26 incr = itertools.repeat(incr)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
27
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
28 def timer():
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
29 time[0] += next(incr)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
30 return time[0]
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
31
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
32 # record original values
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
33 orig = util.timer
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
34 additional_origs = [(o, a, getattr(o, a)) for o, a in additional_targets]
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
35
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
36 # mock out targets
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
37 util.timer = timer
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
38 for obj, attr in additional_targets:
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
39 setattr(obj, attr, timer)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
40
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
41 try:
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
42 yield
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
43 finally:
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
44 # restore originals
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
45 util.timer = orig
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
46 for args in additional_origs:
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
47 setattr(*args)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
48
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
49 # attr.s default factory for util.timedstats.start binds the timer we
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
50 # need to mock out.
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
51 _start_default = (util.timedcmstats.start.default, 'factory')
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
52
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
53 @contextlib.contextmanager
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
54 def capturestderr():
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
55 """Replace utils.procutil.stderr with a pycompat.bytesio instance
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
56
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
57 The instance is made available as the return value of __enter__.
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
58
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
59 This contextmanager is reentrant.
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
60
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
61 """
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
62 orig = utils.procutil.stderr
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
63 utils.procutil.stderr = pycompat.bytesio()
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
64 try:
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
65 yield utils.procutil.stderr
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
66 finally:
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
67 utils.procutil.stderr = orig
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
68
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
69 class timedtests(unittest.TestCase):
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
70 def testtimedcmstatsstr(self):
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
71 stats = util.timedcmstats()
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
72 self.assertEqual(str(stats), '<unknown>')
38812
9d49bb117dde util: make new timedcmstats class Python 3 compatible
Martijn Pieters <mj@zopatista.com>
parents: 38797
diff changeset
73 self.assertEqual(bytes(stats), b'<unknown>')
38797
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
74 stats.elapsed = 12.34
38812
9d49bb117dde util: make new timedcmstats class Python 3 compatible
Martijn Pieters <mj@zopatista.com>
parents: 38797
diff changeset
75 self.assertEqual(str(stats), pycompat.sysstr(util.timecount(12.34)))
9d49bb117dde util: make new timedcmstats class Python 3 compatible
Martijn Pieters <mj@zopatista.com>
parents: 38797
diff changeset
76 self.assertEqual(bytes(stats), util.timecount(12.34))
38797
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
77
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
78 def testtimedcmcleanexit(self):
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
79 # timestamps 1, 4, elapsed time of 4 - 1 = 3
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
80 with mocktimer([1, 3], _start_default):
39258
331ab85e910b cleanup: make all uses of timedcm specify what they're timing
Augie Fackler <augie@google.com>
parents: 38812
diff changeset
81 with util.timedcm('pass') as stats:
38797
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
82 # actual context doesn't matter
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
83 pass
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
84
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
85 self.assertEqual(stats.start, 1)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
86 self.assertEqual(stats.elapsed, 3)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
87 self.assertEqual(stats.level, 1)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
88
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
89 def testtimedcmnested(self):
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
90 # timestamps 1, 3, 6, 10, elapsed times of 6 - 3 = 3 and 10 - 1 = 9
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
91 with mocktimer([1, 2, 3, 4], _start_default):
39258
331ab85e910b cleanup: make all uses of timedcm specify what they're timing
Augie Fackler <augie@google.com>
parents: 38812
diff changeset
92 with util.timedcm('outer') as outer_stats:
331ab85e910b cleanup: make all uses of timedcm specify what they're timing
Augie Fackler <augie@google.com>
parents: 38812
diff changeset
93 with util.timedcm('inner') as inner_stats:
38797
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
94 # actual context doesn't matter
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
95 pass
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
96
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
97 self.assertEqual(outer_stats.start, 1)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
98 self.assertEqual(outer_stats.elapsed, 9)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
99 self.assertEqual(outer_stats.level, 1)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
100
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
101 self.assertEqual(inner_stats.start, 3)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
102 self.assertEqual(inner_stats.elapsed, 3)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
103 self.assertEqual(inner_stats.level, 2)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
104
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
105 def testtimedcmexception(self):
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
106 # timestamps 1, 4, elapsed time of 4 - 1 = 3
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
107 with mocktimer([1, 3], _start_default):
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
108 try:
39258
331ab85e910b cleanup: make all uses of timedcm specify what they're timing
Augie Fackler <augie@google.com>
parents: 38812
diff changeset
109 with util.timedcm('exceptional') as stats:
38797
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
110 raise ValueError()
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
111 except ValueError:
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
112 pass
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
113
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
114 self.assertEqual(stats.start, 1)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
115 self.assertEqual(stats.elapsed, 3)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
116 self.assertEqual(stats.level, 1)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
117
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
118 def testtimeddecorator(self):
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
119 @util.timed
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
120 def testfunc(callcount=1):
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
121 callcount -= 1
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
122 if callcount:
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
123 testfunc(callcount)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
124
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
125 # timestamps 1, 2, 3, 4, elapsed time of 3 - 2 = 1 and 4 - 1 = 3
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
126 with mocktimer(1, _start_default):
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
127 with capturestderr() as out:
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
128 testfunc(2)
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
129
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
130 self.assertEqual(out.getvalue(), (
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
131 b' testfunc: 1.000 s\n'
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
132 b' testfunc: 3.000 s\n'
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
133 ))
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
134
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
135 if __name__ == '__main__':
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
136 import silenttestrunner
8751d1e2a7ff util: create a context manager to handle timing
Martijn Pieters <mj@zopatista.com>
parents:
diff changeset
137 silenttestrunner.main(__name__)