16 """Send a generator of frame bytearray to a reactor. |
16 """Send a generator of frame bytearray to a reactor. |
17 |
17 |
18 Emits a generator of results from ``onframerecv()`` calls. |
18 Emits a generator of results from ``onframerecv()`` calls. |
19 """ |
19 """ |
20 for frame in gen: |
20 for frame in gen: |
21 frametype, frameflags, framelength = framing.parseheader(frame) |
21 rid, frametype, frameflags, framelength = framing.parseheader(frame) |
22 payload = frame[framing.FRAME_HEADER_SIZE:] |
22 payload = frame[framing.FRAME_HEADER_SIZE:] |
23 assert len(payload) == framelength |
23 assert len(payload) == framelength |
24 |
24 |
25 yield reactor.onframerecv(frametype, frameflags, payload) |
25 yield reactor.onframerecv(rid, frametype, frameflags, payload) |
26 |
26 |
27 def sendcommandframes(reactor, cmd, args, datafh=None): |
27 def sendcommandframes(reactor, rid, cmd, args, datafh=None): |
28 """Generate frames to run a command and send them to a reactor.""" |
28 """Generate frames to run a command and send them to a reactor.""" |
29 return sendframes(reactor, framing.createcommandframes(cmd, args, datafh)) |
29 return sendframes(reactor, |
|
30 framing.createcommandframes(rid, cmd, args, datafh)) |
30 |
31 |
31 class FrameTests(unittest.TestCase): |
32 class FrameTests(unittest.TestCase): |
32 def testdataexactframesize(self): |
33 def testdataexactframesize(self): |
33 data = util.bytesio(b'x' * framing.DEFAULT_MAX_FRAME_SIZE) |
34 data = util.bytesio(b'x' * framing.DEFAULT_MAX_FRAME_SIZE) |
34 |
35 |
35 frames = list(framing.createcommandframes(b'command', {}, data)) |
36 frames = list(framing.createcommandframes(1, b'command', {}, data)) |
36 self.assertEqual(frames, [ |
37 self.assertEqual(frames, [ |
37 ffs(b'command-name have-data command'), |
38 ffs(b'1 command-name have-data command'), |
38 ffs(b'command-data continuation %s' % data.getvalue()), |
39 ffs(b'1 command-data continuation %s' % data.getvalue()), |
39 ffs(b'command-data eos ') |
40 ffs(b'1 command-data eos ') |
40 ]) |
41 ]) |
41 |
42 |
42 def testdatamultipleframes(self): |
43 def testdatamultipleframes(self): |
43 data = util.bytesio(b'x' * (framing.DEFAULT_MAX_FRAME_SIZE + 1)) |
44 data = util.bytesio(b'x' * (framing.DEFAULT_MAX_FRAME_SIZE + 1)) |
44 frames = list(framing.createcommandframes(b'command', {}, data)) |
45 frames = list(framing.createcommandframes(1, b'command', {}, data)) |
45 self.assertEqual(frames, [ |
46 self.assertEqual(frames, [ |
46 ffs(b'command-name have-data command'), |
47 ffs(b'1 command-name have-data command'), |
47 ffs(b'command-data continuation %s' % ( |
48 ffs(b'1 command-data continuation %s' % ( |
48 b'x' * framing.DEFAULT_MAX_FRAME_SIZE)), |
49 b'x' * framing.DEFAULT_MAX_FRAME_SIZE)), |
49 ffs(b'command-data eos x'), |
50 ffs(b'1 command-data eos x'), |
50 ]) |
51 ]) |
51 |
52 |
52 def testargsanddata(self): |
53 def testargsanddata(self): |
53 data = util.bytesio(b'x' * 100) |
54 data = util.bytesio(b'x' * 100) |
54 |
55 |
55 frames = list(framing.createcommandframes(b'command', { |
56 frames = list(framing.createcommandframes(1, b'command', { |
56 b'key1': b'key1value', |
57 b'key1': b'key1value', |
57 b'key2': b'key2value', |
58 b'key2': b'key2value', |
58 b'key3': b'key3value', |
59 b'key3': b'key3value', |
59 }, data)) |
60 }, data)) |
60 |
61 |
61 self.assertEqual(frames, [ |
62 self.assertEqual(frames, [ |
62 ffs(b'command-name have-args|have-data command'), |
63 ffs(b'1 command-name have-args|have-data command'), |
63 ffs(br'command-argument 0 \x04\x00\x09\x00key1key1value'), |
64 ffs(br'1 command-argument 0 \x04\x00\x09\x00key1key1value'), |
64 ffs(br'command-argument 0 \x04\x00\x09\x00key2key2value'), |
65 ffs(br'1 command-argument 0 \x04\x00\x09\x00key2key2value'), |
65 ffs(br'command-argument eoa \x04\x00\x09\x00key3key3value'), |
66 ffs(br'1 command-argument eoa \x04\x00\x09\x00key3key3value'), |
66 ffs(b'command-data eos %s' % data.getvalue()), |
67 ffs(b'1 command-data eos %s' % data.getvalue()), |
67 ]) |
68 ]) |
68 |
69 |
69 class ServerReactorTests(unittest.TestCase): |
70 class ServerReactorTests(unittest.TestCase): |
70 def _sendsingleframe(self, reactor, s): |
71 def _sendsingleframe(self, reactor, s): |
71 results = list(sendframes(reactor, [ffs(s)])) |
72 results = list(sendframes(reactor, [ffs(s)])) |
84 self.assertEqual(list(frames), expected) |
85 self.assertEqual(list(frames), expected) |
85 |
86 |
86 def test1framecommand(self): |
87 def test1framecommand(self): |
87 """Receiving a command in a single frame yields request to run it.""" |
88 """Receiving a command in a single frame yields request to run it.""" |
88 reactor = makereactor() |
89 reactor = makereactor() |
89 results = list(sendcommandframes(reactor, b'mycommand', {})) |
90 results = list(sendcommandframes(reactor, 1, b'mycommand', {})) |
90 self.assertEqual(len(results), 1) |
91 self.assertEqual(len(results), 1) |
91 self.assertaction(results[0], 'runcommand') |
92 self.assertaction(results[0], 'runcommand') |
92 self.assertEqual(results[0][1], { |
93 self.assertEqual(results[0][1], { |
|
94 'requestid': 1, |
93 'command': b'mycommand', |
95 'command': b'mycommand', |
94 'args': {}, |
96 'args': {}, |
95 'data': None, |
97 'data': None, |
96 }) |
98 }) |
97 |
99 |
98 result = reactor.oninputeof() |
100 result = reactor.oninputeof() |
99 self.assertaction(result, 'noop') |
101 self.assertaction(result, 'noop') |
100 |
102 |
101 def test1argument(self): |
103 def test1argument(self): |
102 reactor = makereactor() |
104 reactor = makereactor() |
103 results = list(sendcommandframes(reactor, b'mycommand', |
105 results = list(sendcommandframes(reactor, 41, b'mycommand', |
104 {b'foo': b'bar'})) |
106 {b'foo': b'bar'})) |
105 self.assertEqual(len(results), 2) |
107 self.assertEqual(len(results), 2) |
106 self.assertaction(results[0], 'wantframe') |
108 self.assertaction(results[0], 'wantframe') |
107 self.assertaction(results[1], 'runcommand') |
109 self.assertaction(results[1], 'runcommand') |
108 self.assertEqual(results[1][1], { |
110 self.assertEqual(results[1][1], { |
|
111 'requestid': 41, |
109 'command': b'mycommand', |
112 'command': b'mycommand', |
110 'args': {b'foo': b'bar'}, |
113 'args': {b'foo': b'bar'}, |
111 'data': None, |
114 'data': None, |
112 }) |
115 }) |
113 |
116 |
114 def testmultiarguments(self): |
117 def testmultiarguments(self): |
115 reactor = makereactor() |
118 reactor = makereactor() |
116 results = list(sendcommandframes(reactor, b'mycommand', |
119 results = list(sendcommandframes(reactor, 1, b'mycommand', |
117 {b'foo': b'bar', b'biz': b'baz'})) |
120 {b'foo': b'bar', b'biz': b'baz'})) |
118 self.assertEqual(len(results), 3) |
121 self.assertEqual(len(results), 3) |
119 self.assertaction(results[0], 'wantframe') |
122 self.assertaction(results[0], 'wantframe') |
120 self.assertaction(results[1], 'wantframe') |
123 self.assertaction(results[1], 'wantframe') |
121 self.assertaction(results[2], 'runcommand') |
124 self.assertaction(results[2], 'runcommand') |
122 self.assertEqual(results[2][1], { |
125 self.assertEqual(results[2][1], { |
|
126 'requestid': 1, |
123 'command': b'mycommand', |
127 'command': b'mycommand', |
124 'args': {b'foo': b'bar', b'biz': b'baz'}, |
128 'args': {b'foo': b'bar', b'biz': b'baz'}, |
125 'data': None, |
129 'data': None, |
126 }) |
130 }) |
127 |
131 |
128 def testsimplecommanddata(self): |
132 def testsimplecommanddata(self): |
129 reactor = makereactor() |
133 reactor = makereactor() |
130 results = list(sendcommandframes(reactor, b'mycommand', {}, |
134 results = list(sendcommandframes(reactor, 1, b'mycommand', {}, |
131 util.bytesio(b'data!'))) |
135 util.bytesio(b'data!'))) |
132 self.assertEqual(len(results), 2) |
136 self.assertEqual(len(results), 2) |
133 self.assertaction(results[0], 'wantframe') |
137 self.assertaction(results[0], 'wantframe') |
134 self.assertaction(results[1], 'runcommand') |
138 self.assertaction(results[1], 'runcommand') |
135 self.assertEqual(results[1][1], { |
139 self.assertEqual(results[1][1], { |
|
140 'requestid': 1, |
136 'command': b'mycommand', |
141 'command': b'mycommand', |
137 'args': {}, |
142 'args': {}, |
138 'data': b'data!', |
143 'data': b'data!', |
139 }) |
144 }) |
140 |
145 |
141 def testmultipledataframes(self): |
146 def testmultipledataframes(self): |
142 frames = [ |
147 frames = [ |
143 ffs(b'command-name have-data mycommand'), |
148 ffs(b'1 command-name have-data mycommand'), |
144 ffs(b'command-data continuation data1'), |
149 ffs(b'1 command-data continuation data1'), |
145 ffs(b'command-data continuation data2'), |
150 ffs(b'1 command-data continuation data2'), |
146 ffs(b'command-data eos data3'), |
151 ffs(b'1 command-data eos data3'), |
147 ] |
152 ] |
148 |
153 |
149 reactor = makereactor() |
154 reactor = makereactor() |
150 results = list(sendframes(reactor, frames)) |
155 results = list(sendframes(reactor, frames)) |
151 self.assertEqual(len(results), 4) |
156 self.assertEqual(len(results), 4) |
152 for i in range(3): |
157 for i in range(3): |
153 self.assertaction(results[i], 'wantframe') |
158 self.assertaction(results[i], 'wantframe') |
154 self.assertaction(results[3], 'runcommand') |
159 self.assertaction(results[3], 'runcommand') |
155 self.assertEqual(results[3][1], { |
160 self.assertEqual(results[3][1], { |
|
161 'requestid': 1, |
156 'command': b'mycommand', |
162 'command': b'mycommand', |
157 'args': {}, |
163 'args': {}, |
158 'data': b'data1data2data3', |
164 'data': b'data1data2data3', |
159 }) |
165 }) |
160 |
166 |
161 def testargumentanddata(self): |
167 def testargumentanddata(self): |
162 frames = [ |
168 frames = [ |
163 ffs(b'command-name have-args|have-data command'), |
169 ffs(b'1 command-name have-args|have-data command'), |
164 ffs(br'command-argument 0 \x03\x00\x03\x00keyval'), |
170 ffs(br'1 command-argument 0 \x03\x00\x03\x00keyval'), |
165 ffs(br'command-argument eoa \x03\x00\x03\x00foobar'), |
171 ffs(br'1 command-argument eoa \x03\x00\x03\x00foobar'), |
166 ffs(b'command-data continuation value1'), |
172 ffs(b'1 command-data continuation value1'), |
167 ffs(b'command-data eos value2'), |
173 ffs(b'1 command-data eos value2'), |
168 ] |
174 ] |
169 |
175 |
170 reactor = makereactor() |
176 reactor = makereactor() |
171 results = list(sendframes(reactor, frames)) |
177 results = list(sendframes(reactor, frames)) |
172 |
178 |
173 self.assertaction(results[-1], 'runcommand') |
179 self.assertaction(results[-1], 'runcommand') |
174 self.assertEqual(results[-1][1], { |
180 self.assertEqual(results[-1][1], { |
|
181 'requestid': 1, |
175 'command': b'command', |
182 'command': b'command', |
176 'args': { |
183 'args': { |
177 b'key': b'val', |
184 b'key': b'val', |
178 b'foo': b'bar', |
185 b'foo': b'bar', |
179 }, |
186 }, |
181 }) |
188 }) |
182 |
189 |
183 def testunexpectedcommandargument(self): |
190 def testunexpectedcommandargument(self): |
184 """Command argument frame when not running a command is an error.""" |
191 """Command argument frame when not running a command is an error.""" |
185 result = self._sendsingleframe(makereactor(), |
192 result = self._sendsingleframe(makereactor(), |
186 b'command-argument 0 ignored') |
193 b'1 command-argument 0 ignored') |
187 self.assertaction(result, 'error') |
194 self.assertaction(result, 'error') |
188 self.assertEqual(result[1], { |
195 self.assertEqual(result[1], { |
189 'message': b'expected command frame; got 2', |
196 'message': b'expected command frame; got 2', |
190 }) |
197 }) |
191 |
198 |
192 def testunexpectedcommanddata(self): |
199 def testunexpectedcommanddata(self): |
193 """Command argument frame when not running a command is an error.""" |
200 """Command argument frame when not running a command is an error.""" |
194 result = self._sendsingleframe(makereactor(), |
201 result = self._sendsingleframe(makereactor(), |
195 b'command-data 0 ignored') |
202 b'1 command-data 0 ignored') |
196 self.assertaction(result, 'error') |
203 self.assertaction(result, 'error') |
197 self.assertEqual(result[1], { |
204 self.assertEqual(result[1], { |
198 'message': b'expected command frame; got 3', |
205 'message': b'expected command frame; got 3', |
199 }) |
206 }) |
200 |
207 |
201 def testmissingcommandframeflags(self): |
208 def testmissingcommandframeflags(self): |
202 """Command name frame must have flags set.""" |
209 """Command name frame must have flags set.""" |
203 result = self._sendsingleframe(makereactor(), |
210 result = self._sendsingleframe(makereactor(), |
204 b'command-name 0 command') |
211 b'1 command-name 0 command') |
205 self.assertaction(result, 'error') |
212 self.assertaction(result, 'error') |
206 self.assertEqual(result[1], { |
213 self.assertEqual(result[1], { |
207 'message': b'missing frame flags on command frame', |
214 'message': b'missing frame flags on command frame', |
208 }) |
215 }) |
209 |
216 |
210 def testmissingargumentframe(self): |
217 def testmissingargumentframe(self): |
211 frames = [ |
218 frames = [ |
212 ffs(b'command-name have-args command'), |
219 ffs(b'1 command-name have-args command'), |
213 ffs(b'command-name 0 ignored'), |
220 ffs(b'1 command-name 0 ignored'), |
214 ] |
221 ] |
215 |
222 |
216 results = list(sendframes(makereactor(), frames)) |
223 results = list(sendframes(makereactor(), frames)) |
217 self.assertEqual(len(results), 2) |
224 self.assertEqual(len(results), 2) |
218 self.assertaction(results[0], 'wantframe') |
225 self.assertaction(results[0], 'wantframe') |
278 }) |
285 }) |
279 |
286 |
280 def testsimpleresponse(self): |
287 def testsimpleresponse(self): |
281 """Bytes response to command sends result frames.""" |
288 """Bytes response to command sends result frames.""" |
282 reactor = makereactor() |
289 reactor = makereactor() |
283 list(sendcommandframes(reactor, b'mycommand', {})) |
290 list(sendcommandframes(reactor, 1, b'mycommand', {})) |
284 |
291 |
285 result = reactor.onbytesresponseready(b'response') |
292 result = reactor.onbytesresponseready(1, b'response') |
286 self.assertaction(result, 'sendframes') |
293 self.assertaction(result, 'sendframes') |
287 self.assertframesequal(result[1]['framegen'], [ |
294 self.assertframesequal(result[1]['framegen'], [ |
288 b'bytes-response eos response', |
295 b'1 bytes-response eos response', |
289 ]) |
296 ]) |
290 |
297 |
291 def testmultiframeresponse(self): |
298 def testmultiframeresponse(self): |
292 """Bytes response spanning multiple frames is handled.""" |
299 """Bytes response spanning multiple frames is handled.""" |
293 first = b'x' * framing.DEFAULT_MAX_FRAME_SIZE |
300 first = b'x' * framing.DEFAULT_MAX_FRAME_SIZE |
294 second = b'y' * 100 |
301 second = b'y' * 100 |
295 |
302 |
296 reactor = makereactor() |
303 reactor = makereactor() |
297 list(sendcommandframes(reactor, b'mycommand', {})) |
304 list(sendcommandframes(reactor, 1, b'mycommand', {})) |
298 |
305 |
299 result = reactor.onbytesresponseready(first + second) |
306 result = reactor.onbytesresponseready(1, first + second) |
300 self.assertaction(result, 'sendframes') |
307 self.assertaction(result, 'sendframes') |
301 self.assertframesequal(result[1]['framegen'], [ |
308 self.assertframesequal(result[1]['framegen'], [ |
302 b'bytes-response continuation %s' % first, |
309 b'1 bytes-response continuation %s' % first, |
303 b'bytes-response eos %s' % second, |
310 b'1 bytes-response eos %s' % second, |
304 ]) |
311 ]) |
305 |
312 |
306 def testapplicationerror(self): |
313 def testapplicationerror(self): |
307 reactor = makereactor() |
314 reactor = makereactor() |
308 list(sendcommandframes(reactor, b'mycommand', {})) |
315 list(sendcommandframes(reactor, 1, b'mycommand', {})) |
309 |
316 |
310 result = reactor.onapplicationerror(b'some message') |
317 result = reactor.onapplicationerror(1, b'some message') |
311 self.assertaction(result, 'sendframes') |
318 self.assertaction(result, 'sendframes') |
312 self.assertframesequal(result[1]['framegen'], [ |
319 self.assertframesequal(result[1]['framegen'], [ |
313 b'error-response application some message', |
320 b'1 error-response application some message', |
314 ]) |
321 ]) |
315 |
322 |
316 def test1commanddeferresponse(self): |
323 def test1commanddeferresponse(self): |
317 """Responses when in deferred output mode are delayed until EOF.""" |
324 """Responses when in deferred output mode are delayed until EOF.""" |
318 reactor = makereactor(deferoutput=True) |
325 reactor = makereactor(deferoutput=True) |
319 results = list(sendcommandframes(reactor, b'mycommand', {})) |
326 results = list(sendcommandframes(reactor, 1, b'mycommand', {})) |
320 self.assertEqual(len(results), 1) |
327 self.assertEqual(len(results), 1) |
321 self.assertaction(results[0], 'runcommand') |
328 self.assertaction(results[0], 'runcommand') |
322 |
329 |
323 result = reactor.onbytesresponseready(b'response') |
330 result = reactor.onbytesresponseready(1, b'response') |
324 self.assertaction(result, 'noop') |
331 self.assertaction(result, 'noop') |
325 result = reactor.oninputeof() |
332 result = reactor.oninputeof() |
326 self.assertaction(result, 'sendframes') |
333 self.assertaction(result, 'sendframes') |
327 self.assertframesequal(result[1]['framegen'], [ |
334 self.assertframesequal(result[1]['framegen'], [ |
328 b'bytes-response eos response', |
335 b'1 bytes-response eos response', |
329 ]) |
336 ]) |
330 |
337 |
331 def testmultiplecommanddeferresponse(self): |
338 def testmultiplecommanddeferresponse(self): |
332 reactor = makereactor(deferoutput=True) |
339 reactor = makereactor(deferoutput=True) |
333 list(sendcommandframes(reactor, b'command1', {})) |
340 list(sendcommandframes(reactor, 1, b'command1', {})) |
334 list(sendcommandframes(reactor, b'command2', {})) |
341 list(sendcommandframes(reactor, 3, b'command2', {})) |
335 |
342 |
336 result = reactor.onbytesresponseready(b'response1') |
343 result = reactor.onbytesresponseready(1, b'response1') |
337 self.assertaction(result, 'noop') |
344 self.assertaction(result, 'noop') |
338 result = reactor.onbytesresponseready(b'response2') |
345 result = reactor.onbytesresponseready(3, b'response2') |
339 self.assertaction(result, 'noop') |
346 self.assertaction(result, 'noop') |
340 result = reactor.oninputeof() |
347 result = reactor.oninputeof() |
341 self.assertaction(result, 'sendframes') |
348 self.assertaction(result, 'sendframes') |
342 self.assertframesequal(result[1]['framegen'], [ |
349 self.assertframesequal(result[1]['framegen'], [ |
343 b'bytes-response eos response1', |
350 b'1 bytes-response eos response1', |
344 b'bytes-response eos response2' |
351 b'3 bytes-response eos response2' |
|
352 ]) |
|
353 |
|
354 def testrequestidtracking(self): |
|
355 reactor = makereactor(deferoutput=True) |
|
356 list(sendcommandframes(reactor, 1, b'command1', {})) |
|
357 list(sendcommandframes(reactor, 3, b'command2', {})) |
|
358 list(sendcommandframes(reactor, 5, b'command3', {})) |
|
359 |
|
360 # Register results for commands out of order. |
|
361 reactor.onbytesresponseready(3, b'response3') |
|
362 reactor.onbytesresponseready(1, b'response1') |
|
363 reactor.onbytesresponseready(5, b'response5') |
|
364 |
|
365 result = reactor.oninputeof() |
|
366 self.assertaction(result, 'sendframes') |
|
367 self.assertframesequal(result[1]['framegen'], [ |
|
368 b'3 bytes-response eos response3', |
|
369 b'1 bytes-response eos response1', |
|
370 b'5 bytes-response eos response5', |
345 ]) |
371 ]) |
346 |
372 |
347 if __name__ == '__main__': |
373 if __name__ == '__main__': |
348 import silenttestrunner |
374 import silenttestrunner |
349 silenttestrunner.main(__name__) |
375 silenttestrunner.main(__name__) |