/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_smart_transport.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-22 00:56:52 UTC
  • mfrom: (6621.2.26 py3_pokes)
  • Revision ID: jelmer@jelmer.uk-20170522005652-yjahcr9hwmjkno7n
Merge Python3 porting work ('py3 pokes')

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""Tests for smart transport"""
18
18
 
19
19
# all of this deals with byte strings so this is safe
20
 
from cStringIO import StringIO
21
20
import doctest
22
21
import errno
23
22
import os
30
29
from testtools.matchers import DocTestMatches
31
30
 
32
31
import breezy
33
 
from breezy import (
 
32
from .. import (
34
33
        bzrdir,
35
34
        controldir,
36
35
        debug,
40
39
        transport as _mod_transport,
41
40
        urlutils,
42
41
        )
43
 
from breezy.smart import (
 
42
from ..sixish import (
 
43
    BytesIO,
 
44
    )
 
45
from ..smart import (
44
46
        client,
45
47
        medium,
46
48
        message,
48
50
        request as _mod_request,
49
51
        server as _mod_server,
50
52
        vfs,
51
 
)
52
 
from breezy.tests import (
 
53
    )
 
54
from . import (
53
55
    features,
54
56
    test_smart,
55
57
    test_server,
56
58
    )
57
 
from breezy.transport import (
 
59
from ..transport import (
58
60
        http,
59
61
        local,
60
62
        memory,
86
88
    return server_sock, client_sock
87
89
 
88
90
 
89
 
class StringIOSSHVendor(object):
90
 
    """A SSH vendor that uses StringIO to buffer writes and answer reads."""
 
91
class BytesIOSSHVendor(object):
 
92
    """A SSH vendor that uses BytesIO to buffer writes and answer reads."""
91
93
 
92
94
    def __init__(self, read_from, write_to):
93
95
        self.read_from = read_from
97
99
    def connect_ssh(self, username, password, host, port, command):
98
100
        self.calls.append(('connect_ssh', username, password, host, port,
99
101
            command))
100
 
        return StringIOSSHConnection(self)
101
 
 
102
 
 
103
 
class FirstRejectedStringIOSSHVendor(StringIOSSHVendor):
 
102
        return BytesIOSSHConnection(self)
 
103
 
 
104
 
 
105
class FirstRejectedBytesIOSSHVendor(BytesIOSSHVendor):
104
106
    """The first connection will be considered closed.
105
107
 
106
108
    The second connection will succeed normally.
