##// END OF EJS Templates
tests: migrate test-wireproto-serverreactor.py to our internal CBOR...
Augie Fackler -
r41172:a181a1c8 default
parent child Browse files
Show More
@@ -1,633 +1,630
1 from __future__ import absolute_import, print_function
1 from __future__ import absolute_import, print_function
2
2
3 import unittest
3 import unittest
4
4
5 from mercurial.thirdparty import (
6 cbor,
7 )
8 from mercurial import (
5 from mercurial import (
9 ui as uimod,
6 ui as uimod,
10 util,
7 util,
11 wireprotoframing as framing,
8 wireprotoframing as framing,
12 )
9 )
13 from mercurial.utils import (
10 from mercurial.utils import (
14 cborutil,
11 cborutil,
15 )
12 )
16
13
17 ffs = framing.makeframefromhumanstring
14 ffs = framing.makeframefromhumanstring
18
15
19 OK = cbor.dumps({b'status': b'ok'})
16 OK = b''.join(cborutil.streamencode({b'status': b'ok'}))
20
17
21 def makereactor(deferoutput=False):
18 def makereactor(deferoutput=False):
22 ui = uimod.ui()
19 ui = uimod.ui()
23 return framing.serverreactor(ui, deferoutput=deferoutput)
20 return framing.serverreactor(ui, deferoutput=deferoutput)
24
21
25 def sendframes(reactor, gen):
22 def sendframes(reactor, gen):
26 """Send a generator of frame bytearray to a reactor.
23 """Send a generator of frame bytearray to a reactor.
27
24
28 Emits a generator of results from ``onframerecv()`` calls.
25 Emits a generator of results from ``onframerecv()`` calls.
29 """
26 """
30 for frame in gen:
27 for frame in gen:
31 header = framing.parseheader(frame)
28 header = framing.parseheader(frame)
32 payload = frame[framing.FRAME_HEADER_SIZE:]
29 payload = frame[framing.FRAME_HEADER_SIZE:]
33 assert len(payload) == header.length
30 assert len(payload) == header.length
34
31
35 yield reactor.onframerecv(framing.frame(header.requestid,
32 yield reactor.onframerecv(framing.frame(header.requestid,
36 header.streamid,
33 header.streamid,
37 header.streamflags,
34 header.streamflags,
38 header.typeid,
35 header.typeid,
39 header.flags,
36 header.flags,
40 payload))
37 payload))
41
38
42 def sendcommandframes(reactor, stream, rid, cmd, args, datafh=None):
39 def sendcommandframes(reactor, stream, rid, cmd, args, datafh=None):
43 """Generate frames to run a command and send them to a reactor."""
40 """Generate frames to run a command and send them to a reactor."""
44 return sendframes(reactor,
41 return sendframes(reactor,
45 framing.createcommandframes(stream, rid, cmd, args,
42 framing.createcommandframes(stream, rid, cmd, args,
46 datafh))
43 datafh))
47
44
48
45
49 class ServerReactorTests(unittest.TestCase):
46 class ServerReactorTests(unittest.TestCase):
50 def _sendsingleframe(self, reactor, f):
47 def _sendsingleframe(self, reactor, f):
51 results = list(sendframes(reactor, [f]))
48 results = list(sendframes(reactor, [f]))
52 self.assertEqual(len(results), 1)
49 self.assertEqual(len(results), 1)
53
50
54 return results[0]
51 return results[0]
55
52
56 def assertaction(self, res, expected):
53 def assertaction(self, res, expected):
57 self.assertIsInstance(res, tuple)
54 self.assertIsInstance(res, tuple)
58 self.assertEqual(len(res), 2)
55 self.assertEqual(len(res), 2)
59 self.assertIsInstance(res[1], dict)
56 self.assertIsInstance(res[1], dict)
60 self.assertEqual(res[0], expected)
57 self.assertEqual(res[0], expected)
61
58
62 def assertframesequal(self, frames, framestrings):
59 def assertframesequal(self, frames, framestrings):
63 expected = [ffs(s) for s in framestrings]
60 expected = [ffs(s) for s in framestrings]
64 self.assertEqual(list(frames), expected)
61 self.assertEqual(list(frames), expected)
65
62
66 def test1framecommand(self):
63 def test1framecommand(self):
67 """Receiving a command in a single frame yields request to run it."""
64 """Receiving a command in a single frame yields request to run it."""
68 reactor = makereactor()
65 reactor = makereactor()
69 stream = framing.stream(1)
66 stream = framing.stream(1)
70 results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {}))
67 results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {}))
71 self.assertEqual(len(results), 1)
68 self.assertEqual(len(results), 1)
72 self.assertaction(results[0], b'runcommand')
69 self.assertaction(results[0], b'runcommand')
73 self.assertEqual(results[0][1], {
70 self.assertEqual(results[0][1], {
74 b'requestid': 1,
71 b'requestid': 1,
75 b'command': b'mycommand',
72 b'command': b'mycommand',
76 b'args': {},
73 b'args': {},
77 b'redirect': None,
74 b'redirect': None,
78 b'data': None,
75 b'data': None,
79 })
76 })
80
77
81 result = reactor.oninputeof()
78 result = reactor.oninputeof()
82 self.assertaction(result, b'noop')
79 self.assertaction(result, b'noop')
83
80
84 def test1argument(self):
81 def test1argument(self):
85 reactor = makereactor()
82 reactor = makereactor()
86 stream = framing.stream(1)
83 stream = framing.stream(1)
87 results = list(sendcommandframes(reactor, stream, 41, b'mycommand',
84 results = list(sendcommandframes(reactor, stream, 41, b'mycommand',
88 {b'foo': b'bar'}))
85 {b'foo': b'bar'}))
89 self.assertEqual(len(results), 1)
86 self.assertEqual(len(results), 1)
90 self.assertaction(results[0], b'runcommand')
87 self.assertaction(results[0], b'runcommand')
91 self.assertEqual(results[0][1], {
88 self.assertEqual(results[0][1], {
92 b'requestid': 41,
89 b'requestid': 41,
93 b'command': b'mycommand',
90 b'command': b'mycommand',
94 b'args': {b'foo': b'bar'},
91 b'args': {b'foo': b'bar'},
95 b'redirect': None,
92 b'redirect': None,
96 b'data': None,
93 b'data': None,
97 })
94 })
98
95
99 def testmultiarguments(self):
96 def testmultiarguments(self):
100 reactor = makereactor()
97 reactor = makereactor()
101 stream = framing.stream(1)
98 stream = framing.stream(1)
102 results = list(sendcommandframes(reactor, stream, 1, b'mycommand',
99 results = list(sendcommandframes(reactor, stream, 1, b'mycommand',
103 {b'foo': b'bar', b'biz': b'baz'}))
100 {b'foo': b'bar', b'biz': b'baz'}))
104 self.assertEqual(len(results), 1)
101 self.assertEqual(len(results), 1)
105 self.assertaction(results[0], b'runcommand')
102 self.assertaction(results[0], b'runcommand')
106 self.assertEqual(results[0][1], {
103 self.assertEqual(results[0][1], {
107 b'requestid': 1,
104 b'requestid': 1,
108 b'command': b'mycommand',
105 b'command': b'mycommand',
109 b'args': {b'foo': b'bar', b'biz': b'baz'},
106 b'args': {b'foo': b'bar', b'biz': b'baz'},
110 b'redirect': None,
107 b'redirect': None,
111 b'data': None,
108 b'data': None,
112 })
109 })
113
110
114 def testsimplecommanddata(self):
111 def testsimplecommanddata(self):
115 reactor = makereactor()
112 reactor = makereactor()
116 stream = framing.stream(1)
113 stream = framing.stream(1)
117 results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {},
114 results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {},
118 util.bytesio(b'data!')))
115 util.bytesio(b'data!')))
119 self.assertEqual(len(results), 2)
116 self.assertEqual(len(results), 2)
120 self.assertaction(results[0], b'wantframe')
117 self.assertaction(results[0], b'wantframe')
121 self.assertaction(results[1], b'runcommand')
118 self.assertaction(results[1], b'runcommand')
122 self.assertEqual(results[1][1], {
119 self.assertEqual(results[1][1], {
123 b'requestid': 1,
120 b'requestid': 1,
124 b'command': b'mycommand',
121 b'command': b'mycommand',
125 b'args': {},
122 b'args': {},
126 b'redirect': None,
123 b'redirect': None,
127 b'data': b'data!',
124 b'data': b'data!',
128 })
125 })
129
126
130 def testmultipledataframes(self):
127 def testmultipledataframes(self):
131 frames = [
128 frames = [
132 ffs(b'1 1 stream-begin command-request new|have-data '
129 ffs(b'1 1 stream-begin command-request new|have-data '
133 b"cbor:{b'name': b'mycommand'}"),
130 b"cbor:{b'name': b'mycommand'}"),
134 ffs(b'1 1 0 command-data continuation data1'),
131 ffs(b'1 1 0 command-data continuation data1'),
135 ffs(b'1 1 0 command-data continuation data2'),
132 ffs(b'1 1 0 command-data continuation data2'),
136 ffs(b'1 1 0 command-data eos data3'),
133 ffs(b'1 1 0 command-data eos data3'),
137 ]
134 ]
138
135
139 reactor = makereactor()
136 reactor = makereactor()
140 results = list(sendframes(reactor, frames))
137 results = list(sendframes(reactor, frames))
141 self.assertEqual(len(results), 4)
138 self.assertEqual(len(results), 4)
142 for i in range(3):
139 for i in range(3):
143 self.assertaction(results[i], b'wantframe')
140 self.assertaction(results[i], b'wantframe')
144 self.assertaction(results[3], b'runcommand')
141 self.assertaction(results[3], b'runcommand')
145 self.assertEqual(results[3][1], {
142 self.assertEqual(results[3][1], {
146 b'requestid': 1,
143 b'requestid': 1,
147 b'command': b'mycommand',
144 b'command': b'mycommand',
148 b'args': {},
145 b'args': {},
149 b'redirect': None,
146 b'redirect': None,
150 b'data': b'data1data2data3',
147 b'data': b'data1data2data3',
151 })
148 })
152
149
153 def testargumentanddata(self):
150 def testargumentanddata(self):
154 frames = [
151 frames = [
155 ffs(b'1 1 stream-begin command-request new|have-data '
152 ffs(b'1 1 stream-begin command-request new|have-data '
156 b"cbor:{b'name': b'command', b'args': {b'key': b'val',"
153 b"cbor:{b'name': b'command', b'args': {b'key': b'val',"
157 b"b'foo': b'bar'}}"),
154 b"b'foo': b'bar'}}"),
158 ffs(b'1 1 0 command-data continuation value1'),
155 ffs(b'1 1 0 command-data continuation value1'),
159 ffs(b'1 1 0 command-data eos value2'),
156 ffs(b'1 1 0 command-data eos value2'),
160 ]
157 ]
161
158
162 reactor = makereactor()
159 reactor = makereactor()
163 results = list(sendframes(reactor, frames))
160 results = list(sendframes(reactor, frames))
164
161
165 self.assertaction(results[-1], b'runcommand')
162 self.assertaction(results[-1], b'runcommand')
166 self.assertEqual(results[-1][1], {
163 self.assertEqual(results[-1][1], {
167 b'requestid': 1,
164 b'requestid': 1,
168 b'command': b'command',
165 b'command': b'command',
169 b'args': {
166 b'args': {
170 b'key': b'val',
167 b'key': b'val',
171 b'foo': b'bar',
168 b'foo': b'bar',
172 },
169 },
173 b'redirect': None,
170 b'redirect': None,
174 b'data': b'value1value2',
171 b'data': b'value1value2',
175 })
172 })
176
173
177 def testnewandcontinuation(self):
174 def testnewandcontinuation(self):
178 result = self._sendsingleframe(makereactor(),
175 result = self._sendsingleframe(makereactor(),
179 ffs(b'1 1 stream-begin command-request new|continuation '))
176 ffs(b'1 1 stream-begin command-request new|continuation '))
180 self.assertaction(result, b'error')
177 self.assertaction(result, b'error')
181 self.assertEqual(result[1], {
178 self.assertEqual(result[1], {
182 b'message': b'received command request frame with both new and '
179 b'message': b'received command request frame with both new and '
183 b'continuation flags set',
180 b'continuation flags set',
184 })
181 })
185
182
186 def testneithernewnorcontinuation(self):
183 def testneithernewnorcontinuation(self):
187 result = self._sendsingleframe(makereactor(),
184 result = self._sendsingleframe(makereactor(),
188 ffs(b'1 1 stream-begin command-request 0 '))
185 ffs(b'1 1 stream-begin command-request 0 '))
189 self.assertaction(result, b'error')
186 self.assertaction(result, b'error')
190 self.assertEqual(result[1], {
187 self.assertEqual(result[1], {
191 b'message': b'received command request frame with neither new nor '
188 b'message': b'received command request frame with neither new nor '
192 b'continuation flags set',
189 b'continuation flags set',
193 })
190 })
194
191
195 def testunexpectedcommanddata(self):
192 def testunexpectedcommanddata(self):
196 """Command data frame when not running a command is an error."""
193 """Command data frame when not running a command is an error."""
197 result = self._sendsingleframe(makereactor(),
194 result = self._sendsingleframe(makereactor(),
198 ffs(b'1 1 stream-begin command-data 0 ignored'))
195 ffs(b'1 1 stream-begin command-data 0 ignored'))
199 self.assertaction(result, b'error')
196 self.assertaction(result, b'error')
200 self.assertEqual(result[1], {
197 self.assertEqual(result[1], {
201 b'message': b'expected sender protocol settings or command request '
198 b'message': b'expected sender protocol settings or command request '
202 b'frame; got 2',
199 b'frame; got 2',
203 })
200 })
204
201
205 def testunexpectedcommanddatareceiving(self):
202 def testunexpectedcommanddatareceiving(self):
206 """Same as above except the command is receiving."""
203 """Same as above except the command is receiving."""
207 results = list(sendframes(makereactor(), [
204 results = list(sendframes(makereactor(), [
208 ffs(b'1 1 stream-begin command-request new|more '
205 ffs(b'1 1 stream-begin command-request new|more '
209 b"cbor:{b'name': b'ignored'}"),
206 b"cbor:{b'name': b'ignored'}"),
210 ffs(b'1 1 0 command-data eos ignored'),
207 ffs(b'1 1 0 command-data eos ignored'),
211 ]))
208 ]))
212
209
213 self.assertaction(results[0], b'wantframe')
210 self.assertaction(results[0], b'wantframe')
214 self.assertaction(results[1], b'error')
211 self.assertaction(results[1], b'error')
215 self.assertEqual(results[1][1], {
212 self.assertEqual(results[1][1], {
216 b'message': b'received command data frame for request that is not '
213 b'message': b'received command data frame for request that is not '
217 b'expecting data: 1',
214 b'expecting data: 1',
218 })
215 })
219
216
220 def testconflictingrequestidallowed(self):
217 def testconflictingrequestidallowed(self):
221 """Multiple fully serviced commands with same request ID is allowed."""
218 """Multiple fully serviced commands with same request ID is allowed."""
222 reactor = makereactor()
219 reactor = makereactor()
223 results = []
220 results = []
224 outstream = reactor.makeoutputstream()
221 outstream = reactor.makeoutputstream()
225 results.append(self._sendsingleframe(
222 results.append(self._sendsingleframe(
226 reactor, ffs(b'1 1 stream-begin command-request new '
223 reactor, ffs(b'1 1 stream-begin command-request new '
227 b"cbor:{b'name': b'command'}")))
224 b"cbor:{b'name': b'command'}")))
228 result = reactor.oncommandresponsereadyobjects(
225 result = reactor.oncommandresponsereadyobjects(
229 outstream, 1, [b'response1'])
226 outstream, 1, [b'response1'])
230 self.assertaction(result, b'sendframes')
227 self.assertaction(result, b'sendframes')
231 list(result[1][b'framegen'])
228 list(result[1][b'framegen'])
232 results.append(self._sendsingleframe(
229 results.append(self._sendsingleframe(
233 reactor, ffs(b'1 1 stream-begin command-request new '
230 reactor, ffs(b'1 1 stream-begin command-request new '
234 b"cbor:{b'name': b'command'}")))
231 b"cbor:{b'name': b'command'}")))
235 result = reactor.oncommandresponsereadyobjects(
232 result = reactor.oncommandresponsereadyobjects(
236 outstream, 1, [b'response2'])
233 outstream, 1, [b'response2'])
237 self.assertaction(result, b'sendframes')
234 self.assertaction(result, b'sendframes')
238 list(result[1][b'framegen'])
235 list(result[1][b'framegen'])
239 results.append(self._sendsingleframe(
236 results.append(self._sendsingleframe(
240 reactor, ffs(b'1 1 stream-begin command-request new '
237 reactor, ffs(b'1 1 stream-begin command-request new '
241 b"cbor:{b'name': b'command'}")))
238 b"cbor:{b'name': b'command'}")))
242 result = reactor.oncommandresponsereadyobjects(
239 result = reactor.oncommandresponsereadyobjects(
243 outstream, 1, [b'response3'])
240 outstream, 1, [b'response3'])
244 self.assertaction(result, b'sendframes')
241 self.assertaction(result, b'sendframes')
245 list(result[1][b'framegen'])
242 list(result[1][b'framegen'])
246
243
247 for i in range(3):
244 for i in range(3):
248 self.assertaction(results[i], b'runcommand')
245 self.assertaction(results[i], b'runcommand')
249 self.assertEqual(results[i][1], {
246 self.assertEqual(results[i][1], {
250 b'requestid': 1,
247 b'requestid': 1,
251 b'command': b'command',
248 b'command': b'command',
252 b'args': {},
249 b'args': {},
253 b'redirect': None,
250 b'redirect': None,
254 b'data': None,
251 b'data': None,
255 })
252 })
256
253
257 def testconflictingrequestid(self):
254 def testconflictingrequestid(self):
258 """Request ID for new command matching in-flight command is illegal."""
255 """Request ID for new command matching in-flight command is illegal."""
259 results = list(sendframes(makereactor(), [
256 results = list(sendframes(makereactor(), [
260 ffs(b'1 1 stream-begin command-request new|more '
257 ffs(b'1 1 stream-begin command-request new|more '
261 b"cbor:{b'name': b'command'}"),
258 b"cbor:{b'name': b'command'}"),
262 ffs(b'1 1 0 command-request new '
259 ffs(b'1 1 0 command-request new '
263 b"cbor:{b'name': b'command1'}"),
260 b"cbor:{b'name': b'command1'}"),
264 ]))
261 ]))
265
262
266 self.assertaction(results[0], b'wantframe')
263 self.assertaction(results[0], b'wantframe')
267 self.assertaction(results[1], b'error')
264 self.assertaction(results[1], b'error')
268 self.assertEqual(results[1][1], {
265 self.assertEqual(results[1][1], {
269 b'message': b'request with ID 1 already received',
266 b'message': b'request with ID 1 already received',
270 })
267 })
271
268
272 def testinterleavedcommands(self):
269 def testinterleavedcommands(self):
273 cbor1 = cbor.dumps({
270 cbor1 = b''.join(cborutil.streamencode({
274 b'name': b'command1',
271 b'name': b'command1',
275 b'args': {
272 b'args': {
276 b'foo': b'bar',
273 b'foo': b'bar',
277 b'key1': b'val',
274 b'key1': b'val',
278 }
275 }
279 }, canonical=True)
276 }))
280 cbor3 = cbor.dumps({
277 cbor3 = b''.join(cborutil.streamencode({
281 b'name': b'command3',
278 b'name': b'command3',
282 b'args': {
279 b'args': {
283 b'biz': b'baz',
280 b'biz': b'baz',
284 b'key': b'val',
281 b'key': b'val',
285 },
282 },
286 }, canonical=True)
283 }))
287
284
288 results = list(sendframes(makereactor(), [
285 results = list(sendframes(makereactor(), [
289 ffs(b'1 1 stream-begin command-request new|more %s' % cbor1[0:6]),
286 ffs(b'1 1 stream-begin command-request new|more %s' % cbor1[0:6]),
290 ffs(b'3 1 0 command-request new|more %s' % cbor3[0:10]),
287 ffs(b'3 1 0 command-request new|more %s' % cbor3[0:10]),
291 ffs(b'1 1 0 command-request continuation|more %s' % cbor1[6:9]),
288 ffs(b'1 1 0 command-request continuation|more %s' % cbor1[6:9]),
292 ffs(b'3 1 0 command-request continuation|more %s' % cbor3[10:13]),
289 ffs(b'3 1 0 command-request continuation|more %s' % cbor3[10:13]),
293 ffs(b'3 1 0 command-request continuation %s' % cbor3[13:]),
290 ffs(b'3 1 0 command-request continuation %s' % cbor3[13:]),
294 ffs(b'1 1 0 command-request continuation %s' % cbor1[9:]),
291 ffs(b'1 1 0 command-request continuation %s' % cbor1[9:]),
295 ]))
292 ]))
296
293
297 self.assertEqual([t[0] for t in results], [
294 self.assertEqual([t[0] for t in results], [
298 b'wantframe',
295 b'wantframe',
299 b'wantframe',
296 b'wantframe',
300 b'wantframe',
297 b'wantframe',
301 b'wantframe',
298 b'wantframe',
302 b'runcommand',
299 b'runcommand',
303 b'runcommand',
300 b'runcommand',
304 ])
301 ])
305
302
306 self.assertEqual(results[4][1], {
303 self.assertEqual(results[4][1], {
307 b'requestid': 3,
304 b'requestid': 3,
308 b'command': b'command3',
305 b'command': b'command3',
309 b'args': {b'biz': b'baz', b'key': b'val'},
306 b'args': {b'biz': b'baz', b'key': b'val'},
310 b'redirect': None,
307 b'redirect': None,
311 b'data': None,
308 b'data': None,
312 })
309 })
313 self.assertEqual(results[5][1], {
310 self.assertEqual(results[5][1], {
314 b'requestid': 1,
311 b'requestid': 1,
315 b'command': b'command1',
312 b'command': b'command1',
316 b'args': {b'foo': b'bar', b'key1': b'val'},
313 b'args': {b'foo': b'bar', b'key1': b'val'},
317 b'redirect': None,
314 b'redirect': None,
318 b'data': None,
315 b'data': None,
319 })
316 })
320
317
321 def testmissingcommanddataframe(self):
318 def testmissingcommanddataframe(self):
322 # The reactor doesn't currently handle partially received commands.
319 # The reactor doesn't currently handle partially received commands.
323 # So this test is failing to do anything with request 1.
320 # So this test is failing to do anything with request 1.
324 frames = [
321 frames = [
325 ffs(b'1 1 stream-begin command-request new|have-data '
322 ffs(b'1 1 stream-begin command-request new|have-data '
326 b"cbor:{b'name': b'command1'}"),
323 b"cbor:{b'name': b'command1'}"),
327 ffs(b'3 1 0 command-request new '
324 ffs(b'3 1 0 command-request new '
328 b"cbor:{b'name': b'command2'}"),
325 b"cbor:{b'name': b'command2'}"),
329 ]
326 ]
330 results = list(sendframes(makereactor(), frames))
327 results = list(sendframes(makereactor(), frames))
331 self.assertEqual(len(results), 2)
328 self.assertEqual(len(results), 2)
332 self.assertaction(results[0], b'wantframe')
329 self.assertaction(results[0], b'wantframe')
333 self.assertaction(results[1], b'runcommand')
330 self.assertaction(results[1], b'runcommand')
334
331
335 def testmissingcommanddataframeflags(self):
332 def testmissingcommanddataframeflags(self):
336 frames = [
333 frames = [
337 ffs(b'1 1 stream-begin command-request new|have-data '
334 ffs(b'1 1 stream-begin command-request new|have-data '
338 b"cbor:{b'name': b'command1'}"),
335 b"cbor:{b'name': b'command1'}"),
339 ffs(b'1 1 0 command-data 0 data'),
336 ffs(b'1 1 0 command-data 0 data'),
340 ]
337 ]
341 results = list(sendframes(makereactor(), frames))
338 results = list(sendframes(makereactor(), frames))
342 self.assertEqual(len(results), 2)
339 self.assertEqual(len(results), 2)
343 self.assertaction(results[0], b'wantframe')
340 self.assertaction(results[0], b'wantframe')
344 self.assertaction(results[1], b'error')
341 self.assertaction(results[1], b'error')
345 self.assertEqual(results[1][1], {
342 self.assertEqual(results[1][1], {
346 b'message': b'command data frame without flags',
343 b'message': b'command data frame without flags',
347 })
344 })
348
345
349 def testframefornonreceivingrequest(self):
346 def testframefornonreceivingrequest(self):
350 """Receiving a frame for a command that is not receiving is illegal."""
347 """Receiving a frame for a command that is not receiving is illegal."""
351 results = list(sendframes(makereactor(), [
348 results = list(sendframes(makereactor(), [
352 ffs(b'1 1 stream-begin command-request new '
349 ffs(b'1 1 stream-begin command-request new '
353 b"cbor:{b'name': b'command1'}"),
350 b"cbor:{b'name': b'command1'}"),
354 ffs(b'3 1 0 command-request new|have-data '
351 ffs(b'3 1 0 command-request new|have-data '
355 b"cbor:{b'name': b'command3'}"),
352 b"cbor:{b'name': b'command3'}"),
356 ffs(b'5 1 0 command-data eos ignored'),
353 ffs(b'5 1 0 command-data eos ignored'),
357 ]))
354 ]))
358 self.assertaction(results[2], b'error')
355 self.assertaction(results[2], b'error')
359 self.assertEqual(results[2][1], {
356 self.assertEqual(results[2][1], {
360 b'message': b'received frame for request that is not receiving: 5',
357 b'message': b'received frame for request that is not receiving: 5',
361 })
358 })
362
359
363 def testsimpleresponse(self):
360 def testsimpleresponse(self):
364 """Bytes response to command sends result frames."""
361 """Bytes response to command sends result frames."""
365 reactor = makereactor()
362 reactor = makereactor()
366 instream = framing.stream(1)
363 instream = framing.stream(1)
367 list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
364 list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
368
365
369 outstream = reactor.makeoutputstream()
366 outstream = reactor.makeoutputstream()
370 result = reactor.oncommandresponsereadyobjects(
367 result = reactor.oncommandresponsereadyobjects(
371 outstream, 1, [b'response'])
368 outstream, 1, [b'response'])
372 self.assertaction(result, b'sendframes')
369 self.assertaction(result, b'sendframes')
373 self.assertframesequal(result[1][b'framegen'], [
370 self.assertframesequal(result[1][b'framegen'], [
374 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
371 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
375 b'1 2 encoded command-response continuation %s' % OK,
372 b'1 2 encoded command-response continuation %s' % OK,
376 b'1 2 encoded command-response continuation cbor:b"response"',
373 b'1 2 encoded command-response continuation cbor:b"response"',
377 b'1 2 0 command-response eos ',
374 b'1 2 0 command-response eos ',
378 ])
375 ])
379
376
380 def testmultiframeresponse(self):
377 def testmultiframeresponse(self):
381 """Bytes response spanning multiple frames is handled."""
378 """Bytes response spanning multiple frames is handled."""
382 first = b'x' * framing.DEFAULT_MAX_FRAME_SIZE
379 first = b'x' * framing.DEFAULT_MAX_FRAME_SIZE
383 second = b'y' * 100
380 second = b'y' * 100
384
381
385 reactor = makereactor()
382 reactor = makereactor()
386 instream = framing.stream(1)
383 instream = framing.stream(1)
387 list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
384 list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
388
385
389 outstream = reactor.makeoutputstream()
386 outstream = reactor.makeoutputstream()
390 result = reactor.oncommandresponsereadyobjects(
387 result = reactor.oncommandresponsereadyobjects(
391 outstream, 1, [first + second])
388 outstream, 1, [first + second])
392 self.assertaction(result, b'sendframes')
389 self.assertaction(result, b'sendframes')
393 self.assertframesequal(result[1][b'framegen'], [
390 self.assertframesequal(result[1][b'framegen'], [
394 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
391 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
395 b'1 2 encoded command-response continuation %s' % OK,
392 b'1 2 encoded command-response continuation %s' % OK,
396 b'1 2 encoded command-response continuation Y\x80d',
393 b'1 2 encoded command-response continuation Y\x80d',
397 b'1 2 encoded command-response continuation %s' % first,
394 b'1 2 encoded command-response continuation %s' % first,
398 b'1 2 encoded command-response continuation %s' % second,
395 b'1 2 encoded command-response continuation %s' % second,
399 b'1 2 0 command-response eos '
396 b'1 2 0 command-response eos '
400 ])
397 ])
401
398
402 def testservererror(self):
399 def testservererror(self):
403 reactor = makereactor()
400 reactor = makereactor()
404 instream = framing.stream(1)
401 instream = framing.stream(1)
405 list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
402 list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
406
403
407 outstream = reactor.makeoutputstream()
404 outstream = reactor.makeoutputstream()
408 result = reactor.onservererror(outstream, 1, b'some message')
405 result = reactor.onservererror(outstream, 1, b'some message')
409 self.assertaction(result, b'sendframes')
406 self.assertaction(result, b'sendframes')
410 self.assertframesequal(result[1][b'framegen'], [
407 self.assertframesequal(result[1][b'framegen'], [
411 b"1 2 stream-begin error-response 0 "
408 b"1 2 stream-begin error-response 0 "
412 b"cbor:{b'type': b'server', "
409 b"cbor:{b'type': b'server', "
413 b"b'message': [{b'msg': b'some message'}]}",
410 b"b'message': [{b'msg': b'some message'}]}",
414 ])
411 ])
415
412
416 def test1commanddeferresponse(self):
413 def test1commanddeferresponse(self):
417 """Responses when in deferred output mode are delayed until EOF."""
414 """Responses when in deferred output mode are delayed until EOF."""
418 reactor = makereactor(deferoutput=True)
415 reactor = makereactor(deferoutput=True)
419 instream = framing.stream(1)
416 instream = framing.stream(1)
420 results = list(sendcommandframes(reactor, instream, 1, b'mycommand',
417 results = list(sendcommandframes(reactor, instream, 1, b'mycommand',
421 {}))
418 {}))
422 self.assertEqual(len(results), 1)
419 self.assertEqual(len(results), 1)
423 self.assertaction(results[0], b'runcommand')
420 self.assertaction(results[0], b'runcommand')
424
421
425 outstream = reactor.makeoutputstream()
422 outstream = reactor.makeoutputstream()
426 result = reactor.oncommandresponsereadyobjects(
423 result = reactor.oncommandresponsereadyobjects(
427 outstream, 1, [b'response'])
424 outstream, 1, [b'response'])
428 self.assertaction(result, b'noop')
425 self.assertaction(result, b'noop')
429 result = reactor.oninputeof()
426 result = reactor.oninputeof()
430 self.assertaction(result, b'sendframes')
427 self.assertaction(result, b'sendframes')
431 self.assertframesequal(result[1][b'framegen'], [
428 self.assertframesequal(result[1][b'framegen'], [
432 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
429 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
433 b'1 2 encoded command-response continuation %s' % OK,
430 b'1 2 encoded command-response continuation %s' % OK,
434 b'1 2 encoded command-response continuation cbor:b"response"',
431 b'1 2 encoded command-response continuation cbor:b"response"',
435 b'1 2 0 command-response eos ',
432 b'1 2 0 command-response eos ',
436 ])
433 ])
437
434
438 def testmultiplecommanddeferresponse(self):
435 def testmultiplecommanddeferresponse(self):
439 reactor = makereactor(deferoutput=True)
436 reactor = makereactor(deferoutput=True)
440 instream = framing.stream(1)
437 instream = framing.stream(1)
441 list(sendcommandframes(reactor, instream, 1, b'command1', {}))
438 list(sendcommandframes(reactor, instream, 1, b'command1', {}))
442 list(sendcommandframes(reactor, instream, 3, b'command2', {}))
439 list(sendcommandframes(reactor, instream, 3, b'command2', {}))
443
440
444 outstream = reactor.makeoutputstream()
441 outstream = reactor.makeoutputstream()
445 result = reactor.oncommandresponsereadyobjects(
442 result = reactor.oncommandresponsereadyobjects(
446 outstream, 1, [b'response1'])
443 outstream, 1, [b'response1'])
447 self.assertaction(result, b'noop')
444 self.assertaction(result, b'noop')
448 result = reactor.oncommandresponsereadyobjects(
445 result = reactor.oncommandresponsereadyobjects(
449 outstream, 3, [b'response2'])
446 outstream, 3, [b'response2'])
450 self.assertaction(result, b'noop')
447 self.assertaction(result, b'noop')
451 result = reactor.oninputeof()
448 result = reactor.oninputeof()
452 self.assertaction(result, b'sendframes')
449 self.assertaction(result, b'sendframes')
453 self.assertframesequal(result[1][b'framegen'], [
450 self.assertframesequal(result[1][b'framegen'], [
454 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
451 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
455 b'1 2 encoded command-response continuation %s' % OK,
452 b'1 2 encoded command-response continuation %s' % OK,
456 b'1 2 encoded command-response continuation cbor:b"response1"',
453 b'1 2 encoded command-response continuation cbor:b"response1"',
457 b'1 2 0 command-response eos ',
454 b'1 2 0 command-response eos ',
458 b'3 2 encoded command-response continuation %s' % OK,
455 b'3 2 encoded command-response continuation %s' % OK,
459 b'3 2 encoded command-response continuation cbor:b"response2"',
456 b'3 2 encoded command-response continuation cbor:b"response2"',
460 b'3 2 0 command-response eos ',
457 b'3 2 0 command-response eos ',
461 ])
458 ])
462
459
463 def testrequestidtracking(self):
460 def testrequestidtracking(self):
464 reactor = makereactor(deferoutput=True)
461 reactor = makereactor(deferoutput=True)
465 instream = framing.stream(1)
462 instream = framing.stream(1)
466 list(sendcommandframes(reactor, instream, 1, b'command1', {}))
463 list(sendcommandframes(reactor, instream, 1, b'command1', {}))
467 list(sendcommandframes(reactor, instream, 3, b'command2', {}))
464 list(sendcommandframes(reactor, instream, 3, b'command2', {}))
468 list(sendcommandframes(reactor, instream, 5, b'command3', {}))
465 list(sendcommandframes(reactor, instream, 5, b'command3', {}))
469
466
470 # Register results for commands out of order.
467 # Register results for commands out of order.
471 outstream = reactor.makeoutputstream()
468 outstream = reactor.makeoutputstream()
472 reactor.oncommandresponsereadyobjects(outstream, 3, [b'response3'])
469 reactor.oncommandresponsereadyobjects(outstream, 3, [b'response3'])
473 reactor.oncommandresponsereadyobjects(outstream, 1, [b'response1'])
470 reactor.oncommandresponsereadyobjects(outstream, 1, [b'response1'])
474 reactor.oncommandresponsereadyobjects(outstream, 5, [b'response5'])
471 reactor.oncommandresponsereadyobjects(outstream, 5, [b'response5'])
475
472
476 result = reactor.oninputeof()
473 result = reactor.oninputeof()
477 self.assertaction(result, b'sendframes')
474 self.assertaction(result, b'sendframes')
478 self.assertframesequal(result[1][b'framegen'], [
475 self.assertframesequal(result[1][b'framegen'], [
479 b'3 2 stream-begin stream-settings eos cbor:b"identity"',
476 b'3 2 stream-begin stream-settings eos cbor:b"identity"',
480 b'3 2 encoded command-response continuation %s' % OK,
477 b'3 2 encoded command-response continuation %s' % OK,
481 b'3 2 encoded command-response continuation cbor:b"response3"',
478 b'3 2 encoded command-response continuation cbor:b"response3"',
482 b'3 2 0 command-response eos ',
479 b'3 2 0 command-response eos ',
483 b'1 2 encoded command-response continuation %s' % OK,
480 b'1 2 encoded command-response continuation %s' % OK,
484 b'1 2 encoded command-response continuation cbor:b"response1"',
481 b'1 2 encoded command-response continuation cbor:b"response1"',
485 b'1 2 0 command-response eos ',
482 b'1 2 0 command-response eos ',
486 b'5 2 encoded command-response continuation %s' % OK,
483 b'5 2 encoded command-response continuation %s' % OK,
487 b'5 2 encoded command-response continuation cbor:b"response5"',
484 b'5 2 encoded command-response continuation cbor:b"response5"',
488 b'5 2 0 command-response eos ',
485 b'5 2 0 command-response eos ',
489 ])
486 ])
490
487
491 def testduplicaterequestonactivecommand(self):
488 def testduplicaterequestonactivecommand(self):
492 """Receiving a request ID that matches a request that isn't finished."""
489 """Receiving a request ID that matches a request that isn't finished."""
493 reactor = makereactor()
490 reactor = makereactor()
494 stream = framing.stream(1)
491 stream = framing.stream(1)
495 list(sendcommandframes(reactor, stream, 1, b'command1', {}))
492 list(sendcommandframes(reactor, stream, 1, b'command1', {}))
496 results = list(sendcommandframes(reactor, stream, 1, b'command1', {}))
493 results = list(sendcommandframes(reactor, stream, 1, b'command1', {}))
497
494
498 self.assertaction(results[0], b'error')
495 self.assertaction(results[0], b'error')
499 self.assertEqual(results[0][1], {
496 self.assertEqual(results[0][1], {
500 b'message': b'request with ID 1 is already active',
497 b'message': b'request with ID 1 is already active',
501 })
498 })
502
499
503 def testduplicaterequestonactivecommandnosend(self):
500 def testduplicaterequestonactivecommandnosend(self):
504 """Same as above but we've registered a response but haven't sent it."""
501 """Same as above but we've registered a response but haven't sent it."""
505 reactor = makereactor()
502 reactor = makereactor()
506 instream = framing.stream(1)
503 instream = framing.stream(1)
507 list(sendcommandframes(reactor, instream, 1, b'command1', {}))
504 list(sendcommandframes(reactor, instream, 1, b'command1', {}))
508 outstream = reactor.makeoutputstream()
505 outstream = reactor.makeoutputstream()
509 reactor.oncommandresponsereadyobjects(outstream, 1, [b'response'])
506 reactor.oncommandresponsereadyobjects(outstream, 1, [b'response'])
510
507
511 # We've registered the response but haven't sent it. From the
508 # We've registered the response but haven't sent it. From the
512 # perspective of the reactor, the command is still active.
509 # perspective of the reactor, the command is still active.
513
510
514 results = list(sendcommandframes(reactor, instream, 1, b'command1', {}))
511 results = list(sendcommandframes(reactor, instream, 1, b'command1', {}))
515 self.assertaction(results[0], b'error')
512 self.assertaction(results[0], b'error')
516 self.assertEqual(results[0][1], {
513 self.assertEqual(results[0][1], {
517 b'message': b'request with ID 1 is already active',
514 b'message': b'request with ID 1 is already active',
518 })
515 })
519
516
520 def testduplicaterequestaftersend(self):
517 def testduplicaterequestaftersend(self):
521 """We can use a duplicate request ID after we've sent the response."""
518 """We can use a duplicate request ID after we've sent the response."""
522 reactor = makereactor()
519 reactor = makereactor()
523 instream = framing.stream(1)
520 instream = framing.stream(1)
524 list(sendcommandframes(reactor, instream, 1, b'command1', {}))
521 list(sendcommandframes(reactor, instream, 1, b'command1', {}))
525 outstream = reactor.makeoutputstream()
522 outstream = reactor.makeoutputstream()
526 res = reactor.oncommandresponsereadyobjects(outstream, 1, [b'response'])
523 res = reactor.oncommandresponsereadyobjects(outstream, 1, [b'response'])
527 list(res[1][b'framegen'])
524 list(res[1][b'framegen'])
528
525
529 results = list(sendcommandframes(reactor, instream, 1, b'command1', {}))
526 results = list(sendcommandframes(reactor, instream, 1, b'command1', {}))
530 self.assertaction(results[0], b'runcommand')
527 self.assertaction(results[0], b'runcommand')
531
528
532 def testprotocolsettingsnoflags(self):
529 def testprotocolsettingsnoflags(self):
533 result = self._sendsingleframe(
530 result = self._sendsingleframe(
534 makereactor(),
531 makereactor(),
535 ffs(b'0 1 stream-begin sender-protocol-settings 0 '))
532 ffs(b'0 1 stream-begin sender-protocol-settings 0 '))
536 self.assertaction(result, b'error')
533 self.assertaction(result, b'error')
537 self.assertEqual(result[1], {
534 self.assertEqual(result[1], {
538 b'message': b'sender protocol settings frame must have '
535 b'message': b'sender protocol settings frame must have '
539 b'continuation or end of stream flag set',
536 b'continuation or end of stream flag set',
540 })
537 })
541
538
542 def testprotocolsettingsconflictflags(self):
539 def testprotocolsettingsconflictflags(self):
543 result = self._sendsingleframe(
540 result = self._sendsingleframe(
544 makereactor(),
541 makereactor(),
545 ffs(b'0 1 stream-begin sender-protocol-settings continuation|eos '))
542 ffs(b'0 1 stream-begin sender-protocol-settings continuation|eos '))
546 self.assertaction(result, b'error')
543 self.assertaction(result, b'error')
547 self.assertEqual(result[1], {
544 self.assertEqual(result[1], {
548 b'message': b'sender protocol settings frame cannot have both '
545 b'message': b'sender protocol settings frame cannot have both '
549 b'continuation and end of stream flags set',
546 b'continuation and end of stream flags set',
550 })
547 })
551
548
552 def testprotocolsettingsemptypayload(self):
549 def testprotocolsettingsemptypayload(self):
553 result = self._sendsingleframe(
550 result = self._sendsingleframe(
554 makereactor(),
551 makereactor(),
555 ffs(b'0 1 stream-begin sender-protocol-settings eos '))
552 ffs(b'0 1 stream-begin sender-protocol-settings eos '))
556 self.assertaction(result, b'error')
553 self.assertaction(result, b'error')
557 self.assertEqual(result[1], {
554 self.assertEqual(result[1], {
558 b'message': b'sender protocol settings frame did not contain CBOR '
555 b'message': b'sender protocol settings frame did not contain CBOR '
559 b'data',
556 b'data',
560 })
557 })
561
558
562 def testprotocolsettingsmultipleobjects(self):
559 def testprotocolsettingsmultipleobjects(self):
563 result = self._sendsingleframe(
560 result = self._sendsingleframe(
564 makereactor(),
561 makereactor(),
565 ffs(b'0 1 stream-begin sender-protocol-settings eos '
562 ffs(b'0 1 stream-begin sender-protocol-settings eos '
566 b'\x46foobar\x43foo'))
563 b'\x46foobar\x43foo'))
567 self.assertaction(result, b'error')
564 self.assertaction(result, b'error')
568 self.assertEqual(result[1], {
565 self.assertEqual(result[1], {
569 b'message': b'sender protocol settings frame contained multiple '
566 b'message': b'sender protocol settings frame contained multiple '
570 b'CBOR values',
567 b'CBOR values',
571 })
568 })
572
569
573 def testprotocolsettingscontentencodings(self):
570 def testprotocolsettingscontentencodings(self):
574 reactor = makereactor()
571 reactor = makereactor()
575
572
576 result = self._sendsingleframe(
573 result = self._sendsingleframe(
577 reactor,
574 reactor,
578 ffs(b'0 1 stream-begin sender-protocol-settings eos '
575 ffs(b'0 1 stream-begin sender-protocol-settings eos '
579 b'cbor:{b"contentencodings": [b"a", b"b"]}'))
576 b'cbor:{b"contentencodings": [b"a", b"b"]}'))
580 self.assertaction(result, b'wantframe')
577 self.assertaction(result, b'wantframe')
581
578
582 self.assertEqual(reactor._state, b'idle')
579 self.assertEqual(reactor._state, b'idle')
583 self.assertEqual(reactor._sendersettings[b'contentencodings'],
580 self.assertEqual(reactor._sendersettings[b'contentencodings'],
584 [b'a', b'b'])
581 [b'a', b'b'])
585
582
586 def testprotocolsettingsmultipleframes(self):
583 def testprotocolsettingsmultipleframes(self):
587 reactor = makereactor()
584 reactor = makereactor()
588
585
589 data = b''.join(cborutil.streamencode({
586 data = b''.join(cborutil.streamencode({
590 b'contentencodings': [b'value1', b'value2'],
587 b'contentencodings': [b'value1', b'value2'],
591 }))
588 }))
592
589
593 results = list(sendframes(reactor, [
590 results = list(sendframes(reactor, [
594 ffs(b'0 1 stream-begin sender-protocol-settings continuation %s' %
591 ffs(b'0 1 stream-begin sender-protocol-settings continuation %s' %
595 data[0:5]),
592 data[0:5]),
596 ffs(b'0 1 0 sender-protocol-settings eos %s' % data[5:]),
593 ffs(b'0 1 0 sender-protocol-settings eos %s' % data[5:]),
597 ]))
594 ]))
598
595
599 self.assertEqual(len(results), 2)
596 self.assertEqual(len(results), 2)
600
597
601 self.assertaction(results[0], b'wantframe')
598 self.assertaction(results[0], b'wantframe')
602 self.assertaction(results[1], b'wantframe')
599 self.assertaction(results[1], b'wantframe')
603
600
604 self.assertEqual(reactor._state, b'idle')
601 self.assertEqual(reactor._state, b'idle')
605 self.assertEqual(reactor._sendersettings[b'contentencodings'],
602 self.assertEqual(reactor._sendersettings[b'contentencodings'],
606 [b'value1', b'value2'])
603 [b'value1', b'value2'])
607
604
608 def testprotocolsettingsbadcbor(self):
605 def testprotocolsettingsbadcbor(self):
609 result = self._sendsingleframe(
606 result = self._sendsingleframe(
610 makereactor(),
607 makereactor(),
611 ffs(b'0 1 stream-begin sender-protocol-settings eos badvalue'))
608 ffs(b'0 1 stream-begin sender-protocol-settings eos badvalue'))
612 self.assertaction(result, b'error')
609 self.assertaction(result, b'error')
613
610
614 def testprotocolsettingsnoninitial(self):
611 def testprotocolsettingsnoninitial(self):
615 # Cannot have protocol settings frames as non-initial frames.
612 # Cannot have protocol settings frames as non-initial frames.
616 reactor = makereactor()
613 reactor = makereactor()
617
614
618 stream = framing.stream(1)
615 stream = framing.stream(1)
619 results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {}))
616 results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {}))
620 self.assertEqual(len(results), 1)
617 self.assertEqual(len(results), 1)
621 self.assertaction(results[0], b'runcommand')
618 self.assertaction(results[0], b'runcommand')
622
619
623 result = self._sendsingleframe(
620 result = self._sendsingleframe(
624 reactor,
621 reactor,
625 ffs(b'0 1 0 sender-protocol-settings eos '))
622 ffs(b'0 1 0 sender-protocol-settings eos '))
626 self.assertaction(result, b'error')
623 self.assertaction(result, b'error')
627 self.assertEqual(result[1], {
624 self.assertEqual(result[1], {
628 b'message': b'expected command request frame; got 8',
625 b'message': b'expected command request frame; got 8',
629 })
626 })
630
627
631 if __name__ == '__main__':
628 if __name__ == '__main__':
632 import silenttestrunner
629 import silenttestrunner
633 silenttestrunner.main(__name__)
630 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now