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