107
109
    """
108
110
 
109
111
    def __init__(self, read_from, write_to, fail_at_write=True):
110
 
        super(FirstRejectedStringIOSSHVendor, self).__init__(read_from,
 
112
        super(FirstRejectedBytesIOSSHVendor, self).__init__(read_from,
111
113
            write_to)
112
114
        self.fail_at_write = fail_at_write
113
115
        self._first = True
118
120
        if self._first:
119
121
            self._first = False
120
122
            return ClosedSSHConnection(self)
121
 
        return StringIOSSHConnection(self)
122
 
 
123
 
 
124
 
class StringIOSSHConnection(ssh.SSHConnection):
125
 
    """A SSH connection that uses StringIO to buffer writes and answer reads."""
 
123
        return BytesIOSSHConnection(self)
 
124
 
 
125
 
 
126
class BytesIOSSHConnection(ssh.SSHConnection):
 
127
    """A SSH connection that uses BytesIO to buffer writes and answer reads."""
126
128
 
127
129
    def __init__(self, vendor):
128
130
        self.vendor = vendor
236
238
        # of the SmartClientStreamMediumRequest and the SmartClientStreamMedium
237
239
        # classes - as the sibling classes share this logic, they do not have
238
240
        # explicit tests for this.
239
 
        output = StringIO()
 
241
        output = BytesIO()
240
242
        client_medium = medium.SmartSimplePipesClientMedium(
241
243
            None, output, 'base')
242
244
        request = client_medium.get_request()
248
250
 
249
251
    def test_simple_pipes_client__accept_bytes_writes_to_writable(self):
250
252
        # accept_bytes writes to the writeable pipe.
251
 
        output = StringIO()
 
253
        output = BytesIO()
252
254
        client_medium = medium.SmartSimplePipesClientMedium(
253
255
            None, output, 'base')
254
256
        client_medium._accept_bytes('abc')
341
343
 
342
344
    def test_simple_pipes_client_disconnect_does_nothing(self):
343
345
        # calling disconnect does nothing.
344
 
        input = StringIO()
345
 
        output = StringIO()
 
346
        input = BytesIO()
 
347
        output = BytesIO()
346
348
        client_medium = medium.SmartSimplePipesClientMedium(
347
349
            input, output, 'base')
348
350
        # send some bytes to ensure disconnecting after activity still does not
355
357
    def test_simple_pipes_client_accept_bytes_after_disconnect(self):
356
358
        # calling disconnect on the client does not alter the pipe that
357
359
        # accept_bytes writes to.
358
 
        input = StringIO()
359
 
        output = StringIO()
 
360
        input = BytesIO()
 
361
        output = BytesIO()
360
362
        client_medium = medium.SmartSimplePipesClientMedium(
361
363
            input, output, 'base')
362
364
        client_medium._accept_bytes('abc')
375
377
    def test_simple_pipes_client_can_always_read(self):
376
378
        # SmartSimplePipesClientMedium is never disconnected, so read_bytes
377
379
        # always tries to read from the underlying pipe.
378
 
        input = StringIO('abcdef')
 
380
        input = BytesIO(b'abcdef')
379
381
        client_medium = medium.SmartSimplePipesClientMedium(input, None, 'base')
380
382
        self.assertEqual('abc', client_medium.read_bytes(3))
381
383
        client_medium.disconnect()
385
387
        # invoking _flush on a SimplePipesClient should flush the output
386
388
        # pipe. We test this by creating an output pipe that records
387
389
        # flush calls made to it.
388
 
        from StringIO import StringIO # get regular StringIO
389
 
        input = StringIO()
390
 
        output = StringIO()
 
390
        from io import BytesIO # get regular BytesIO
 
391
        input = BytesIO()
 
392
        output = BytesIO()
391
393
        flush_calls = []
392
394
        def logging_flush(): flush_calls.append('flush')
393
395
        output.flush = logging_flush
419
421
    def test_ssh_client_connects_on_first_use(self):
420
422
        # The only thing that initiates a connection from the medium is giving
421
423
        # it bytes.
422
 
        output = StringIO()
423
 
        vendor = StringIOSSHVendor(StringIO(), output)
 
424
        output = BytesIO()
 
425
        vendor = BytesIOSSHVendor(BytesIO(), output)
424
426
        ssh_params = medium.SSHParams(
425
427
            'a hostname', 'a port', 'a username', 'a password', 'bzr')
426
428
        client_medium = medium.SmartSSHClientMedium('base', ssh_params, vendor)
434
436
    def test_ssh_client_changes_command_when_bzr_remote_path_passed(self):
435
437
        # The only thing that initiates a connection from the medium is giving
436
438
        # it bytes.
437
 
        output = StringIO()
438
 
        vendor = StringIOSSHVendor(StringIO(), output)
 
439
        output = BytesIO()
 
440
        vendor = BytesIOSSHVendor(BytesIO(), output)
439
441
        ssh_params = medium.SSHParams(
440
442
            'a hostname', 'a port', 'a username', 'a password',
441
443
            bzr_remote_path='fugly')
450
452
    def test_ssh_client_disconnect_does_so(self):
451
453
        # calling disconnect should disconnect both the read_from and write_to
452
454
        # file-like object it from the ssh connection.
453
 
        input = StringIO()
454
 
        output = StringIO()
455
 
        vendor = StringIOSSHVendor(input, output)
 
455
        input = BytesIO()
 
456
        output = BytesIO()
 
457
        vendor = BytesIOSSHVendor(input, output)
456
458
        client_medium = medium.SmartSSHClientMedium(
457
459
            'base', medium.SSHParams('a hostname'), vendor)
458
460
        client_medium._accept_bytes('abc')
471
473
        # not prevent additional connections occuring.
472
474
        # we test this by initiating a second connection after doing a
473
475
        # disconnect.
474
 
        input = StringIO()
475
 
        output = StringIO()
476
 
        vendor = StringIOSSHVendor(input, output)
 
476
        input = BytesIO()
 
477
        output = BytesIO()
 
478
        vendor = BytesIOSSHVendor(input, output)
477
479
        client_medium = medium.SmartSSHClientMedium(
478
480
            'base', medium.SSHParams('a hostname'), vendor)
479
481
        client_medium._accept_bytes('abc')
480
482
        client_medium.disconnect()
481
483
        # the disconnect has closed output, so we need a new output for the
482
484
        # new connection to write to.
483
 
        input2 = StringIO()
484
 
        output2 = StringIO()
 
485
        input2 = BytesIO()
 
486
        output2 = BytesIO()
485
487
        vendor.read_from = input2
486
488
        vendor.write_to = output2
487
489
        client_medium._accept_bytes('abc')
542
544
        # invoking _flush on a SSHClientMedium should flush the output
543
545
        # pipe. We test this by creating an output pipe that records
544
546
        # flush calls made to it.
545
 
        from StringIO import StringIO # get regular StringIO
546
 
        input = StringIO()
547
 
        output = StringIO()
 
547
        from io import BytesIO # get regular BytesIO
 
548
        input = BytesIO()
 
549
        output = BytesIO()
548
550
        flush_calls = []
549
551
        def logging_flush(): flush_calls.append('flush')
550
552
        output.flush = logging_flush
551
 
        vendor = StringIOSSHVendor(input, output)
 
553
        vendor = BytesIOSSHVendor(input, output)
552
554
        client_medium = medium.SmartSSHClientMedium(
553
555
            'base', medium.SSHParams('a hostname'), vendor=vendor)
554
556
        # this call is here to ensure we only flush once, not on every
648
650
        # calling accept_bytes after calling finished_writing raises
649
651
        # WritingCompleted to prevent bad assumptions on stream environments
650
652
        # breaking the needs of message-based environments.
651
 
        output = StringIO()
 
653
        output = BytesIO()
652
654
        client_medium = medium.SmartSimplePipesClientMedium(
653
655
            None, output, 'base')
654
656
        request = medium.SmartClientStreamMediumRequest(client_medium)
659
661
        # accept bytes should invoke _accept_bytes on the stream medium.
660
662
        # we test this by using the SimplePipes medium - the most trivial one
661
663
        # and checking that the pipes get the data.
662
 
        input = StringIO()
663
 
        output = StringIO()
 
664
        input = BytesIO()
 
665
        output = BytesIO()
664
666
        client_medium = medium.SmartSimplePipesClientMedium(
665
667
            input, output, 'base')
666
668
        request = medium.SmartClientStreamMediumRequest(client_medium)
673
675
    def test_construct_sets_stream_request(self):
674
676
        # constructing a SmartClientStreamMediumRequest on a StreamMedium sets
675
677
        # the current request to the new SmartClientStreamMediumRequest
676
 
        output = StringIO()
 
678
        output = BytesIO()
677
679
        client_medium = medium.SmartSimplePipesClientMedium(
678
680
            None, output, 'base')
679
681
        request = medium.SmartClientStreamMediumRequest(client_medium)
682
684
    def test_construct_while_another_request_active_throws(self):
683
685
        # constructing a SmartClientStreamMediumRequest on a StreamMedium with
684
686
        # a non-None _current_request raises TooManyConcurrentRequests.
685
 
        output = StringIO()
 
687
        output = BytesIO()
686
688
        client_medium = medium.SmartSimplePipesClientMedium(
687
689
            None, output, 'base')
688
690
        client_medium._current_request = "a"
692
694
    def test_finished_read_clears_current_request(self):
693
695
        # calling finished_reading clears the current request from the requests
694
696
        # medium
695
 
        output = StringIO()
 
697
        output = BytesIO()
696
698
        client_medium = medium.SmartSimplePipesClientMedium(
697
699
            None, output, 'base')
698
700
        request = medium.SmartClientStreamMediumRequest(client_medium)
715
717
        # faulty implementation could poke at the pipe variables them selves,
716
718
        # but we trust that this will be caught as it will break the integration
717
719
        # smoke tests.
718
 
        input = StringIO('321')
719
 
        output = StringIO()
 
720
        input = BytesIO(b'321')
 
721
        output = BytesIO()
720
722
        client_medium = medium.SmartSimplePipesClientMedium(
721
723
            input, output, 'base')
722
724
        request = medium.SmartClientStreamMediumRequest(client_medium)
739
741
        # calling read_bytes after calling finished_reading raises
740
742
        # ReadingCompleted to prevent bad assumptions on stream environments
741
743
        # breaking the needs of message-based environments.
742
 
        output = StringIO()
 
744
        output = BytesIO()
743
745
        client_medium = medium.SmartSimplePipesClientMedium(
744
746
            None, output, 'base')
745
747
        request = medium.SmartClientStreamMediumRequest(client_medium)
764
766
        self.assertIs(None, client_medium._socket)
765
767
        try:
766
768
            self.assertEqual('', client_sock.recv(1))
767
 
        except socket.error, e:
 
769
        except socket.error as e:
768
770
            if e.errno not in (errno.EBADF,):
769
771
                raise
770
772
        req = client_medium.get_request()
832
834
        """Create a SmartServerSocketStreamMedium.
