25 yield reactor.onframerecv(framing.frame(header.requestid, |
25 yield reactor.onframerecv(framing.frame(header.requestid, |
26 header.typeid, |
26 header.typeid, |
27 header.flags, |
27 header.flags, |
28 payload)) |
28 payload)) |
29 |
29 |
30 def sendcommandframes(reactor, rid, cmd, args, datafh=None): |
30 def sendcommandframes(reactor, stream, rid, cmd, args, datafh=None): |
31 """Generate frames to run a command and send them to a reactor.""" |
31 """Generate frames to run a command and send them to a reactor.""" |
32 return sendframes(reactor, |
32 return sendframes(reactor, |
33 framing.createcommandframes(rid, cmd, args, datafh)) |
33 framing.createcommandframes(stream, rid, cmd, args, |
|
34 datafh)) |
34 |
35 |
35 class FrameTests(unittest.TestCase): |
36 class FrameTests(unittest.TestCase): |
36 def testdataexactframesize(self): |
37 def testdataexactframesize(self): |
37 data = util.bytesio(b'x' * framing.DEFAULT_MAX_FRAME_SIZE) |
38 data = util.bytesio(b'x' * framing.DEFAULT_MAX_FRAME_SIZE) |
38 |
39 |
39 frames = list(framing.createcommandframes(1, b'command', {}, data)) |
40 stream = framing.stream() |
|
41 frames = list(framing.createcommandframes(stream, 1, b'command', |
|
42 {}, data)) |
40 self.assertEqual(frames, [ |
43 self.assertEqual(frames, [ |
41 ffs(b'1 command-name have-data command'), |
44 ffs(b'1 command-name have-data command'), |
42 ffs(b'1 command-data continuation %s' % data.getvalue()), |
45 ffs(b'1 command-data continuation %s' % data.getvalue()), |
43 ffs(b'1 command-data eos ') |
46 ffs(b'1 command-data eos ') |
44 ]) |
47 ]) |
45 |
48 |
46 def testdatamultipleframes(self): |
49 def testdatamultipleframes(self): |
47 data = util.bytesio(b'x' * (framing.DEFAULT_MAX_FRAME_SIZE + 1)) |
50 data = util.bytesio(b'x' * (framing.DEFAULT_MAX_FRAME_SIZE + 1)) |
48 frames = list(framing.createcommandframes(1, b'command', {}, data)) |
51 |
|
52 stream = framing.stream() |
|
53 frames = list(framing.createcommandframes(stream, 1, b'command', {}, |
|
54 data)) |
49 self.assertEqual(frames, [ |
55 self.assertEqual(frames, [ |
50 ffs(b'1 command-name have-data command'), |
56 ffs(b'1 command-name have-data command'), |
51 ffs(b'1 command-data continuation %s' % ( |
57 ffs(b'1 command-data continuation %s' % ( |
52 b'x' * framing.DEFAULT_MAX_FRAME_SIZE)), |
58 b'x' * framing.DEFAULT_MAX_FRAME_SIZE)), |
53 ffs(b'1 command-data eos x'), |
59 ffs(b'1 command-data eos x'), |
54 ]) |
60 ]) |
55 |
61 |
56 def testargsanddata(self): |
62 def testargsanddata(self): |
57 data = util.bytesio(b'x' * 100) |
63 data = util.bytesio(b'x' * 100) |
58 |
64 |
59 frames = list(framing.createcommandframes(1, b'command', { |
65 stream = framing.stream() |
|
66 frames = list(framing.createcommandframes(stream, 1, b'command', { |
60 b'key1': b'key1value', |
67 b'key1': b'key1value', |
61 b'key2': b'key2value', |
68 b'key2': b'key2value', |
62 b'key3': b'key3value', |
69 b'key3': b'key3value', |
63 }, data)) |
70 }, data)) |
64 |
71 |
73 def testtextoutputexcessiveargs(self): |
80 def testtextoutputexcessiveargs(self): |
74 """At most 255 formatting arguments are allowed.""" |
81 """At most 255 formatting arguments are allowed.""" |
75 with self.assertRaisesRegexp(ValueError, |
82 with self.assertRaisesRegexp(ValueError, |
76 'cannot use more than 255 formatting'): |
83 'cannot use more than 255 formatting'): |
77 args = [b'x' for i in range(256)] |
84 args = [b'x' for i in range(256)] |
78 list(framing.createtextoutputframe(1, [(b'bleh', args, [])])) |
85 list(framing.createtextoutputframe(None, 1, |
|
86 [(b'bleh', args, [])])) |
79 |
87 |
80 def testtextoutputexcessivelabels(self): |
88 def testtextoutputexcessivelabels(self): |
81 """At most 255 labels are allowed.""" |
89 """At most 255 labels are allowed.""" |
82 with self.assertRaisesRegexp(ValueError, |
90 with self.assertRaisesRegexp(ValueError, |
83 'cannot use more than 255 labels'): |
91 'cannot use more than 255 labels'): |
84 labels = [b'l' for i in range(256)] |
92 labels = [b'l' for i in range(256)] |
85 list(framing.createtextoutputframe(1, [(b'bleh', [], labels)])) |
93 list(framing.createtextoutputframe(None, 1, |
|
94 [(b'bleh', [], labels)])) |
86 |
95 |
87 def testtextoutputformattingstringtype(self): |
96 def testtextoutputformattingstringtype(self): |
88 """Formatting string must be bytes.""" |
97 """Formatting string must be bytes.""" |
89 with self.assertRaisesRegexp(ValueError, 'must use bytes formatting '): |
98 with self.assertRaisesRegexp(ValueError, 'must use bytes formatting '): |
90 list(framing.createtextoutputframe(1, [ |
99 list(framing.createtextoutputframe(None, 1, [ |
91 (b'foo'.decode('ascii'), [], [])])) |
100 (b'foo'.decode('ascii'), [], [])])) |
92 |
101 |
93 def testtextoutputargumentbytes(self): |
102 def testtextoutputargumentbytes(self): |
94 with self.assertRaisesRegexp(ValueError, 'must use bytes for argument'): |
103 with self.assertRaisesRegexp(ValueError, 'must use bytes for argument'): |
95 list(framing.createtextoutputframe(1, [ |
104 list(framing.createtextoutputframe(None, 1, [ |
96 (b'foo', [b'foo'.decode('ascii')], [])])) |
105 (b'foo', [b'foo'.decode('ascii')], [])])) |
97 |
106 |
98 def testtextoutputlabelbytes(self): |
107 def testtextoutputlabelbytes(self): |
99 with self.assertRaisesRegexp(ValueError, 'must use bytes for labels'): |
108 with self.assertRaisesRegexp(ValueError, 'must use bytes for labels'): |
100 list(framing.createtextoutputframe(1, [ |
109 list(framing.createtextoutputframe(None, 1, [ |
101 (b'foo', [], [b'foo'.decode('ascii')])])) |
110 (b'foo', [], [b'foo'.decode('ascii')])])) |
102 |
111 |
103 def testtextoutputtoolongformatstring(self): |
112 def testtextoutputtoolongformatstring(self): |
104 with self.assertRaisesRegexp(ValueError, |
113 with self.assertRaisesRegexp(ValueError, |
105 'formatting string cannot be longer than'): |
114 'formatting string cannot be longer than'): |
106 list(framing.createtextoutputframe(1, [ |
115 list(framing.createtextoutputframe(None, 1, [ |
107 (b'x' * 65536, [], [])])) |
116 (b'x' * 65536, [], [])])) |
108 |
117 |
109 def testtextoutputtoolongargumentstring(self): |
118 def testtextoutputtoolongargumentstring(self): |
110 with self.assertRaisesRegexp(ValueError, |
119 with self.assertRaisesRegexp(ValueError, |
111 'argument string cannot be longer than'): |
120 'argument string cannot be longer than'): |
112 list(framing.createtextoutputframe(1, [ |
121 list(framing.createtextoutputframe(None, 1, [ |
113 (b'bleh', [b'x' * 65536], [])])) |
122 (b'bleh', [b'x' * 65536], [])])) |
114 |
123 |
115 def testtextoutputtoolonglabelstring(self): |
124 def testtextoutputtoolonglabelstring(self): |
116 with self.assertRaisesRegexp(ValueError, |
125 with self.assertRaisesRegexp(ValueError, |
117 'label string cannot be longer than'): |
126 'label string cannot be longer than'): |
118 list(framing.createtextoutputframe(1, [ |
127 list(framing.createtextoutputframe(None, 1, [ |
119 (b'bleh', [], [b'x' * 65536])])) |
128 (b'bleh', [], [b'x' * 65536])])) |
120 |
129 |
121 def testtextoutput1simpleatom(self): |
130 def testtextoutput1simpleatom(self): |
122 val = list(framing.createtextoutputframe(1, [ |
131 stream = framing.stream() |
|
132 val = list(framing.createtextoutputframe(stream, 1, [ |
123 (b'foo', [], [])])) |
133 (b'foo', [], [])])) |
124 |
134 |
125 self.assertEqual(val, [ |
135 self.assertEqual(val, [ |
126 ffs(br'1 text-output 0 \x03\x00\x00\x00foo'), |
136 ffs(br'1 text-output 0 \x03\x00\x00\x00foo'), |
127 ]) |
137 ]) |
128 |
138 |
129 def testtextoutput2simpleatoms(self): |
139 def testtextoutput2simpleatoms(self): |
130 val = list(framing.createtextoutputframe(1, [ |
140 stream = framing.stream() |
|
141 val = list(framing.createtextoutputframe(stream, 1, [ |
131 (b'foo', [], []), |
142 (b'foo', [], []), |
132 (b'bar', [], []), |
143 (b'bar', [], []), |
133 ])) |
144 ])) |
134 |
145 |
135 self.assertEqual(val, [ |
146 self.assertEqual(val, [ |
136 ffs(br'1 text-output 0 \x03\x00\x00\x00foo\x03\x00\x00\x00bar'), |
147 ffs(br'1 text-output 0 \x03\x00\x00\x00foo\x03\x00\x00\x00bar'), |
137 ]) |
148 ]) |
138 |
149 |
139 def testtextoutput1arg(self): |
150 def testtextoutput1arg(self): |
140 val = list(framing.createtextoutputframe(1, [ |
151 stream = framing.stream() |
|
152 val = list(framing.createtextoutputframe(stream, 1, [ |
141 (b'foo %s', [b'val1'], []), |
153 (b'foo %s', [b'val1'], []), |
142 ])) |
154 ])) |
143 |
155 |
144 self.assertEqual(val, [ |
156 self.assertEqual(val, [ |
145 ffs(br'1 text-output 0 \x06\x00\x00\x01\x04\x00foo %sval1'), |
157 ffs(br'1 text-output 0 \x06\x00\x00\x01\x04\x00foo %sval1'), |
146 ]) |
158 ]) |
147 |
159 |
148 def testtextoutput2arg(self): |
160 def testtextoutput2arg(self): |
149 val = list(framing.createtextoutputframe(1, [ |
161 stream = framing.stream() |
|
162 val = list(framing.createtextoutputframe(stream, 1, [ |
150 (b'foo %s %s', [b'val', b'value'], []), |
163 (b'foo %s %s', [b'val', b'value'], []), |
151 ])) |
164 ])) |
152 |
165 |
153 self.assertEqual(val, [ |
166 self.assertEqual(val, [ |
154 ffs(br'1 text-output 0 \x09\x00\x00\x02\x03\x00\x05\x00' |
167 ffs(br'1 text-output 0 \x09\x00\x00\x02\x03\x00\x05\x00' |
155 br'foo %s %svalvalue'), |
168 br'foo %s %svalvalue'), |
156 ]) |
169 ]) |
157 |
170 |
158 def testtextoutput1label(self): |
171 def testtextoutput1label(self): |
159 val = list(framing.createtextoutputframe(1, [ |
172 stream = framing.stream() |
|
173 val = list(framing.createtextoutputframe(stream, 1, [ |
160 (b'foo', [], [b'label']), |
174 (b'foo', [], [b'label']), |
161 ])) |
175 ])) |
162 |
176 |
163 self.assertEqual(val, [ |
177 self.assertEqual(val, [ |
164 ffs(br'1 text-output 0 \x03\x00\x01\x00\x05foolabel'), |
178 ffs(br'1 text-output 0 \x03\x00\x01\x00\x05foolabel'), |
165 ]) |
179 ]) |
166 |
180 |
167 def testargandlabel(self): |
181 def testargandlabel(self): |
168 val = list(framing.createtextoutputframe(1, [ |
182 stream = framing.stream() |
|
183 val = list(framing.createtextoutputframe(stream, 1, [ |
169 (b'foo %s', [b'arg'], [b'label']), |
184 (b'foo %s', [b'arg'], [b'label']), |
170 ])) |
185 ])) |
171 |
186 |
172 self.assertEqual(val, [ |
187 self.assertEqual(val, [ |
173 ffs(br'1 text-output 0 \x06\x00\x01\x01\x05\x03\x00foo %slabelarg'), |
188 ffs(br'1 text-output 0 \x06\x00\x01\x01\x05\x03\x00foo %slabelarg'), |
348 |
367 |
349 def testconflictingrequestidallowed(self): |
368 def testconflictingrequestidallowed(self): |
350 """Multiple fully serviced commands with same request ID is allowed.""" |
369 """Multiple fully serviced commands with same request ID is allowed.""" |
351 reactor = makereactor() |
370 reactor = makereactor() |
352 results = [] |
371 results = [] |
|
372 outstream = framing.stream() |
353 results.append(self._sendsingleframe( |
373 results.append(self._sendsingleframe( |
354 reactor, ffs(b'1 command-name eos command'))) |
374 reactor, ffs(b'1 command-name eos command'))) |
355 result = reactor.onbytesresponseready(1, b'response1') |
375 result = reactor.onbytesresponseready(outstream, 1, b'response1') |
356 self.assertaction(result, 'sendframes') |
376 self.assertaction(result, 'sendframes') |
357 list(result[1]['framegen']) |
377 list(result[1]['framegen']) |
358 results.append(self._sendsingleframe( |
378 results.append(self._sendsingleframe( |
359 reactor, ffs(b'1 command-name eos command'))) |
379 reactor, ffs(b'1 command-name eos command'))) |
360 result = reactor.onbytesresponseready(1, b'response2') |
380 result = reactor.onbytesresponseready(outstream, 1, b'response2') |
361 self.assertaction(result, 'sendframes') |
381 self.assertaction(result, 'sendframes') |
362 list(result[1]['framegen']) |
382 list(result[1]['framegen']) |
363 results.append(self._sendsingleframe( |
383 results.append(self._sendsingleframe( |
364 reactor, ffs(b'1 command-name eos command'))) |
384 reactor, ffs(b'1 command-name eos command'))) |
365 result = reactor.onbytesresponseready(1, b'response3') |
385 result = reactor.onbytesresponseready(outstream, 1, b'response3') |
366 self.assertaction(result, 'sendframes') |
386 self.assertaction(result, 'sendframes') |
367 list(result[1]['framegen']) |
387 list(result[1]['framegen']) |
368 |
388 |
369 for i in range(3): |
389 for i in range(3): |
370 self.assertaction(results[i], 'runcommand') |
390 self.assertaction(results[i], 'runcommand') |
513 """Bytes response spanning multiple frames is handled.""" |
535 """Bytes response spanning multiple frames is handled.""" |
514 first = b'x' * framing.DEFAULT_MAX_FRAME_SIZE |
536 first = b'x' * framing.DEFAULT_MAX_FRAME_SIZE |
515 second = b'y' * 100 |
537 second = b'y' * 100 |
516 |
538 |
517 reactor = makereactor() |
539 reactor = makereactor() |
518 list(sendcommandframes(reactor, 1, b'mycommand', {})) |
540 instream = framing.stream() |
519 |
541 list(sendcommandframes(reactor, instream, 1, b'mycommand', {})) |
520 result = reactor.onbytesresponseready(1, first + second) |
542 |
|
543 outstream = framing.stream() |
|
544 result = reactor.onbytesresponseready(outstream, 1, first + second) |
521 self.assertaction(result, 'sendframes') |
545 self.assertaction(result, 'sendframes') |
522 self.assertframesequal(result[1]['framegen'], [ |
546 self.assertframesequal(result[1]['framegen'], [ |
523 b'1 bytes-response continuation %s' % first, |
547 b'1 bytes-response continuation %s' % first, |
524 b'1 bytes-response eos %s' % second, |
548 b'1 bytes-response eos %s' % second, |
525 ]) |
549 ]) |
526 |
550 |
527 def testapplicationerror(self): |
551 def testapplicationerror(self): |
528 reactor = makereactor() |
552 reactor = makereactor() |
529 list(sendcommandframes(reactor, 1, b'mycommand', {})) |
553 instream = framing.stream() |
530 |
554 list(sendcommandframes(reactor, instream, 1, b'mycommand', {})) |
531 result = reactor.onapplicationerror(1, b'some message') |
555 |
|
556 outstream = framing.stream() |
|
557 result = reactor.onapplicationerror(outstream, 1, b'some message') |
532 self.assertaction(result, 'sendframes') |
558 self.assertaction(result, 'sendframes') |
533 self.assertframesequal(result[1]['framegen'], [ |
559 self.assertframesequal(result[1]['framegen'], [ |
534 b'1 error-response application some message', |
560 b'1 error-response application some message', |
535 ]) |
561 ]) |
536 |
562 |
537 def test1commanddeferresponse(self): |
563 def test1commanddeferresponse(self): |
538 """Responses when in deferred output mode are delayed until EOF.""" |
564 """Responses when in deferred output mode are delayed until EOF.""" |
539 reactor = makereactor(deferoutput=True) |
565 reactor = makereactor(deferoutput=True) |
540 results = list(sendcommandframes(reactor, 1, b'mycommand', {})) |
566 instream = framing.stream() |
|
567 results = list(sendcommandframes(reactor, instream, 1, b'mycommand', |
|
568 {})) |
541 self.assertEqual(len(results), 1) |
569 self.assertEqual(len(results), 1) |
542 self.assertaction(results[0], 'runcommand') |
570 self.assertaction(results[0], 'runcommand') |
543 |
571 |
544 result = reactor.onbytesresponseready(1, b'response') |
572 outstream = framing.stream() |
|
573 result = reactor.onbytesresponseready(outstream, 1, b'response') |
545 self.assertaction(result, 'noop') |
574 self.assertaction(result, 'noop') |
546 result = reactor.oninputeof() |
575 result = reactor.oninputeof() |
547 self.assertaction(result, 'sendframes') |
576 self.assertaction(result, 'sendframes') |
548 self.assertframesequal(result[1]['framegen'], [ |
577 self.assertframesequal(result[1]['framegen'], [ |
549 b'1 bytes-response eos response', |
578 b'1 bytes-response eos response', |
550 ]) |
579 ]) |
551 |
580 |
552 def testmultiplecommanddeferresponse(self): |
581 def testmultiplecommanddeferresponse(self): |
553 reactor = makereactor(deferoutput=True) |
582 reactor = makereactor(deferoutput=True) |
554 list(sendcommandframes(reactor, 1, b'command1', {})) |
583 instream = framing.stream() |
555 list(sendcommandframes(reactor, 3, b'command2', {})) |
584 list(sendcommandframes(reactor, instream, 1, b'command1', {})) |
556 |
585 list(sendcommandframes(reactor, instream, 3, b'command2', {})) |
557 result = reactor.onbytesresponseready(1, b'response1') |
586 |
|
587 outstream = framing.stream() |
|
588 result = reactor.onbytesresponseready(outstream, 1, b'response1') |
558 self.assertaction(result, 'noop') |
589 self.assertaction(result, 'noop') |
559 result = reactor.onbytesresponseready(3, b'response2') |
590 result = reactor.onbytesresponseready(outstream, 3, b'response2') |
560 self.assertaction(result, 'noop') |
591 self.assertaction(result, 'noop') |
561 result = reactor.oninputeof() |
592 result = reactor.oninputeof() |
562 self.assertaction(result, 'sendframes') |
593 self.assertaction(result, 'sendframes') |
563 self.assertframesequal(result[1]['framegen'], [ |
594 self.assertframesequal(result[1]['framegen'], [ |
564 b'1 bytes-response eos response1', |
595 b'1 bytes-response eos response1', |
565 b'3 bytes-response eos response2' |
596 b'3 bytes-response eos response2' |
566 ]) |
597 ]) |
567 |
598 |
568 def testrequestidtracking(self): |
599 def testrequestidtracking(self): |
569 reactor = makereactor(deferoutput=True) |
600 reactor = makereactor(deferoutput=True) |
570 list(sendcommandframes(reactor, 1, b'command1', {})) |
601 instream = framing.stream() |
571 list(sendcommandframes(reactor, 3, b'command2', {})) |
602 list(sendcommandframes(reactor, instream, 1, b'command1', {})) |
572 list(sendcommandframes(reactor, 5, b'command3', {})) |
603 list(sendcommandframes(reactor, instream, 3, b'command2', {})) |
|
604 list(sendcommandframes(reactor, instream, 5, b'command3', {})) |
573 |
605 |
574 # Register results for commands out of order. |
606 # Register results for commands out of order. |
575 reactor.onbytesresponseready(3, b'response3') |
607 outstream = framing.stream() |
576 reactor.onbytesresponseready(1, b'response1') |
608 reactor.onbytesresponseready(outstream, 3, b'response3') |
577 reactor.onbytesresponseready(5, b'response5') |
609 reactor.onbytesresponseready(outstream, 1, b'response1') |
|
610 reactor.onbytesresponseready(outstream, 5, b'response5') |
578 |
611 |
579 result = reactor.oninputeof() |
612 result = reactor.oninputeof() |
580 self.assertaction(result, 'sendframes') |
613 self.assertaction(result, 'sendframes') |
581 self.assertframesequal(result[1]['framegen'], [ |
614 self.assertframesequal(result[1]['framegen'], [ |
582 b'3 bytes-response eos response3', |
615 b'3 bytes-response eos response3', |
585 ]) |
618 ]) |
586 |
619 |
587 def testduplicaterequestonactivecommand(self): |
620 def testduplicaterequestonactivecommand(self): |
588 """Receiving a request ID that matches a request that isn't finished.""" |
621 """Receiving a request ID that matches a request that isn't finished.""" |
589 reactor = makereactor() |
622 reactor = makereactor() |
590 list(sendcommandframes(reactor, 1, b'command1', {})) |
623 stream = framing.stream() |
591 results = list(sendcommandframes(reactor, 1, b'command1', {})) |
624 list(sendcommandframes(reactor, stream, 1, b'command1', {})) |
|
625 results = list(sendcommandframes(reactor, stream, 1, b'command1', {})) |
592 |
626 |
593 self.assertaction(results[0], 'error') |
627 self.assertaction(results[0], 'error') |
594 self.assertEqual(results[0][1], { |
628 self.assertEqual(results[0][1], { |
595 'message': b'request with ID 1 is already active', |
629 'message': b'request with ID 1 is already active', |
596 }) |
630 }) |
597 |
631 |
598 def testduplicaterequestonactivecommandnosend(self): |
632 def testduplicaterequestonactivecommandnosend(self): |
599 """Same as above but we've registered a response but haven't sent it.""" |
633 """Same as above but we've registered a response but haven't sent it.""" |
600 reactor = makereactor() |
634 reactor = makereactor() |
601 list(sendcommandframes(reactor, 1, b'command1', {})) |
635 instream = framing.stream() |
602 reactor.onbytesresponseready(1, b'response') |
636 list(sendcommandframes(reactor, instream, 1, b'command1', {})) |
|
637 outstream = framing.stream() |
|
638 reactor.onbytesresponseready(outstream, 1, b'response') |
603 |
639 |
604 # We've registered the response but haven't sent it. From the |
640 # We've registered the response but haven't sent it. From the |
605 # perspective of the reactor, the command is still active. |
641 # perspective of the reactor, the command is still active. |
606 |
642 |
607 results = list(sendcommandframes(reactor, 1, b'command1', {})) |
643 results = list(sendcommandframes(reactor, instream, 1, b'command1', {})) |
608 self.assertaction(results[0], 'error') |
644 self.assertaction(results[0], 'error') |
609 self.assertEqual(results[0][1], { |
645 self.assertEqual(results[0][1], { |
610 'message': b'request with ID 1 is already active', |
646 'message': b'request with ID 1 is already active', |
611 }) |
647 }) |
612 |
648 |
613 def testduplicaterequestargumentframe(self): |
649 def testduplicaterequestargumentframe(self): |
614 """Variant on above except we sent an argument frame instead of name.""" |
650 """Variant on above except we sent an argument frame instead of name.""" |
615 reactor = makereactor() |
651 reactor = makereactor() |
616 list(sendcommandframes(reactor, 1, b'command', {})) |
652 stream = framing.stream() |
|
653 list(sendcommandframes(reactor, stream, 1, b'command', {})) |
617 results = list(sendframes(reactor, [ |
654 results = list(sendframes(reactor, [ |
618 ffs(b'3 command-name have-args command'), |
655 ffs(b'3 command-name have-args command'), |
619 ffs(b'1 command-argument 0 ignored'), |
656 ffs(b'1 command-argument 0 ignored'), |
620 ])) |
657 ])) |
621 self.assertaction(results[0], 'wantframe') |
658 self.assertaction(results[0], 'wantframe') |