annotate tests/test-wireproto-clientreactor.py @ 37543:01361be9e2dc

wireproto: introduce a reactor for client-side state We have a nice state machine of sorts for reacting to server-side events. Now it is time to implement the client equivalent. We introduce a "clientreactor." It allows callers to request that commands be issued. It has multiple modes of operation to reflect what the underlying transport supports. e.g. for SSH, we can perform wire sends immediately but for HTTP we need to buffer sends until all command requests are received. In addition, SSH allows sending multiple requests as long as the connection is open. But HTTP/1.1 only allows sending request data once. For SSH, we'll have one reactor per connection. For HTTP, we'll have one reactor per HTTP request. But because code that calls wire protocol commands should not be aware of how the underlying transport works, this will all be abstracted away by the peer interface. Our crude HTTP peer has been updated to use the reactor instead of formulating frames directly. No behavior should have changed here and tests seem to confirm that. Basic unit tests for the reactor behavior have been added. Differential Revision: https://phab.mercurial-scm.org/D3223
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 09 Apr 2018 15:32:01 -0700
parents
children 55b5ba8d4e68
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
37543
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
1 from __future__ import absolute_import
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
2
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
3 import unittest
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
4
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
5 from mercurial import (
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
6 error,
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
7 wireprotoframing as framing,
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
8 )
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
9
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
10 class SingleSendTests(unittest.TestCase):
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
11 """A reactor that can only send once rejects subsequent sends."""
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
12 def testbasic(self):
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
13 reactor = framing.clientreactor(hasmultiplesend=False, buffersends=True)
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
14
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
15 request, action, meta = reactor.callcommand(b'foo', {})
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
16 self.assertEqual(request.state, 'pending')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
17 self.assertEqual(action, 'noop')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
18
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
19 action, meta = reactor.flushcommands()
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
20 self.assertEqual(action, 'sendframes')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
21
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
22 for frame in meta['framegen']:
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
23 self.assertEqual(request.state, 'sending')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
24
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
25 self.assertEqual(request.state, 'sent')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
26
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
27 with self.assertRaisesRegexp(error.ProgrammingError,
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
28 'cannot issue new commands'):
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
29 reactor.callcommand(b'foo', {})
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
30
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
31 with self.assertRaisesRegexp(error.ProgrammingError,
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
32 'cannot issue new commands'):
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
33 reactor.callcommand(b'foo', {})
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
34
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
35 class NoBufferTests(unittest.TestCase):
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
36 """A reactor without send buffering sends requests immediately."""
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
37 def testbasic(self):
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
38 reactor = framing.clientreactor(hasmultiplesend=True, buffersends=False)
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
39
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
40 request, action, meta = reactor.callcommand(b'command1', {})
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
41 self.assertEqual(request.requestid, 1)
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
42 self.assertEqual(action, 'sendframes')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
43
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
44 self.assertEqual(request.state, 'pending')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
45
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
46 for frame in meta['framegen']:
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
47 self.assertEqual(request.state, 'sending')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
48
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
49 self.assertEqual(request.state, 'sent')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
50
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
51 action, meta = reactor.flushcommands()
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
52 self.assertEqual(action, 'noop')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
53
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
54 # And we can send another command.
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
55 request, action, meta = reactor.callcommand(b'command2', {})
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
56 self.assertEqual(request.requestid, 3)
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
57 self.assertEqual(action, 'sendframes')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
58
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
59 for frame in meta['framegen']:
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
60 self.assertEqual(request.state, 'sending')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
61
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
62 self.assertEqual(request.state, 'sent')
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
63
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
64 if __name__ == '__main__':
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
65 import silenttestrunner
01361be9e2dc wireproto: introduce a reactor for client-side state
Gregory Szorc <gregory.szorc@gmail.com>
parents:
diff changeset
66 silenttestrunner.main(__name__)