833
835
 
834
836
        This differes from create_pipe_medium, in that we initialize the
835
 
        request that is sent to the server, and return the StringIO class that
 
837
        request that is sent to the server, and return the BytesIO class that
836
838
        will hold the response.
837
839
        """
838
 
        to_server = StringIO(to_server_bytes)
839
 
        from_server = StringIO()
 
840
        to_server = BytesIO(to_server_bytes)
 
841
        from_server = BytesIO()
840
842
        m = self.create_pipe_medium(to_server, from_server, transport)
841
843
        return m, from_server
842
844
 
1033
1035
        self.assertEqual('', client_sock.recv(1))
1034
1036
 
1035
1037
    def test_pipe_like_stream_error_handling(self):
1036
 
        # Use plain python StringIO so we can monkey-patch the close method to
 
1038
        # Use plain python BytesIO so we can monkey-patch the close method to
1037
1039
        # not discard the contents.
1038
 
        from StringIO import StringIO
1039
 
        to_server = StringIO('')
1040
 
        from_server = StringIO()
 
1040
        from io import BytesIO
 
1041
        to_server = BytesIO(b'')
 
1042
        from_server = BytesIO()
1041
1043
        self.closed = False
1042
1044
        def close():
1043
1045
            self.closed = True
1190
1192
 
1191
1193
    def test_pipe_wait_for_bytes_with_timeout_with_data(self):
1192
1194
        # We intentionally use a real pipe here, so that we can 'select' on it.
1193
 
        # You can't select() on a StringIO
 
1195
        # You can't select() on a BytesIO
1194
1196
        (r_server, w_client) = os.pipe()
1195
1197
        self.addCleanup(os.close, w_client)
1196
1198
        with os.fdopen(r_server, 'rb') as rf_server:
1204
1206
 
1205
1207
    def test_pipe_wait_for_bytes_with_timeout_no_data(self):
1206
1208
        # We intentionally use a real pipe here, so that we can 'select' on it.
1207
 
        # You can't select() on a StringIO
 
1209
        # You can't select() on a BytesIO
1208
1210
        (r_server, w_client) = os.pipe()
1209
1211
        # We can't add an os.close cleanup here, because we need to control
1210
1212
        # when the file handle gets closed ourselves.
1315
1317
        try:
1316
1318
            client_sock = self.connect_to_server(server)
1317
1319
            client_sock.close()
1318
 
        except socket.error, e:
 
1320
        except socket.error as e:
1319
1321
            # If the server has hung up already, that is fine.
1320
1322
            pass
1321
1323
 
1757
1759
 
1758
1760
        cmd = _mod_request.GetBundleRequest(self.get_transport(), '/')
1759
1761
        response = cmd.execute('.', rev_id)
1760
 
        bundle = serializer.read_bundle(StringIO(response.body))
 
1762
        bundle = serializer.read_bundle(BytesIO(response.body))
1761
1763
        self.assertEqual((), response.args)
1762
1764
 
1763
1765
 
1875
1877
 
1876
1878
    def test_use_connection_factory(self):
1877
1879
        # We want to be able to pass a client as a parameter to RemoteTransport.
1878
 
        input = StringIO('ok\n3\nbardone\n')
1879
 
        output = StringIO()
 
1880
        input = BytesIO(b'ok\n3\nbardone\n')
 
1881
        output = BytesIO()
1880
1882
        client_medium = medium.SmartSimplePipesClientMedium(
1881
1883
            input, output, 'base')
1882
1884
        transport = remote.RemoteTransport(
1936
1938
        # breezy.smart.client._SmartClient._build_client_protocol
1937
1939
        # XXX: make this use _SmartClient!
1938
1940
        if input_bytes is None:
1939
 
            input = StringIO()
 
1941
            input = BytesIO()
1940
1942
        else:
1941
 
            input = StringIO(input_bytes)
1942
 
        output = StringIO()
 
1943
            input = BytesIO(input_bytes)
 
1944
        output = BytesIO()
1943
1945
        client_medium = medium.SmartSimplePipesClientMedium(
1944
1946
            input, output, 'base')
1945
1947
        request = client_medium.get_request()
1963
1965
        return requester, response_handler
1964
1966
 
1965
1967
    def make_server_protocol(self):
1966
 
        out_stream = StringIO()
 
1968
        out_stream = BytesIO()
1967
1969
        smart_protocol = self.server_protocol_class(None, out_stream.write)
1968
1970
        return smart_protocol, out_stream
1969
1971
 
2075
2077
 
2076
2078
    def test_construct_version_one_client_protocol(self):
2077
2079
        # we can construct a client protocol from a client medium request
2078
 
        output = StringIO()
 
2080
        output = BytesIO()
2079
2081
        client_medium = medium.SmartSimplePipesClientMedium(
2080
2082
            None, output, 'base')
2081
2083
        request = client_medium.get_request()
2082
2084
        client_protocol = protocol.SmartClientRequestProtocolOne(request)
2083
2085
 
2084
2086
    def test_accept_bytes_of_bad_request_to_protocol(self):
2085
 
        out_stream = StringIO()
 
2087
        out_stream = BytesIO()
2086
2088
        smart_protocol = protocol.SmartServerRequestProtocolOne(
2087
2089
            None, out_stream.write)
2088
2090
        smart_protocol.accept_bytes('abc')
2108
2110
        self.overrideEnv('BRZ_NO_SMART_VFS', None)
2109
2111
        mem_transport = memory.MemoryTransport()
2110
2112
        mem_transport.put_bytes('foo', 'abcdefghij')
2111
 
        out_stream = StringIO()
 
2113
        out_stream = BytesIO()
2112
2114
        smart_protocol = protocol.SmartServerRequestProtocolOne(mem_transport,
2113
2115
                out_stream.write)
2114
2116
        smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
2118
2120
        self.assertEqual('', smart_protocol.in_buffer)
2119
2121
 
2120
2122
    def test_accept_excess_bytes_are_preserved(self):
2121
 
        out_stream = StringIO()
 
2123
        out_stream = BytesIO()
2122
2124
        smart_protocol = protocol.SmartServerRequestProtocolOne(
2123
2125
            None, out_stream.write)
2124
2126
        smart_protocol.accept_bytes('hello\nhello\n')
2137
2139
        self.assertEqual("", protocol.in_buffer)
2138
2140
 
2139
2141
    def test_accept_excess_bytes_after_dispatch(self):
2140
 
        out_stream = StringIO()
 
2142
        out_stream = BytesIO()
2141
2143
        smart_protocol = protocol.SmartServerRequestProtocolOne(
2142
2144
            None, out_stream.write)
2143
2145
        smart_protocol.accept_bytes('hello\n')
2173
2175
        # accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
2174
2176
        # response of tuple-encoded (ok, 1).  Also, separately we should test
2175
2177
        # the error if the response is a non-understood version.
2176
 
        input = StringIO('ok\x012\n')
2177
 
        output = StringIO()
 
2178
        input = BytesIO(b'ok\x012\n')
 
2179
        output = BytesIO()
2178
2180
        client_medium = medium.SmartSimplePipesClientMedium(
2179
2181
            input, output, 'base')
2180
2182
        request = client_medium.get_request()
2196
2198
    def test_client_call_with_body_bytes_uploads(self):
2197
2199
        # protocol.call_with_body_bytes should length-prefix the bytes onto the
2198
2200
        # wire.
2199
 
        expected_bytes = "foo\n7\nabcdefgdone\n"
2200
 
        input = StringIO("\n")
2201
 
        output = StringIO()
 
2201
        expected_bytes = b"foo\n7\nabcdefgdone\n"
 
2202
        input = BytesIO(b"\n")
 
2203
        output = BytesIO()
2202
2204
        client_medium = medium.SmartSimplePipesClientMedium(
2203
2205
            input, output, 'base')
2204
2206
        request = client_medium.get_request()
2209
2211
    def test_client_call_with_body_readv_array(self):
2210
2212
        # protocol.call_with_upload should encode the readv array and then
2211
2213
        # length-prefix the bytes onto the wire.
2212
 
        expected_bytes = "foo\n7\n1,2\n5,6done\n"
2213
 
        input = StringIO("\n")
2214
 
        output = StringIO()
 
2214
        expected_bytes = b"foo\n7\n1,2\n5,6done\n"
 
2215
        input = BytesIO(b"\n")
 
2216
        output = BytesIO()
2215
2217
        client_medium = medium.SmartSimplePipesClientMedium(
2216
2218
            input, output, 'base')
2217
2219
        request = client_medium.get_request()
2221
2223
 
2222
2224
    def _test_client_read_response_tuple_raises_UnknownSmartMethod(self,
2223
2225
            server_bytes):
2224
 
        input = StringIO(server_bytes)
2225
 
        output = StringIO()
 
2226
        input = BytesIO(server_bytes)
 
2227
        output = BytesIO()
2226
2228
        client_medium = medium.SmartSimplePipesClientMedium(
2227
2229
            input, output, 'base')
2228
2230
        request = client_medium.get_request()
2258
2260
    def test_client_read_body_bytes_all(self):
2259
2261
        # read_body_bytes should decode the body bytes from the wire into
2260
2262
        # a response.
2261
 
        expected_bytes = "1234567"
2262
 
        server_bytes = "ok\n7\n1234567done\n"
2263
 
        input = StringIO(server_bytes)
2264
 
        output = StringIO()
 
2263
        expected_bytes = b"1234567"
 
2264
        server_bytes = b"ok\n7\n1234567done\n"
 
2265
        input = BytesIO(server_bytes)
 
2266
        output = BytesIO()
2265
2267
        client_medium = medium.SmartSimplePipesClientMedium(
2266
2268
            input, output, 'base')
2267
2269
        request = client_medium.get_request()
2276
2278
        # to make the state machine work harder: however, as we use the
2277
2279
        # LengthPrefixedBodyDecoder that is already well tested - we can skip
2278
2280
        # that.
2279
 
        expected_bytes = "1234567"
2280
 
        server_bytes = "ok\n7\n1234567done\n"
2281
 
        input = StringIO(server_bytes)
2282
 
        output = StringIO()
 
2281
        expected_bytes = b"1234567"
 
2282
        server_bytes = b"ok\n7\n1234567done\n"
 
2283
        input = BytesIO(server_bytes)
 
2284
        output = BytesIO()
2283
2285
        client_medium = medium.SmartSimplePipesClientMedium(
2284
2286
            input, output, 'base')
2285
2287
        request = client_medium.get_request()
2294
2296
    def test_client_cancel_read_body_does_not_eat_body_bytes(self):
2295
2297
        # cancelling the expected body needs to finish the request, but not
2296
2298
        # read any more bytes.
2297
 
        expected_bytes = "1234567"
2298
 
        server_bytes = "ok\n7\n1234567done\n"
2299
 
        input = StringIO(server_bytes)
2300
 
        output = StringIO()
 
2299
        expected_bytes = b"1234567"
 
2300
        server_bytes = b"ok\n7\n1234567done\n"
 
2301
        input = BytesIO(server_bytes)
 
2302
        output = BytesIO()
2301
2303
        client_medium = medium.SmartSimplePipesClientMedium(
2302
2304
            input, output, 'base')
2303
2305
        request = client_medium.get_request()
2310
2312
            errors.ReadingCompleted, smart_protocol.read_body_bytes)
2311
2313
 
2312
2314
    def test_client_read_body_bytes_interrupted_connection(self):
2313
 
        server_bytes = "ok\n999\nincomplete body"
2314
 
        input = StringIO(server_bytes)
2315
 
        output = StringIO()
 
2315
        server_bytes = b"ok\n999\nincomplete body"
 
2316
        input = BytesIO(server_bytes)
 
2317
        output = BytesIO()
2316
2318
        client_medium = medium.SmartSimplePipesClientMedium(
2317
2319
            input, output, 'base')
2318
2320
        request = client_medium.get_request()
2341
2343
 
2342
2344
    def test_construct_version_two_client_protocol(self):
2343
2345
        # we can construct a client protocol from a client medium request
2344
 
        output = StringIO()
 
2346
        output = BytesIO()
2345
2347
        client_medium = medium.SmartSimplePipesClientMedium(
2346
2348
            None, output, 'base')
2347
2349
        request = client_medium.get_request()
2348
2350
        client_protocol = protocol.SmartClientRequestProtocolTwo(request)
2349
2351
 
2350
2352
    def test_accept_bytes_of_bad_request_to_protocol(self):
2351
 
        out_stream = StringIO()
 
2353
        out_stream = BytesIO()
2352
2354
        smart_protocol = self.server_protocol_class(None, out_stream.write)
2353
2355
        smart_protocol.accept_bytes('abc')
2354
2356
        self.assertEqual('abc', smart_protocol.in_buffer)
2374
2376
        self.overrideEnv('BRZ_NO_SMART_VFS', None)
2375
2377
        mem_transport = memory.MemoryTransport()
2376
2378
        mem_transport.put_bytes('foo', 'abcdefghij')
2377
 
        out_stream = StringIO()
 
2379
        out_stream = BytesIO()
2378
2380
        smart_protocol = self.server_protocol_class(
2379
2381
            mem_transport, out_stream.write)
2380
2382
        smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
2386
2388
        self.assertEqual('', smart_protocol.in_buffer)
2387
2389
 
2388
2390
    def test_accept_excess_bytes_are_preserved(self):
2389
 
        out_stream = StringIO()
 
2391
        out_stream = BytesIO()
2390
2392
        smart_protocol = self.server_protocol_class(None, out_stream.write)
2391
2393
        smart_protocol.accept_bytes('hello\nhello\n')
2392
2394
        self.assertEqual(self.response_marker + "success\nok\x012\n",
2408
2410
        self.assertEqual("", server_protocol.in_buffer)
2409
2411
 
2410
2412
    def test_accept_excess_bytes_after_dispatch(self):
2411
 
        out_stream = StringIO()
 
2413
        out_stream = BytesIO()
2412
2414
        smart_protocol = self.server_protocol_class(None, out_stream.write)
2413
2415
        smart_protocol.accept_bytes('hello\n')
2414
2416
        self.assertEqual(self.response_marker + "success\nok\x012\n",
2444
2446
        # accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
2445
2447
        # response of tuple-encoded (ok, 1).  Also, separately we should test
2446
2448
        # the error if the response is a non-understood version.
2447
 
        input = StringIO(self.response_marker + 'success\nok\x012\n')
2448
 
        output = StringIO()
 
2449
        input = BytesIO(self.response_marker + b'success\nok\x012\n')
 
2450
        output = BytesIO()
2449
2451
        client_medium = medium.SmartSimplePipesClientMedium(
2450
2452
            input, output, 'base')
2451
2453
        request = client_medium.get_request()
2470
2472
    def test_client_call_with_body_bytes_uploads(self):
2471
2473
        # protocol.call_with_body_bytes should length-prefix the bytes onto the
2472
2474
        # wire.
2473
 
        expected_bytes = self.request_marker + "foo\n7\nabcdefgdone\n"
2474
 
        input = StringIO("\n")
2475
 
        output = StringIO()
 
2475
        expected_bytes = self.request_marker + b"foo\n7\nabcdefgdone\n"
 
2476
        input = BytesIO(b"\n")
 
2477
        output = BytesIO()
2476
2478
        client_medium = medium.SmartSimplePipesClientMedium(
2477
2479
            input, output, 'base')
2478
2480
        request = client_medium.get_request()
2483
2485
    def test_client_call_with_body_readv_array(self):
2484
2486
        # protocol.call_with_upload should encode the readv array and then
2485
2487
        # length-prefix the bytes onto the wire.
2486
 
        expected_bytes = self.request_marker + "foo\n7\n1,2\n5,6done\n"
2487
 
        input = StringIO("\n")
2488
 
        output = StringIO()
 
2488
        expected_bytes = self.request_marker + b"foo\n7\n1,2\n5,6done\n"
 
2489
        input = BytesIO(b"\n")
 
2490
        output = BytesIO()
2489
2491
        client_medium = medium.SmartSimplePipesClientMedium(
2490
2492
            input, output, 'base')
2491
2493
        request = client_medium.get_request()
2496
2498
    def test_client_read_body_bytes_all(self):
2497
2499
        # read_body_bytes should decode the body bytes from the wire into
2498
2500
        # a response.
2499
 
        expected_bytes = "1234567"
 
2501
        expected_bytes = b"1234567"
2500
2502
        server_bytes = (self.response_marker +
2501
 
                        "success\nok\n7\n1234567done\n")
2502
 
        input = StringIO(server_bytes)
2503
 
        output = StringIO()
 
2503
                        b"success\nok\n7\n1234567done\n")
 
2504
        input = BytesIO(server_bytes)
 
2505
        output = BytesIO()
2504
2506
        client_medium = medium.SmartSimplePipesClientMedium(
2505
2507
            input, output, 'base')
2506
2508
        request = client_medium.get_request()
2515
2517
        # to make the state machine work harder: however, as we use the
2516
2518
        # LengthPrefixedBodyDecoder that is already well tested - we can skip
2517
2519
        # that.
2518
 
        expected_bytes = "1234567"
2519
 
        server_bytes = self.response_marker + "success\nok\n7\n1234567done\n"
2520
 
        input = StringIO(server_bytes)
2521
 
        output = StringIO()
 
2520
        expected_bytes = b"1234567"
 
2521
        server_bytes = self.response_marker + b"success\nok\n7\n1234567done\n"
 
2522
        input = BytesIO(server_bytes)
 
2523
        output = BytesIO()
2522
2524
        client_medium = medium.SmartSimplePipesClientMedium(
2523
2525
            input, output, 'base')
2524
2526
        request = client_medium.get_request()
2533
2535
    def test_client_cancel_read_body_does_not_eat_body_bytes(self):
2534
2536
        # cancelling the expected body needs to finish the request, but not
2535
2537
        # read any more bytes.
2536
 
        server_bytes = self.response_marker + "success\nok\n7\n1234567done\n"
2537
 
        input = StringIO(server_bytes)
2538
 
        output = StringIO()
 
2538
        server_bytes = self.response_marker + b"success\nok\n7\n1234567done\n"
 
2539
        input = BytesIO(server_bytes)
 
2540
        output = BytesIO()
2539
2541
        client_medium = medium.SmartSimplePipesClientMedium(
2540
2542
            input, output, 'base')
2541
2543
        request = client_medium.get_request()
2550
2552
 
2551
2553
    def test_client_read_body_bytes_interrupted_connection(self):
2552
2554
        server_bytes = (self.response_marker +
2553
 
                        "success\nok\n999\nincomplete body")
2554
 
        input = StringIO(server_bytes)
2555
 
        output = StringIO()
 
2555
                        b"success\nok\n999\nincomplete body")
 
2556
        input = BytesIO(server_bytes)
 
2557
        output = BytesIO()
2556
2558
        client_medium = medium.SmartSimplePipesClientMedium(
2557
2559
            input, output, 'base')
2558
2560
        request = client_medium.get_request()
2568
2570
    def assertBodyStreamSerialisation(self, expected_serialisation,
2569
2571
                                      body_stream):
2570
2572
        """Assert that body_stream is serialised as expected_serialisation."""
2571
 
        out_stream = StringIO()
 
2573
        out_stream = BytesIO()
2572
2574
        protocol._send_stream(body_stream, out_stream.write)
2573
2575
        self.assertEqual(expected_serialisation, out_stream.getvalue())
2574
2576
 
2576
2578
        """Assert that body_stream is the same after being serialised and
