tests/test-wireproto-serverreactor.py
changeset 43076 2372284d9457
parent 41136 a181a1c8af1d
child 45942 89a2afe31e82
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
     5 from mercurial import (
     5 from mercurial import (
     6     ui as uimod,
     6     ui as uimod,
     7     util,
     7     util,
     8     wireprotoframing as framing,
     8     wireprotoframing as framing,
     9 )
     9 )
    10 from mercurial.utils import (
    10 from mercurial.utils import cborutil
    11     cborutil,
       
    12 )
       
    13 
    11 
    14 ffs = framing.makeframefromhumanstring
    12 ffs = framing.makeframefromhumanstring
    15 
    13 
    16 OK = b''.join(cborutil.streamencode({b'status': b'ok'}))
    14 OK = b''.join(cborutil.streamencode({b'status': b'ok'}))
       
    15 
    17 
    16 
    18 def makereactor(deferoutput=False):
    17 def makereactor(deferoutput=False):
    19     ui = uimod.ui()
    18     ui = uimod.ui()
    20     return framing.serverreactor(ui, deferoutput=deferoutput)
    19     return framing.serverreactor(ui, deferoutput=deferoutput)
       
    20 
    21 
    21 
    22 def sendframes(reactor, gen):
    22 def sendframes(reactor, gen):
    23     """Send a generator of frame bytearray to a reactor.
    23     """Send a generator of frame bytearray to a reactor.
    24 
    24 
    25     Emits a generator of results from ``onframerecv()`` calls.
    25     Emits a generator of results from ``onframerecv()`` calls.
    26     """
    26     """
    27     for frame in gen:
    27     for frame in gen:
    28         header = framing.parseheader(frame)
    28         header = framing.parseheader(frame)
    29         payload = frame[framing.FRAME_HEADER_SIZE:]
    29         payload = frame[framing.FRAME_HEADER_SIZE :]
    30         assert len(payload) == header.length
    30         assert len(payload) == header.length
    31 
    31 
    32         yield reactor.onframerecv(framing.frame(header.requestid,
    32         yield reactor.onframerecv(
    33                                                 header.streamid,
    33             framing.frame(
    34                                                 header.streamflags,
    34                 header.requestid,
    35                                                 header.typeid,
    35                 header.streamid,
    36                                                 header.flags,
    36                 header.streamflags,
    37                                                 payload))
    37                 header.typeid,
       
    38                 header.flags,
       
    39                 payload,
       
    40             )
       
    41         )
       
    42 
    38 
    43 
    39 def sendcommandframes(reactor, stream, rid, cmd, args, datafh=None):
    44 def sendcommandframes(reactor, stream, rid, cmd, args, datafh=None):
    40     """Generate frames to run a command and send them to a reactor."""
    45     """Generate frames to run a command and send them to a reactor."""
    41     return sendframes(reactor,
    46     return sendframes(
    42                       framing.createcommandframes(stream, rid, cmd, args,
    47         reactor, framing.createcommandframes(stream, rid, cmd, args, datafh)
    43                                                   datafh))
    48     )
    44 
    49 
    45 
    50 
    46 class ServerReactorTests(unittest.TestCase):
    51 class ServerReactorTests(unittest.TestCase):
    47     def _sendsingleframe(self, reactor, f):
    52     def _sendsingleframe(self, reactor, f):
    48         results = list(sendframes(reactor, [f]))
    53         results = list(sendframes(reactor, [f]))
    65         reactor = makereactor()
    70         reactor = makereactor()
    66         stream = framing.stream(1)
    71         stream = framing.stream(1)
    67         results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {}))
    72         results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {}))
    68         self.assertEqual(len(results), 1)
    73         self.assertEqual(len(results), 1)
    69         self.assertaction(results[0], b'runcommand')
    74         self.assertaction(results[0], b'runcommand')
    70         self.assertEqual(results[0][1], {
    75         self.assertEqual(
    71             b'requestid': 1,
    76             results[0][1],
    72             b'command': b'mycommand',
    77             {
    73             b'args': {},
    78                 b'requestid': 1,
    74             b'redirect': None,
    79                 b'command': b'mycommand',
    75             b'data': None,
    80                 b'args': {},
    76         })
    81                 b'redirect': None,
       
    82                 b'data': None,
       
    83             },
       
    84         )
    77 
    85 
    78         result = reactor.oninputeof()
    86         result = reactor.oninputeof()
    79         self.assertaction(result, b'noop')
    87         self.assertaction(result, b'noop')
    80 
    88 
    81     def test1argument(self):
    89     def test1argument(self):
    82         reactor = makereactor()
    90         reactor = makereactor()
    83         stream = framing.stream(1)
    91         stream = framing.stream(1)
    84         results = list(sendcommandframes(reactor, stream, 41, b'mycommand',
    92         results = list(
    85                                          {b'foo': b'bar'}))
    93             sendcommandframes(
       
    94                 reactor, stream, 41, b'mycommand', {b'foo': b'bar'}
       
    95             )
       
    96         )
    86         self.assertEqual(len(results), 1)
    97         self.assertEqual(len(results), 1)
    87         self.assertaction(results[0], b'runcommand')
    98         self.assertaction(results[0], b'runcommand')
    88         self.assertEqual(results[0][1], {
    99         self.assertEqual(
    89             b'requestid': 41,
   100             results[0][1],
    90             b'command': b'mycommand',
   101             {
    91             b'args': {b'foo': b'bar'},
   102                 b'requestid': 41,
    92             b'redirect': None,
   103                 b'command': b'mycommand',
    93             b'data': None,
   104                 b'args': {b'foo': b'bar'},
    94         })
   105                 b'redirect': None,
       
   106                 b'data': None,
       
   107             },
       
   108         )
    95 
   109 
    96     def testmultiarguments(self):
   110     def testmultiarguments(self):
    97         reactor = makereactor()
   111         reactor = makereactor()
    98         stream = framing.stream(1)
   112         stream = framing.stream(1)
    99         results = list(sendcommandframes(reactor, stream, 1, b'mycommand',
   113         results = list(
   100                                          {b'foo': b'bar', b'biz': b'baz'}))
   114             sendcommandframes(
       
   115                 reactor,
       
   116                 stream,
       
   117                 1,
       
   118                 b'mycommand',
       
   119                 {b'foo': b'bar', b'biz': b'baz'},
       
   120             )
       
   121         )
   101         self.assertEqual(len(results), 1)
   122         self.assertEqual(len(results), 1)
   102         self.assertaction(results[0], b'runcommand')
   123         self.assertaction(results[0], b'runcommand')
   103         self.assertEqual(results[0][1], {
   124         self.assertEqual(
   104             b'requestid': 1,
   125             results[0][1],
   105             b'command': b'mycommand',
   126             {
   106             b'args': {b'foo': b'bar', b'biz': b'baz'},
   127                 b'requestid': 1,
   107             b'redirect': None,
   128                 b'command': b'mycommand',
   108             b'data': None,
   129                 b'args': {b'foo': b'bar', b'biz': b'baz'},
   109         })
   130                 b'redirect': None,
       
   131                 b'data': None,
       
   132             },
       
   133         )
   110 
   134 
   111     def testsimplecommanddata(self):
   135     def testsimplecommanddata(self):
   112         reactor = makereactor()
   136         reactor = makereactor()
   113         stream = framing.stream(1)
   137         stream = framing.stream(1)
   114         results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {},
   138         results = list(
   115                                          util.bytesio(b'data!')))
   139             sendcommandframes(
       
   140                 reactor, stream, 1, b'mycommand', {}, util.bytesio(b'data!')
       
   141             )
       
   142         )
   116         self.assertEqual(len(results), 2)
   143         self.assertEqual(len(results), 2)
   117         self.assertaction(results[0], b'wantframe')
   144         self.assertaction(results[0], b'wantframe')
   118         self.assertaction(results[1], b'runcommand')
   145         self.assertaction(results[1], b'runcommand')
   119         self.assertEqual(results[1][1], {
   146         self.assertEqual(
   120             b'requestid': 1,
   147             results[1][1],
   121             b'command': b'mycommand',
   148             {
   122             b'args': {},
   149                 b'requestid': 1,
   123             b'redirect': None,
   150                 b'command': b'mycommand',
   124             b'data': b'data!',
   151                 b'args': {},
   125         })
   152                 b'redirect': None,
       
   153                 b'data': b'data!',
       
   154             },
       
   155         )
   126 
   156 
   127     def testmultipledataframes(self):
   157     def testmultipledataframes(self):
   128         frames = [
   158         frames = [
   129             ffs(b'1 1 stream-begin command-request new|have-data '
   159             ffs(
   130                 b"cbor:{b'name': b'mycommand'}"),
   160                 b'1 1 stream-begin command-request new|have-data '
       
   161                 b"cbor:{b'name': b'mycommand'}"
       
   162             ),
   131             ffs(b'1 1 0 command-data continuation data1'),
   163             ffs(b'1 1 0 command-data continuation data1'),
   132             ffs(b'1 1 0 command-data continuation data2'),
   164             ffs(b'1 1 0 command-data continuation data2'),
   133             ffs(b'1 1 0 command-data eos data3'),
   165             ffs(b'1 1 0 command-data eos data3'),
   134         ]
   166         ]
   135 
   167 
   137         results = list(sendframes(reactor, frames))
   169         results = list(sendframes(reactor, frames))
   138         self.assertEqual(len(results), 4)
   170         self.assertEqual(len(results), 4)
   139         for i in range(3):
   171         for i in range(3):
   140             self.assertaction(results[i], b'wantframe')
   172             self.assertaction(results[i], b'wantframe')
   141         self.assertaction(results[3], b'runcommand')
   173         self.assertaction(results[3], b'runcommand')
   142         self.assertEqual(results[3][1], {
   174         self.assertEqual(
   143             b'requestid': 1,
   175             results[3][1],
   144             b'command': b'mycommand',
   176             {
   145             b'args': {},
   177                 b'requestid': 1,
   146             b'redirect': None,
   178                 b'command': b'mycommand',
   147             b'data': b'data1data2data3',
   179                 b'args': {},
   148         })
   180                 b'redirect': None,
       
   181                 b'data': b'data1data2data3',
       
   182             },
       
   183         )
   149 
   184 
   150     def testargumentanddata(self):
   185     def testargumentanddata(self):
   151         frames = [
   186         frames = [
   152             ffs(b'1 1 stream-begin command-request new|have-data '
   187             ffs(
       
   188                 b'1 1 stream-begin command-request new|have-data '
   153                 b"cbor:{b'name': b'command', b'args': {b'key': b'val',"
   189                 b"cbor:{b'name': b'command', b'args': {b'key': b'val',"
   154                 b"b'foo': b'bar'}}"),
   190                 b"b'foo': b'bar'}}"
       
   191             ),
   155             ffs(b'1 1 0 command-data continuation value1'),
   192             ffs(b'1 1 0 command-data continuation value1'),
   156             ffs(b'1 1 0 command-data eos value2'),
   193             ffs(b'1 1 0 command-data eos value2'),
   157         ]
   194         ]
   158 
   195 
   159         reactor = makereactor()
   196         reactor = makereactor()
   160         results = list(sendframes(reactor, frames))
   197         results = list(sendframes(reactor, frames))
   161 
   198 
   162         self.assertaction(results[-1], b'runcommand')
   199         self.assertaction(results[-1], b'runcommand')
   163         self.assertEqual(results[-1][1], {
   200         self.assertEqual(
   164             b'requestid': 1,
   201             results[-1][1],
   165             b'command': b'command',
   202             {
   166             b'args': {
   203                 b'requestid': 1,
   167                 b'key': b'val',
   204                 b'command': b'command',
   168                 b'foo': b'bar',
   205                 b'args': {b'key': b'val', b'foo': b'bar',},
   169             },
   206                 b'redirect': None,
   170             b'redirect': None,
   207                 b'data': b'value1value2',
   171             b'data': b'value1value2',
   208             },
   172         })
   209         )
   173 
   210 
   174     def testnewandcontinuation(self):
   211     def testnewandcontinuation(self):
   175         result = self._sendsingleframe(makereactor(),
   212         result = self._sendsingleframe(
   176             ffs(b'1 1 stream-begin command-request new|continuation '))
   213             makereactor(),
       
   214             ffs(b'1 1 stream-begin command-request new|continuation '),
       
   215         )
   177         self.assertaction(result, b'error')
   216         self.assertaction(result, b'error')
   178         self.assertEqual(result[1], {
   217         self.assertEqual(
   179             b'message': b'received command request frame with both new and '
   218             result[1],
   180                         b'continuation flags set',
   219             {
   181         })
   220                 b'message': b'received command request frame with both new and '
       
   221                 b'continuation flags set',
       
   222             },
       
   223         )
   182 
   224 
   183     def testneithernewnorcontinuation(self):
   225     def testneithernewnorcontinuation(self):
   184         result = self._sendsingleframe(makereactor(),
   226         result = self._sendsingleframe(
   185             ffs(b'1 1 stream-begin command-request 0 '))
   227             makereactor(), ffs(b'1 1 stream-begin command-request 0 ')
       
   228         )
   186         self.assertaction(result, b'error')
   229         self.assertaction(result, b'error')
   187         self.assertEqual(result[1], {
   230         self.assertEqual(
   188             b'message': b'received command request frame with neither new nor '
   231             result[1],
   189                         b'continuation flags set',
   232             {
   190         })
   233                 b'message': b'received command request frame with neither new nor '
       
   234                 b'continuation flags set',
       
   235             },
       
   236         )
   191 
   237 
   192     def testunexpectedcommanddata(self):
   238     def testunexpectedcommanddata(self):
   193         """Command data frame when not running a command is an error."""
   239         """Command data frame when not running a command is an error."""
   194         result = self._sendsingleframe(makereactor(),
   240         result = self._sendsingleframe(
   195             ffs(b'1 1 stream-begin command-data 0 ignored'))
   241             makereactor(), ffs(b'1 1 stream-begin command-data 0 ignored')
       
   242         )
   196         self.assertaction(result, b'error')
   243         self.assertaction(result, b'error')
   197         self.assertEqual(result[1], {
   244         self.assertEqual(
   198             b'message': b'expected sender protocol settings or command request '
   245             result[1],
   199                         b'frame; got 2',
   246             {
   200         })
   247                 b'message': b'expected sender protocol settings or command request '
       
   248                 b'frame; got 2',
       
   249             },
       
   250         )
   201 
   251 
   202     def testunexpectedcommanddatareceiving(self):
   252     def testunexpectedcommanddatareceiving(self):
   203         """Same as above except the command is receiving."""
   253         """Same as above except the command is receiving."""
   204         results = list(sendframes(makereactor(), [
   254         results = list(
   205             ffs(b'1 1 stream-begin command-request new|more '
   255             sendframes(
   206                 b"cbor:{b'name': b'ignored'}"),
   256                 makereactor(),
   207             ffs(b'1 1 0 command-data eos ignored'),
   257                 [
   208         ]))
   258                     ffs(
       
   259                         b'1 1 stream-begin command-request new|more '
       
   260                         b"cbor:{b'name': b'ignored'}"
       
   261                     ),
       
   262                     ffs(b'1 1 0 command-data eos ignored'),
       
   263                 ],
       
   264             )
       
   265         )
   209 
   266 
   210         self.assertaction(results[0], b'wantframe')
   267         self.assertaction(results[0], b'wantframe')
   211         self.assertaction(results[1], b'error')
   268         self.assertaction(results[1], b'error')
   212         self.assertEqual(results[1][1], {
   269         self.assertEqual(
   213             b'message': b'received command data frame for request that is not '
   270             results[1][1],
   214                         b'expecting data: 1',
   271             {
   215         })
   272                 b'message': b'received command data frame for request that is not '
       
   273                 b'expecting data: 1',
       
   274             },
       
   275         )
   216 
   276 
   217     def testconflictingrequestidallowed(self):
   277     def testconflictingrequestidallowed(self):
   218         """Multiple fully serviced commands with same request ID is allowed."""
   278         """Multiple fully serviced commands with same request ID is allowed."""
   219         reactor = makereactor()
   279         reactor = makereactor()
   220         results = []
   280         results = []
   221         outstream = reactor.makeoutputstream()
   281         outstream = reactor.makeoutputstream()
   222         results.append(self._sendsingleframe(
   282         results.append(
   223             reactor, ffs(b'1 1 stream-begin command-request new '
   283             self._sendsingleframe(
   224                          b"cbor:{b'name': b'command'}")))
   284                 reactor,
       
   285                 ffs(
       
   286                     b'1 1 stream-begin command-request new '
       
   287                     b"cbor:{b'name': b'command'}"
       
   288                 ),
       
   289             )
       
   290         )
   225         result = reactor.oncommandresponsereadyobjects(
   291         result = reactor.oncommandresponsereadyobjects(
   226             outstream, 1, [b'response1'])
   292             outstream, 1, [b'response1']
       
   293         )
   227         self.assertaction(result, b'sendframes')
   294         self.assertaction(result, b'sendframes')
   228         list(result[1][b'framegen'])
   295         list(result[1][b'framegen'])
   229         results.append(self._sendsingleframe(
   296         results.append(
   230             reactor, ffs(b'1 1 stream-begin command-request new '
   297             self._sendsingleframe(
   231                          b"cbor:{b'name': b'command'}")))
   298                 reactor,
       
   299                 ffs(
       
   300                     b'1 1 stream-begin command-request new '
       
   301                     b"cbor:{b'name': b'command'}"
       
   302                 ),
       
   303             )
       
   304         )
   232         result = reactor.oncommandresponsereadyobjects(
   305         result = reactor.oncommandresponsereadyobjects(
   233             outstream, 1, [b'response2'])
   306             outstream, 1, [b'response2']
       
   307         )
   234         self.assertaction(result, b'sendframes')
   308         self.assertaction(result, b'sendframes')
   235         list(result[1][b'framegen'])
   309         list(result[1][b'framegen'])
   236         results.append(self._sendsingleframe(
   310         results.append(
   237             reactor, ffs(b'1 1 stream-begin command-request new '
   311             self._sendsingleframe(
   238                          b"cbor:{b'name': b'command'}")))
   312                 reactor,
       
   313                 ffs(
       
   314                     b'1 1 stream-begin command-request new '
       
   315                     b"cbor:{b'name': b'command'}"
       
   316                 ),
       
   317             )
       
   318         )
   239         result = reactor.oncommandresponsereadyobjects(
   319         result = reactor.oncommandresponsereadyobjects(
   240             outstream, 1, [b'response3'])
   320             outstream, 1, [b'response3']
       
   321         )
   241         self.assertaction(result, b'sendframes')
   322         self.assertaction(result, b'sendframes')
   242         list(result[1][b'framegen'])
   323         list(result[1][b'framegen'])
   243 
   324 
   244         for i in range(3):
   325         for i in range(3):
   245             self.assertaction(results[i], b'runcommand')
   326             self.assertaction(results[i], b'runcommand')
   246             self.assertEqual(results[i][1], {
   327             self.assertEqual(
   247                 b'requestid': 1,
   328                 results[i][1],
   248                 b'command': b'command',
   329                 {
   249                 b'args': {},
   330                     b'requestid': 1,
       
   331                     b'command': b'command',
       
   332                     b'args': {},
       
   333                     b'redirect': None,
       
   334                     b'data': None,
       
   335                 },
       
   336             )
       
   337 
       
   338     def testconflictingrequestid(self):
       
   339         """Request ID for new command matching in-flight command is illegal."""
       
   340         results = list(
       
   341             sendframes(
       
   342                 makereactor(),
       
   343                 [
       
   344                     ffs(
       
   345                         b'1 1 stream-begin command-request new|more '
       
   346                         b"cbor:{b'name': b'command'}"
       
   347                     ),
       
   348                     ffs(
       
   349                         b'1 1 0 command-request new '
       
   350                         b"cbor:{b'name': b'command1'}"
       
   351                     ),
       
   352                 ],
       
   353             )
       
   354         )
       
   355 
       
   356         self.assertaction(results[0], b'wantframe')
       
   357         self.assertaction(results[1], b'error')
       
   358         self.assertEqual(
       
   359             results[1][1], {b'message': b'request with ID 1 already received',}
       
   360         )
       
   361 
       
   362     def testinterleavedcommands(self):
       
   363         cbor1 = b''.join(
       
   364             cborutil.streamencode(
       
   365                 {
       
   366                     b'name': b'command1',
       
   367                     b'args': {b'foo': b'bar', b'key1': b'val',},
       
   368                 }
       
   369             )
       
   370         )
       
   371         cbor3 = b''.join(
       
   372             cborutil.streamencode(
       
   373                 {
       
   374                     b'name': b'command3',
       
   375                     b'args': {b'biz': b'baz', b'key': b'val',},
       
   376                 }
       
   377             )
       
   378         )
       
   379 
       
   380         results = list(
       
   381             sendframes(
       
   382                 makereactor(),
       
   383                 [
       
   384                     ffs(
       
   385                         b'1 1 stream-begin command-request new|more %s'
       
   386                         % cbor1[0:6]
       
   387                     ),
       
   388                     ffs(b'3 1 0 command-request new|more %s' % cbor3[0:10]),
       
   389                     ffs(
       
   390                         b'1 1 0 command-request continuation|more %s'
       
   391                         % cbor1[6:9]
       
   392                     ),
       
   393                     ffs(
       
   394                         b'3 1 0 command-request continuation|more %s'
       
   395                         % cbor3[10:13]
       
   396                     ),
       
   397                     ffs(b'3 1 0 command-request continuation %s' % cbor3[13:]),
       
   398                     ffs(b'1 1 0 command-request continuation %s' % cbor1[9:]),
       
   399                 ],
       
   400             )
       
   401         )
       
   402 
       
   403         self.assertEqual(
       
   404             [t[0] for t in results],
       
   405             [
       
   406                 b'wantframe',
       
   407                 b'wantframe',
       
   408                 b'wantframe',
       
   409                 b'wantframe',
       
   410                 b'runcommand',
       
   411                 b'runcommand',
       
   412             ],
       
   413         )
       
   414 
       
   415         self.assertEqual(
       
   416             results[4][1],
       
   417             {
       
   418                 b'requestid': 3,
       
   419                 b'command': b'command3',
       
   420                 b'args': {b'biz': b'baz', b'key': b'val'},
   250                 b'redirect': None,
   421                 b'redirect': None,
   251                 b'data': None,
   422                 b'data': None,
   252             })
   423             },
   253 
   424         )
   254     def testconflictingrequestid(self):
   425         self.assertEqual(
   255         """Request ID for new command matching in-flight command is illegal."""
   426             results[5][1],
   256         results = list(sendframes(makereactor(), [
   427             {
   257             ffs(b'1 1 stream-begin command-request new|more '
   428                 b'requestid': 1,
   258                 b"cbor:{b'name': b'command'}"),
   429                 b'command': b'command1',
   259             ffs(b'1 1 0 command-request new '
   430                 b'args': {b'foo': b'bar', b'key1': b'val'},
   260                 b"cbor:{b'name': b'command1'}"),
   431                 b'redirect': None,
   261         ]))
   432                 b'data': None,
   262 
   433             },
   263         self.assertaction(results[0], b'wantframe')
   434         )
   264         self.assertaction(results[1], b'error')
       
   265         self.assertEqual(results[1][1], {
       
   266             b'message': b'request with ID 1 already received',
       
   267         })
       
   268 
       
   269     def testinterleavedcommands(self):
       
   270         cbor1 = b''.join(cborutil.streamencode({
       
   271             b'name': b'command1',
       
   272             b'args': {
       
   273                 b'foo': b'bar',
       
   274                 b'key1': b'val',
       
   275             }
       
   276         }))
       
   277         cbor3 = b''.join(cborutil.streamencode({
       
   278             b'name': b'command3',
       
   279             b'args': {
       
   280                 b'biz': b'baz',
       
   281                 b'key': b'val',
       
   282             },
       
   283         }))
       
   284 
       
   285         results = list(sendframes(makereactor(), [
       
   286             ffs(b'1 1 stream-begin command-request new|more %s' % cbor1[0:6]),
       
   287             ffs(b'3 1 0 command-request new|more %s' % cbor3[0:10]),
       
   288             ffs(b'1 1 0 command-request continuation|more %s' % cbor1[6:9]),
       
   289             ffs(b'3 1 0 command-request continuation|more %s' % cbor3[10:13]),
       
   290             ffs(b'3 1 0 command-request continuation %s' % cbor3[13:]),
       
   291             ffs(b'1 1 0 command-request continuation %s' % cbor1[9:]),
       
   292         ]))
       
   293 
       
   294         self.assertEqual([t[0] for t in results], [
       
   295             b'wantframe',
       
   296             b'wantframe',
       
   297             b'wantframe',
       
   298             b'wantframe',
       
   299             b'runcommand',
       
   300             b'runcommand',
       
   301         ])
       
   302 
       
   303         self.assertEqual(results[4][1], {
       
   304             b'requestid': 3,
       
   305             b'command': b'command3',
       
   306             b'args': {b'biz': b'baz', b'key': b'val'},
       
   307             b'redirect': None,
       
   308             b'data': None,
       
   309         })
       
   310         self.assertEqual(results[5][1], {
       
   311             b'requestid': 1,
       
   312             b'command': b'command1',
       
   313             b'args': {b'foo': b'bar', b'key1': b'val'},
       
   314             b'redirect': None,
       
   315             b'data': None,
       
   316         })
       
   317 
   435 
   318     def testmissingcommanddataframe(self):
   436     def testmissingcommanddataframe(self):
   319         # The reactor doesn't currently handle partially received commands.
   437         # The reactor doesn't currently handle partially received commands.
   320         # So this test is failing to do anything with request 1.
   438         # So this test is failing to do anything with request 1.
   321         frames = [
   439         frames = [
   322             ffs(b'1 1 stream-begin command-request new|have-data '
   440             ffs(
   323                 b"cbor:{b'name': b'command1'}"),
   441                 b'1 1 stream-begin command-request new|have-data '
   324             ffs(b'3 1 0 command-request new '
   442                 b"cbor:{b'name': b'command1'}"
   325                 b"cbor:{b'name': b'command2'}"),
   443             ),
       
   444             ffs(b'3 1 0 command-request new ' b"cbor:{b'name': b'command2'}"),
   326         ]
   445         ]
   327         results = list(sendframes(makereactor(), frames))
   446         results = list(sendframes(makereactor(), frames))
   328         self.assertEqual(len(results), 2)
   447         self.assertEqual(len(results), 2)
   329         self.assertaction(results[0], b'wantframe')
   448         self.assertaction(results[0], b'wantframe')
   330         self.assertaction(results[1], b'runcommand')
   449         self.assertaction(results[1], b'runcommand')
   331 
   450 
   332     def testmissingcommanddataframeflags(self):
   451     def testmissingcommanddataframeflags(self):
   333         frames = [
   452         frames = [
   334             ffs(b'1 1 stream-begin command-request new|have-data '
   453             ffs(
   335                 b"cbor:{b'name': b'command1'}"),
   454                 b'1 1 stream-begin command-request new|have-data '
       
   455                 b"cbor:{b'name': b'command1'}"
       
   456             ),
   336             ffs(b'1 1 0 command-data 0 data'),
   457             ffs(b'1 1 0 command-data 0 data'),
   337         ]
   458         ]
   338         results = list(sendframes(makereactor(), frames))
   459         results = list(sendframes(makereactor(), frames))
   339         self.assertEqual(len(results), 2)
   460         self.assertEqual(len(results), 2)
   340         self.assertaction(results[0], b'wantframe')
   461         self.assertaction(results[0], b'wantframe')
   341         self.assertaction(results[1], b'error')
   462         self.assertaction(results[1], b'error')
   342         self.assertEqual(results[1][1], {
   463         self.assertEqual(
   343             b'message': b'command data frame without flags',
   464             results[1][1], {b'message': b'command data frame without flags',}
   344         })
   465         )
   345 
   466 
   346     def testframefornonreceivingrequest(self):
   467     def testframefornonreceivingrequest(self):
   347         """Receiving a frame for a command that is not receiving is illegal."""
   468         """Receiving a frame for a command that is not receiving is illegal."""
   348         results = list(sendframes(makereactor(), [
   469         results = list(
   349             ffs(b'1 1 stream-begin command-request new '
   470             sendframes(
   350                 b"cbor:{b'name': b'command1'}"),
   471                 makereactor(),
   351             ffs(b'3 1 0 command-request new|have-data '
   472                 [
   352                 b"cbor:{b'name': b'command3'}"),
   473                     ffs(
   353             ffs(b'5 1 0 command-data eos ignored'),
   474                         b'1 1 stream-begin command-request new '
   354         ]))
   475                         b"cbor:{b'name': b'command1'}"
       
   476                     ),
       
   477                     ffs(
       
   478                         b'3 1 0 command-request new|have-data '
       
   479                         b"cbor:{b'name': b'command3'}"
       
   480                     ),
       
   481                     ffs(b'5 1 0 command-data eos ignored'),
       
   482                 ],
       
   483             )
       
   484         )
   355         self.assertaction(results[2], b'error')
   485         self.assertaction(results[2], b'error')
   356         self.assertEqual(results[2][1], {
   486         self.assertEqual(
   357             b'message': b'received frame for request that is not receiving: 5',
   487             results[2][1],
   358         })
   488             {
       
   489                 b'message': b'received frame for request that is not receiving: 5',
       
   490             },
       
   491         )
   359 
   492 
   360     def testsimpleresponse(self):
   493     def testsimpleresponse(self):
   361         """Bytes response to command sends result frames."""
   494         """Bytes response to command sends result frames."""
   362         reactor = makereactor()
   495         reactor = makereactor()
   363         instream = framing.stream(1)
   496         instream = framing.stream(1)
   364         list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
   497         list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
   365 
   498 
   366         outstream = reactor.makeoutputstream()
   499         outstream = reactor.makeoutputstream()
   367         result = reactor.oncommandresponsereadyobjects(
   500         result = reactor.oncommandresponsereadyobjects(
   368             outstream, 1, [b'response'])
   501             outstream, 1, [b'response']
       
   502         )
   369         self.assertaction(result, b'sendframes')
   503         self.assertaction(result, b'sendframes')
   370         self.assertframesequal(result[1][b'framegen'], [
   504         self.assertframesequal(
   371             b'1 2 stream-begin stream-settings eos cbor:b"identity"',
   505             result[1][b'framegen'],
   372             b'1 2 encoded command-response continuation %s' % OK,
   506             [
   373             b'1 2 encoded command-response continuation cbor:b"response"',
   507                 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
   374             b'1 2 0 command-response eos ',
   508                 b'1 2 encoded command-response continuation %s' % OK,
   375         ])
   509                 b'1 2 encoded command-response continuation cbor:b"response"',
       
   510                 b'1 2 0 command-response eos ',
       
   511             ],
       
   512         )
   376 
   513 
   377     def testmultiframeresponse(self):
   514     def testmultiframeresponse(self):
   378         """Bytes response spanning multiple frames is handled."""
   515         """Bytes response spanning multiple frames is handled."""
   379         first = b'x' * framing.DEFAULT_MAX_FRAME_SIZE
   516         first = b'x' * framing.DEFAULT_MAX_FRAME_SIZE
   380         second = b'y' * 100
   517         second = b'y' * 100
   383         instream = framing.stream(1)
   520         instream = framing.stream(1)
   384         list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
   521         list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
   385 
   522 
   386         outstream = reactor.makeoutputstream()
   523         outstream = reactor.makeoutputstream()
   387         result = reactor.oncommandresponsereadyobjects(
   524         result = reactor.oncommandresponsereadyobjects(
   388             outstream, 1, [first + second])
   525             outstream, 1, [first + second]
       
   526         )
   389         self.assertaction(result, b'sendframes')
   527         self.assertaction(result, b'sendframes')
   390         self.assertframesequal(result[1][b'framegen'], [
   528         self.assertframesequal(
   391             b'1 2 stream-begin stream-settings eos cbor:b"identity"',
   529             result[1][b'framegen'],
   392             b'1 2 encoded command-response continuation %s' % OK,
   530             [
   393             b'1 2 encoded command-response continuation Y\x80d',
   531                 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
   394             b'1 2 encoded command-response continuation %s' % first,
   532                 b'1 2 encoded command-response continuation %s' % OK,
   395             b'1 2 encoded command-response continuation %s' % second,
   533                 b'1 2 encoded command-response continuation Y\x80d',
   396             b'1 2 0 command-response eos '
   534                 b'1 2 encoded command-response continuation %s' % first,
   397         ])
   535                 b'1 2 encoded command-response continuation %s' % second,
       
   536                 b'1 2 0 command-response eos ',
       
   537             ],
       
   538         )
   398 
   539 
   399     def testservererror(self):
   540     def testservererror(self):
   400         reactor = makereactor()
   541         reactor = makereactor()
   401         instream = framing.stream(1)
   542         instream = framing.stream(1)
   402         list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
   543         list(sendcommandframes(reactor, instream, 1, b'mycommand', {}))
   403 
   544 
   404         outstream = reactor.makeoutputstream()
   545         outstream = reactor.makeoutputstream()
   405         result = reactor.onservererror(outstream, 1, b'some message')
   546         result = reactor.onservererror(outstream, 1, b'some message')
   406         self.assertaction(result, b'sendframes')
   547         self.assertaction(result, b'sendframes')
   407         self.assertframesequal(result[1][b'framegen'], [
   548         self.assertframesequal(
   408             b"1 2 stream-begin error-response 0 "
   549             result[1][b'framegen'],
   409             b"cbor:{b'type': b'server', "
   550             [
   410             b"b'message': [{b'msg': b'some message'}]}",
   551                 b"1 2 stream-begin error-response 0 "
   411         ])
   552                 b"cbor:{b'type': b'server', "
       
   553                 b"b'message': [{b'msg': b'some message'}]}",
       
   554             ],
       
   555         )
   412 
   556 
   413     def test1commanddeferresponse(self):
   557     def test1commanddeferresponse(self):
   414         """Responses when in deferred output mode are delayed until EOF."""
   558         """Responses when in deferred output mode are delayed until EOF."""
   415         reactor = makereactor(deferoutput=True)
   559         reactor = makereactor(deferoutput=True)
   416         instream = framing.stream(1)
   560         instream = framing.stream(1)
   417         results = list(sendcommandframes(reactor, instream, 1, b'mycommand',
   561         results = list(
   418                                          {}))
   562             sendcommandframes(reactor, instream, 1, b'mycommand', {})
       
   563         )
   419         self.assertEqual(len(results), 1)
   564         self.assertEqual(len(results), 1)
   420         self.assertaction(results[0], b'runcommand')
   565         self.assertaction(results[0], b'runcommand')
   421 
   566 
   422         outstream = reactor.makeoutputstream()
   567         outstream = reactor.makeoutputstream()
   423         result = reactor.oncommandresponsereadyobjects(
   568         result = reactor.oncommandresponsereadyobjects(
   424             outstream, 1, [b'response'])
   569             outstream, 1, [b'response']
       
   570         )
   425         self.assertaction(result, b'noop')
   571         self.assertaction(result, b'noop')
   426         result = reactor.oninputeof()
   572         result = reactor.oninputeof()
   427         self.assertaction(result, b'sendframes')
   573         self.assertaction(result, b'sendframes')
   428         self.assertframesequal(result[1][b'framegen'], [
   574         self.assertframesequal(
   429             b'1 2 stream-begin stream-settings eos cbor:b"identity"',
   575             result[1][b'framegen'],
   430             b'1 2 encoded command-response continuation %s' % OK,
   576             [
   431             b'1 2 encoded command-response continuation cbor:b"response"',
   577                 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
   432             b'1 2 0 command-response eos ',
   578                 b'1 2 encoded command-response continuation %s' % OK,
   433         ])
   579                 b'1 2 encoded command-response continuation cbor:b"response"',
       
   580                 b'1 2 0 command-response eos ',
       
   581             ],
       
   582         )
   434 
   583 
   435     def testmultiplecommanddeferresponse(self):
   584     def testmultiplecommanddeferresponse(self):
   436         reactor = makereactor(deferoutput=True)
   585         reactor = makereactor(deferoutput=True)
   437         instream = framing.stream(1)
   586         instream = framing.stream(1)
   438         list(sendcommandframes(reactor, instream, 1, b'command1', {}))
   587         list(sendcommandframes(reactor, instream, 1, b'command1', {}))
   439         list(sendcommandframes(reactor, instream, 3, b'command2', {}))
   588         list(sendcommandframes(reactor, instream, 3, b'command2', {}))
   440 
   589 
   441         outstream = reactor.makeoutputstream()
   590         outstream = reactor.makeoutputstream()
   442         result = reactor.oncommandresponsereadyobjects(
   591         result = reactor.oncommandresponsereadyobjects(
   443             outstream, 1, [b'response1'])
   592             outstream, 1, [b'response1']
       
   593         )
   444         self.assertaction(result, b'noop')
   594         self.assertaction(result, b'noop')
   445         result = reactor.oncommandresponsereadyobjects(
   595         result = reactor.oncommandresponsereadyobjects(
   446             outstream, 3, [b'response2'])
   596             outstream, 3, [b'response2']
       
   597         )
   447         self.assertaction(result, b'noop')
   598         self.assertaction(result, b'noop')
   448         result = reactor.oninputeof()
   599         result = reactor.oninputeof()
   449         self.assertaction(result, b'sendframes')
   600         self.assertaction(result, b'sendframes')
   450         self.assertframesequal(result[1][b'framegen'], [
   601         self.assertframesequal(
   451             b'1 2 stream-begin stream-settings eos cbor:b"identity"',
   602             result[1][b'framegen'],
   452             b'1 2 encoded command-response continuation %s' % OK,
   603             [
   453             b'1 2 encoded command-response continuation cbor:b"response1"',
   604                 b'1 2 stream-begin stream-settings eos cbor:b"identity"',
   454             b'1 2 0 command-response eos ',
   605                 b'1 2 encoded command-response continuation %s' % OK,
   455             b'3 2 encoded command-response continuation %s' % OK,
   606                 b'1 2 encoded command-response continuation cbor:b"response1"',
   456             b'3 2 encoded command-response continuation cbor:b"response2"',
   607                 b'1 2 0 command-response eos ',
   457             b'3 2 0 command-response eos ',
   608                 b'3 2 encoded command-response continuation %s' % OK,
   458         ])
   609                 b'3 2 encoded command-response continuation cbor:b"response2"',
       
   610                 b'3 2 0 command-response eos ',
       
   611             ],
       
   612         )
   459 
   613 
   460     def testrequestidtracking(self):
   614     def testrequestidtracking(self):
   461         reactor = makereactor(deferoutput=True)
   615         reactor = makereactor(deferoutput=True)
   462         instream = framing.stream(1)
   616         instream = framing.stream(1)
   463         list(sendcommandframes(reactor, instream, 1, b'command1', {}))
   617         list(sendcommandframes(reactor, instream, 1, b'command1', {}))
   470         reactor.oncommandresponsereadyobjects(outstream, 1, [b'response1'])
   624         reactor.oncommandresponsereadyobjects(outstream, 1, [b'response1'])
   471         reactor.oncommandresponsereadyobjects(outstream, 5, [b'response5'])
   625         reactor.oncommandresponsereadyobjects(outstream, 5, [b'response5'])
   472 
   626 
   473         result = reactor.oninputeof()
   627         result = reactor.oninputeof()
   474         self.assertaction(result, b'sendframes')
   628         self.assertaction(result, b'sendframes')
   475         self.assertframesequal(result[1][b'framegen'], [
   629         self.assertframesequal(
   476             b'3 2 stream-begin stream-settings eos cbor:b"identity"',
   630             result[1][b'framegen'],
   477             b'3 2 encoded command-response continuation %s' % OK,
   631             [
   478             b'3 2 encoded command-response continuation cbor:b"response3"',
   632                 b'3 2 stream-begin stream-settings eos cbor:b"identity"',
   479             b'3 2 0 command-response eos ',
   633                 b'3 2 encoded command-response continuation %s' % OK,
   480             b'1 2 encoded command-response continuation %s' % OK,
   634                 b'3 2 encoded command-response continuation cbor:b"response3"',
   481             b'1 2 encoded command-response continuation cbor:b"response1"',
   635                 b'3 2 0 command-response eos ',
   482             b'1 2 0 command-response eos ',
   636                 b'1 2 encoded command-response continuation %s' % OK,
   483             b'5 2 encoded command-response continuation %s' % OK,
   637                 b'1 2 encoded command-response continuation cbor:b"response1"',
   484             b'5 2 encoded command-response continuation cbor:b"response5"',
   638                 b'1 2 0 command-response eos ',
   485             b'5 2 0 command-response eos ',
   639                 b'5 2 encoded command-response continuation %s' % OK,
   486         ])
   640                 b'5 2 encoded command-response continuation cbor:b"response5"',
       
   641                 b'5 2 0 command-response eos ',
       
   642             ],
       
   643         )
   487 
   644 
   488     def testduplicaterequestonactivecommand(self):
   645     def testduplicaterequestonactivecommand(self):
   489         """Receiving a request ID that matches a request that isn't finished."""
   646         """Receiving a request ID that matches a request that isn't finished."""
   490         reactor = makereactor()
   647         reactor = makereactor()
   491         stream = framing.stream(1)
   648         stream = framing.stream(1)
   492         list(sendcommandframes(reactor, stream, 1, b'command1', {}))
   649         list(sendcommandframes(reactor, stream, 1, b'command1', {}))
   493         results = list(sendcommandframes(reactor, stream, 1, b'command1', {}))
   650         results = list(sendcommandframes(reactor, stream, 1, b'command1', {}))
   494 
   651 
   495         self.assertaction(results[0], b'error')
   652         self.assertaction(results[0], b'error')
   496         self.assertEqual(results[0][1], {
   653         self.assertEqual(
   497             b'message': b'request with ID 1 is already active',
   654             results[0][1], {b'message': b'request with ID 1 is already active',}
   498         })
   655         )
   499 
   656 
   500     def testduplicaterequestonactivecommandnosend(self):
   657     def testduplicaterequestonactivecommandnosend(self):
   501         """Same as above but we've registered a response but haven't sent it."""
   658         """Same as above but we've registered a response but haven't sent it."""
   502         reactor = makereactor()
   659         reactor = makereactor()
   503         instream = framing.stream(1)
   660         instream = framing.stream(1)
   508         # We've registered the response but haven't sent it. From the
   665         # We've registered the response but haven't sent it. From the
   509         # perspective of the reactor, the command is still active.
   666         # perspective of the reactor, the command is still active.
   510 
   667 
   511         results = list(sendcommandframes(reactor, instream, 1, b'command1', {}))
   668         results = list(sendcommandframes(reactor, instream, 1, b'command1', {}))
   512         self.assertaction(results[0], b'error')
   669         self.assertaction(results[0], b'error')
   513         self.assertEqual(results[0][1], {
   670         self.assertEqual(
   514             b'message': b'request with ID 1 is already active',
   671             results[0][1], {b'message': b'request with ID 1 is already active',}
   515         })
   672         )
   516 
   673 
   517     def testduplicaterequestaftersend(self):
   674     def testduplicaterequestaftersend(self):
   518         """We can use a duplicate request ID after we've sent the response."""
   675         """We can use a duplicate request ID after we've sent the response."""
   519         reactor = makereactor()
   676         reactor = makereactor()
   520         instream = framing.stream(1)
   677         instream = framing.stream(1)
   526         results = list(sendcommandframes(reactor, instream, 1, b'command1', {}))
   683         results = list(sendcommandframes(reactor, instream, 1, b'command1', {}))
   527         self.assertaction(results[0], b'runcommand')
   684         self.assertaction(results[0], b'runcommand')
   528 
   685 
   529     def testprotocolsettingsnoflags(self):
   686     def testprotocolsettingsnoflags(self):
   530         result = self._sendsingleframe(
   687         result = self._sendsingleframe(
       
   688             makereactor(), ffs(b'0 1 stream-begin sender-protocol-settings 0 ')
       
   689         )
       
   690         self.assertaction(result, b'error')
       
   691         self.assertEqual(
       
   692             result[1],
       
   693             {
       
   694                 b'message': b'sender protocol settings frame must have '
       
   695                 b'continuation or end of stream flag set',
       
   696             },
       
   697         )
       
   698 
       
   699     def testprotocolsettingsconflictflags(self):
       
   700         result = self._sendsingleframe(
   531             makereactor(),
   701             makereactor(),
   532             ffs(b'0 1 stream-begin sender-protocol-settings 0 '))
   702             ffs(b'0 1 stream-begin sender-protocol-settings continuation|eos '),
       
   703         )
   533         self.assertaction(result, b'error')
   704         self.assertaction(result, b'error')
   534         self.assertEqual(result[1], {
   705         self.assertEqual(
   535             b'message': b'sender protocol settings frame must have '
   706             result[1],
   536                         b'continuation or end of stream flag set',
   707             {
   537         })
   708                 b'message': b'sender protocol settings frame cannot have both '
   538 
   709                 b'continuation and end of stream flags set',
   539     def testprotocolsettingsconflictflags(self):
   710             },
       
   711         )
       
   712 
       
   713     def testprotocolsettingsemptypayload(self):
   540         result = self._sendsingleframe(
   714         result = self._sendsingleframe(
   541             makereactor(),
   715             makereactor(),
   542             ffs(b'0 1 stream-begin sender-protocol-settings continuation|eos '))
   716             ffs(b'0 1 stream-begin sender-protocol-settings eos '),
       
   717         )
   543         self.assertaction(result, b'error')
   718         self.assertaction(result, b'error')
   544         self.assertEqual(result[1], {
   719         self.assertEqual(
   545             b'message': b'sender protocol settings frame cannot have both '
   720             result[1],
   546                         b'continuation and end of stream flags set',
   721             {
   547         })
   722                 b'message': b'sender protocol settings frame did not contain CBOR '
   548 
   723                 b'data',
   549     def testprotocolsettingsemptypayload(self):
   724             },
       
   725         )
       
   726 
       
   727     def testprotocolsettingsmultipleobjects(self):
   550         result = self._sendsingleframe(
   728         result = self._sendsingleframe(
   551             makereactor(),
   729             makereactor(),
   552             ffs(b'0 1 stream-begin sender-protocol-settings eos '))
   730             ffs(
       
   731                 b'0 1 stream-begin sender-protocol-settings eos '
       
   732                 b'\x46foobar\x43foo'
       
   733             ),
       
   734         )
   553         self.assertaction(result, b'error')
   735         self.assertaction(result, b'error')
   554         self.assertEqual(result[1], {
   736         self.assertEqual(
   555             b'message': b'sender protocol settings frame did not contain CBOR '
   737             result[1],
   556                         b'data',
   738             {
   557         })
   739                 b'message': b'sender protocol settings frame contained multiple '
   558 
   740                 b'CBOR values',
   559     def testprotocolsettingsmultipleobjects(self):
   741             },
   560         result = self._sendsingleframe(
   742         )
   561             makereactor(),
       
   562             ffs(b'0 1 stream-begin sender-protocol-settings eos '
       
   563                 b'\x46foobar\x43foo'))
       
   564         self.assertaction(result, b'error')
       
   565         self.assertEqual(result[1], {
       
   566             b'message': b'sender protocol settings frame contained multiple '
       
   567                         b'CBOR values',
       
   568         })
       
   569 
   743 
   570     def testprotocolsettingscontentencodings(self):
   744     def testprotocolsettingscontentencodings(self):
   571         reactor = makereactor()
   745         reactor = makereactor()
   572 
   746 
   573         result = self._sendsingleframe(
   747         result = self._sendsingleframe(
   574             reactor,
   748             reactor,
   575             ffs(b'0 1 stream-begin sender-protocol-settings eos '
   749             ffs(
   576                 b'cbor:{b"contentencodings": [b"a", b"b"]}'))
   750                 b'0 1 stream-begin sender-protocol-settings eos '
       
   751                 b'cbor:{b"contentencodings": [b"a", b"b"]}'
       
   752             ),
       
   753         )
   577         self.assertaction(result, b'wantframe')
   754         self.assertaction(result, b'wantframe')
   578 
   755 
   579         self.assertEqual(reactor._state, b'idle')
   756         self.assertEqual(reactor._state, b'idle')
   580         self.assertEqual(reactor._sendersettings[b'contentencodings'],
   757         self.assertEqual(
   581                          [b'a', b'b'])
   758             reactor._sendersettings[b'contentencodings'], [b'a', b'b']
       
   759         )
   582 
   760 
   583     def testprotocolsettingsmultipleframes(self):
   761     def testprotocolsettingsmultipleframes(self):
   584         reactor = makereactor()
   762         reactor = makereactor()
   585 
   763 
   586         data = b''.join(cborutil.streamencode({
   764         data = b''.join(
   587             b'contentencodings': [b'value1', b'value2'],
   765             cborutil.streamencode(
   588         }))
   766                 {b'contentencodings': [b'value1', b'value2'],}
   589 
   767             )
   590         results = list(sendframes(reactor, [
   768         )
   591             ffs(b'0 1 stream-begin sender-protocol-settings continuation %s' %
   769 
   592                 data[0:5]),
   770         results = list(
   593             ffs(b'0 1 0 sender-protocol-settings eos %s' % data[5:]),
   771             sendframes(
   594         ]))
   772                 reactor,
       
   773                 [
       
   774                     ffs(
       
   775                         b'0 1 stream-begin sender-protocol-settings continuation %s'
       
   776                         % data[0:5]
       
   777                     ),
       
   778                     ffs(b'0 1 0 sender-protocol-settings eos %s' % data[5:]),
       
   779                 ],
       
   780             )
       
   781         )
   595 
   782 
   596         self.assertEqual(len(results), 2)
   783         self.assertEqual(len(results), 2)
   597 
   784 
   598         self.assertaction(results[0], b'wantframe')
   785         self.assertaction(results[0], b'wantframe')
   599         self.assertaction(results[1], b'wantframe')
   786         self.assertaction(results[1], b'wantframe')
   600 
   787 
   601         self.assertEqual(reactor._state, b'idle')
   788         self.assertEqual(reactor._state, b'idle')
   602         self.assertEqual(reactor._sendersettings[b'contentencodings'],
   789         self.assertEqual(
   603                          [b'value1', b'value2'])
   790             reactor._sendersettings[b'contentencodings'], [b'value1', b'value2']
       
   791         )
   604 
   792 
   605     def testprotocolsettingsbadcbor(self):
   793     def testprotocolsettingsbadcbor(self):
   606         result = self._sendsingleframe(
   794         result = self._sendsingleframe(
   607             makereactor(),
   795             makereactor(),
   608             ffs(b'0 1 stream-begin sender-protocol-settings eos badvalue'))
   796             ffs(b'0 1 stream-begin sender-protocol-settings eos badvalue'),
       
   797         )
   609         self.assertaction(result, b'error')
   798         self.assertaction(result, b'error')
   610 
   799 
   611     def testprotocolsettingsnoninitial(self):
   800     def testprotocolsettingsnoninitial(self):
   612         # Cannot have protocol settings frames as non-initial frames.
   801         # Cannot have protocol settings frames as non-initial frames.
   613         reactor = makereactor()
   802         reactor = makereactor()
   616         results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {}))
   805         results = list(sendcommandframes(reactor, stream, 1, b'mycommand', {}))
   617         self.assertEqual(len(results), 1)
   806         self.assertEqual(len(results), 1)
   618         self.assertaction(results[0], b'runcommand')
   807         self.assertaction(results[0], b'runcommand')
   619 
   808 
   620         result = self._sendsingleframe(
   809         result = self._sendsingleframe(
   621             reactor,
   810             reactor, ffs(b'0 1 0 sender-protocol-settings eos ')
   622             ffs(b'0 1 0 sender-protocol-settings eos '))
   811         )
   623         self.assertaction(result, b'error')
   812         self.assertaction(result, b'error')
   624         self.assertEqual(result[1], {
   813         self.assertEqual(
   625             b'message': b'expected command request frame; got 8',
   814             result[1], {b'message': b'expected command request frame; got 8',}
   626         })
   815         )
       
   816 
   627 
   817 
   628 if __name__ == '__main__':
   818 if __name__ == '__main__':
   629     import silenttestrunner
   819     import silenttestrunner
       
   820 
   630     silenttestrunner.main(__name__)
   821     silenttestrunner.main(__name__)