2577
2579
        deserialised.
2578
2580
        """
2579
 
        out_stream = StringIO()
 
2581
        out_stream = BytesIO()
2580
2582
        protocol._send_stream(body_stream, out_stream.write)
2581
2583
        decoder = protocol.ChunkedBodyDecoder()
2582
2584
        decoder.accept_bytes(out_stream.getvalue())
2619
2621
 
2620
2622
    def test__send_response_includes_failure_marker(self):
2621
2623
        """FailedSmartServerResponse have 'failed\n' after the version."""
2622
 
        out_stream = StringIO()
 
2624
        out_stream = BytesIO()
2623
2625
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
2624
2626
            None, out_stream.write)
2625
2627
        smart_protocol._send_response(
2629
2631
 
2630
2632
    def test__send_response_includes_success_marker(self):
2631
2633
        """SuccessfulSmartServerResponse have 'success\n' after the version."""
2632
 
        out_stream = StringIO()
 
2634
        out_stream = BytesIO()
2633
2635
        smart_protocol = protocol.SmartServerRequestProtocolTwo(
2634
2636
            None, out_stream.write)
2635
2637
        smart_protocol._send_response(
2652
2654
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
2653
2655
                        "success\nok\n" + body_header + two_body_chunks +
2654
2656
                        body_terminator)
2655
 
        input = StringIO(server_bytes)
2656
 
        output = StringIO()
 
2657
        input = BytesIO(server_bytes)
 
2658
        output = BytesIO()
2657
2659
        client_medium = medium.SmartSimplePipesClientMedium(
2658
2660
            input, output, 'base')
2659
2661
        request = client_medium.get_request()
2673
2675
        body = body_header + a_body_chunk + err_signal + err_chunks + finish
2674
2676
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
2675
2677
                        "success\nok\n" + body)
2676
 
        input = StringIO(server_bytes)
2677
 
        output = StringIO()
 
2678
        input = BytesIO(server_bytes)
 
2679
        output = BytesIO()
2678
2680
        client_medium = medium.SmartSimplePipesClientMedium(
2679
2681
            input, output, 'base')
2680
2682
        smart_request = client_medium.get_request()
2692
2694
        incomplete_body_chunk = "9999\nincomplete chunk"
2693
2695
        server_bytes = (protocol.RESPONSE_VERSION_TWO +
2694
2696
                        "success\nok\n" + body_header + incomplete_body_chunk)
2695
 
        input = StringIO(server_bytes)
2696
 
        output = StringIO()
 
2697
        input = BytesIO(server_bytes)
 
2698
        output = BytesIO()
2697
2699
        client_medium = medium.SmartSimplePipesClientMedium(
2698
2700
            input, output, 'base')
2699
2701
        request = client_medium.get_request()
2705
2707
 
2706
2708
    def test_client_read_response_tuple_sets_response_status(self):
2707
2709
        server_bytes = protocol.RESPONSE_VERSION_TWO + "success\nok\n"
2708
 
        input = StringIO(server_bytes)
2709
 
        output = StringIO()
 
2710
        input = BytesIO(server_bytes)
 
2711
        output = BytesIO()
2710
2712
        client_medium = medium.SmartSimplePipesClientMedium(
2711
2713
            input, output, 'base')
2712
2714
        request = client_medium.get_request()
2723
2725
            protocol.RESPONSE_VERSION_TWO +
2724
2726
            "failed\n" +
2725
2727
            "error\x01Generic bzr smart protocol error: bad request 'foo'\n")
2726
 
        input = StringIO(server_bytes)
2727
 
        output = StringIO()
 
2728
        input = BytesIO(server_bytes)
 
2729
        output = BytesIO()
2728
2730
        client_medium = medium.SmartSimplePipesClientMedium(
2729
2731
            input, output, 'base')
2730
2732
        request = client_medium.get_request()
2819
2821
        """Smoke test for the simplest possible v3 request: empty headers, no
2820
2822
        message parts.
2821
2823
        """
2822
 
        output = StringIO()
 
2824
        output = BytesIO()
2823
2825
        headers = '\0\0\0\x02de'  # length-prefixed, bencoded empty dict
2824
2826
        end = 'e'
2825
2827
        request_bytes = headers + end
2832
2834
        """Repeated calls to accept_bytes after the message end has been parsed
2833
2835
        accumlates the bytes in the unused_data attribute.
2834
2836
        """
2835
 
        output = StringIO()
 
2837
        output = BytesIO()
2836
2838
        headers = '\0\0\0\x02de'  # length-prefixed, bencoded empty dict
2837
2839
        end = 'e'
2838
2840
        request_bytes = headers + end
2903
2905
        protocol_decoder = protocol.ProtocolThreeDecoder(response_handler)
2904
2906
        # put decoder in desired state (waiting for message parts)
2905
2907
        protocol_decoder.state_accept = protocol_decoder._state_accept_expecting_message_part
2906
 
        output = StringIO()
 
2908
        output = BytesIO()
2907
2909
        client_medium = medium.SmartSimplePipesClientMedium(
2908
 
            StringIO(response_bytes), output, 'base')
 
2910
            BytesIO(response_bytes), output, 'base')
2909
2911
        medium_request = client_medium.get_request()
2910
2912
        medium_request.finished_writing()
2911
2913
        response_handler.setProtoAndMediumRequest(
3043
3045
            'e' # end of message
3044
3046
            )
3045
3047
 
3046
 
        to_server = StringIO(invalid_request)
3047
 
        from_server = StringIO()
 
3048
        to_server = BytesIO(invalid_request)
 
3049
        from_server = BytesIO()
3048
3050
        transport = memory.MemoryTransport('memory:///')
3049
3051
        server = medium.SmartServerPipeStreamMedium(
3050
3052
            to_server, from_server, transport, timeout=4.0)
3357
3359
class TestResponseEncodingProtocolThree(tests.TestCase):
3358
3360
 
3359
3361
    def make_response_encoder(self):
3360
 
        out_stream = StringIO()
 
3362
        out_stream = BytesIO()
3361
3363
        response_encoder = protocol.ProtocolThreeResponder(out_stream.write)
3362
3364
        return response_encoder, out_stream
3363
3365
 
3459
3461
        the medium is left in a sane state, i.e. is capable of allowing further
3460
3462
        requests.
3461
3463
        """
3462
 
        input = StringIO("\n")
3463
 
        output = StringIO()
 
3464
        input = BytesIO(b"\n")
 
3465
        output = BytesIO()
3464
3466
        client_medium = medium.SmartSimplePipesClientMedium(
3465
3467
            input, output, 'ignored base')
3466
3468
        smart_client = client._SmartClient(client_medium)
3753
3755
class Test_SmartClientRequest(tests.TestCase):
3754
3756
 
3755
3757
    def make_client_with_failing_medium(self, fail_at_write=True, response=''):
3756
 
        response_io = StringIO(response)
3757
 
        output = StringIO()
3758
 
        vendor = FirstRejectedStringIOSSHVendor(response_io, output,
 
3758
        response_io = BytesIO(response)
 
3759
        output = BytesIO()
 
3760
        vendor = FirstRejectedBytesIOSSHVendor(response_io, output,
3759
3761
                    fail_at_write=fail_at_write)
3760
3762
        ssh_params = medium.SSHParams('a host', 'a port', 'a user', 'a pass')
3761
3763
        client_medium = medium.SmartSSHClientMedium('base', ssh_params, vendor)
3763
3765
        return output, vendor, smart_client
3764
3766
 
3765
3767
    def make_response(self, args, body=None, body_stream=None):
3766
 
        response_io = StringIO()
 
3768
        response_io = BytesIO()
3767
3769
        response = _mod_request.SuccessfulSmartServerResponse(args, body=body,
3768
3770
            body_stream=body_stream)
3769
3771
        responder = protocol.ProtocolThreeResponder(response_io.write)
3886
3888
                         output.getvalue())
3887
3889
 
3888
3890
    def test__send_request_stops_if_body_started(self):
3889
 
        # We intentionally use the python StringIO so that we can subclass it.
3890
 
        from StringIO import StringIO
3891
 
        response = StringIO()
 
3891
        # We intentionally use the python BytesIO so that we can subclass it.
 
3892
        from io import BytesIO
 
3893
        response = BytesIO()
3892
3894
 
3893
 
        class FailAfterFirstWrite(StringIO):
 
3895
        class FailAfterFirstWrite(BytesIO):
3894
3896
            """Allow one 'write' call to pass, fail the rest"""
3895
3897
            def __init__(self):
3896
 
                StringIO.__init__(self)
 
3898
                BytesIO.__init__(self)
3897
3899
                self._first = True
3898
3900
 
3899
3901
            def write(self, s):
3900
3902
                if self._first:
3901
3903
                    self._first = False
3902
 
                    return StringIO.write(self, s)
 
3904
                    return BytesIO.write(self, s)
3903
3905
                raise IOError(errno.EINVAL, 'invalid file handle')
3904
3906
        output = FailAfterFirstWrite()
3905
3907
 
3906
 
        vendor = FirstRejectedStringIOSSHVendor(response, output,
 
3908
        vendor = FirstRejectedBytesIOSSHVendor(response, output,
3907
3909
            fail_at_write=False)
3908
3910
        ssh_params = medium.SSHParams('a host', 'a port', 'a user', 'a pass')
3909
3911
        client_medium = medium.SmartSSHClientMedium('base', ssh_params, vendor)