/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1
# Copyright (C) 2006 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for smart transport"""
18
19
# all of this deals with byte strings so this is safe
20
from cStringIO import StringIO
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
21
import os
22
import socket
23
import threading
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
24
import urllib2
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
25
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
26
from bzrlib import (
27
        bzrdir,
28
        errors,
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
29
        osutils,
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
30
        tests,
2049.1.1 by Lukáš Lalinský
Windows-speficic smart server transport selftest fixes.
31
        urlutils,
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
32
        )
2018.5.21 by Andrew Bennetts
Move bzrlib.transport.smart to bzrlib.smart
33
from bzrlib import smart
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
34
from bzrlib.transport import (
35
        get_transport,
36
        local,
37
        memory,
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
38
        remote,
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
39
        )
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
40
from bzrlib.transport.http import (
41
        HTTPServerWithSmarts,
42
        SmartClientHTTPMediumRequest,
43
        SmartRequestHandler,
44
        )
2018.5.21 by Andrew Bennetts
Move bzrlib.transport.smart to bzrlib.smart
45
from bzrlib.smart import (
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
46
        medium,
47
        protocol,
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
48
        request,
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
49
        server,
50
        vfs,
51
)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
52
53
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
54
class StringIOSSHVendor(object):
55
    """A SSH vendor that uses StringIO to buffer writes and answer reads."""
56
57
    def __init__(self, read_from, write_to):
58
        self.read_from = read_from
59
        self.write_to = write_to
60
        self.calls = []
61
62
    def connect_ssh(self, username, password, host, port, command):
63
        self.calls.append(('connect_ssh', username, password, host, port,
64
            command))
65
        return StringIOSSHConnection(self)
66
67
68
class StringIOSSHConnection(object):
69
    """A SSH connection that uses StringIO to buffer writes and answer reads."""
70
71
    def __init__(self, vendor):
72
        self.vendor = vendor
73
    
74
    def close(self):
75
        self.vendor.calls.append(('close', ))
76
        
77
    def get_filelike_channels(self):
78
        return self.vendor.read_from, self.vendor.write_to
79
80
81
82
class SmartClientMediumTests(tests.TestCase):
83
    """Tests for SmartClientMedium.
84
85
    We should create a test scenario for this: we need a server module that
86
    construct the test-servers (like make_loopsocket_and_medium), and the list
87
    of SmartClientMedium classes to test.
88
    """
89
90
    def make_loopsocket_and_medium(self):
91
        """Create a loopback socket for testing, and a medium aimed at it."""
92
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
93
        sock.bind(('127.0.0.1', 0))
94
        sock.listen(1)
95
        port = sock.getsockname()[1]
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
96
        client_medium = medium.SmartTCPClientMedium('127.0.0.1', port)
97
        return sock, client_medium
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
98
99
    def receive_bytes_on_server(self, sock, bytes):
100
        """Accept a connection on sock and read 3 bytes.
101
102
        The bytes are appended to the list bytes.
103
104
        :return: a Thread which is running to do the accept and recv.
105
        """
106
        def _receive_bytes_on_server():
107
            connection, address = sock.accept()
2091.1.1 by Martin Pool
Avoid MSG_WAITALL as it doesn't work on Windows
108
            bytes.append(osutils.recv_all(connection, 3))
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
109
            connection.close()
110
        t = threading.Thread(target=_receive_bytes_on_server)
111
        t.start()
112
        return t
113
    
114
    def test_construct_smart_stream_medium_client(self):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
115
        # make a new instance of the common base for Stream-like Mediums.
116
        # this just ensures that the constructor stays parameter-free which
117
        # is important for reuse : some subclasses will dynamically connect,
118
        # others are always on, etc.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
119
        client_medium = medium.SmartClientStreamMedium()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
120
121
    def test_construct_smart_client_medium(self):
122
        # the base client medium takes no parameters
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
123
        client_medium = medium.SmartClientMedium()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
124
    
125
    def test_construct_smart_simple_pipes_client_medium(self):
126
        # the SimplePipes client medium takes two pipes:
127
        # readable pipe, writeable pipe.
128
        # Constructing one should just save these and do nothing.
129
        # We test this by passing in None.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
130
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
131
        
132
    def test_simple_pipes_client_request_type(self):
133
        # SimplePipesClient should use SmartClientStreamMediumRequest's.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
134
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
135
        request = client_medium.get_request()
136
        self.assertIsInstance(request, medium.SmartClientStreamMediumRequest)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
137
138
    def test_simple_pipes_client_get_concurrent_requests(self):
139
        # the simple_pipes client does not support pipelined requests:
140
        # but it does support serial requests: we construct one after 
141
        # another is finished. This is a smoke test testing the integration
142
        # of the SmartClientStreamMediumRequest and the SmartClientStreamMedium
143
        # classes - as the sibling classes share this logic, they do not have
144
        # explicit tests for this.
145
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
146
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
147
        request = client_medium.get_request()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
148
        request.finished_writing()
149
        request.finished_reading()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
150
        request2 = client_medium.get_request()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
151
        request2.finished_writing()
152
        request2.finished_reading()
153
154
    def test_simple_pipes_client__accept_bytes_writes_to_writable(self):
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
155
        # accept_bytes writes to the writeable pipe.
156
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
157
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
158
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
159
        self.assertEqual('abc', output.getvalue())
160
    
161
    def test_simple_pipes_client_disconnect_does_nothing(self):
162
        # calling disconnect does nothing.
163
        input = StringIO()
164
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
165
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
166
        # send some bytes to ensure disconnecting after activity still does not
167
        # close.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
168
        client_medium._accept_bytes('abc')
169
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
170
        self.assertFalse(input.closed)
171
        self.assertFalse(output.closed)
172
173
    def test_simple_pipes_client_accept_bytes_after_disconnect(self):
174
        # calling disconnect on the client does not alter the pipe that
175
        # accept_bytes writes to.
176
        input = StringIO()
177
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
178
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
179
        client_medium._accept_bytes('abc')
180
        client_medium.disconnect()
181
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
182
        self.assertFalse(input.closed)
183
        self.assertFalse(output.closed)
184
        self.assertEqual('abcabc', output.getvalue())
185
    
186
    def test_simple_pipes_client_ignores_disconnect_when_not_connected(self):
187
        # Doing a disconnect on a new (and thus unconnected) SimplePipes medium
188
        # does nothing.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
189
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
190
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
191
192
    def test_simple_pipes_client_can_always_read(self):
193
        # SmartSimplePipesClientMedium is never disconnected, so read_bytes
194
        # always tries to read from the underlying pipe.
195
        input = StringIO('abcdef')
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
196
        client_medium = medium.SmartSimplePipesClientMedium(input, None)
197
        self.assertEqual('abc', client_medium.read_bytes(3))
198
        client_medium.disconnect()
199
        self.assertEqual('def', client_medium.read_bytes(3))
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
200
        
201
    def test_simple_pipes_client_supports__flush(self):
202
        # invoking _flush on a SimplePipesClient should flush the output 
203
        # pipe. We test this by creating an output pipe that records
204
        # flush calls made to it.
205
        from StringIO import StringIO # get regular StringIO
206
        input = StringIO()
207
        output = StringIO()
208
        flush_calls = []
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
209
        def logging_flush(): flush_calls.append('flush')
210
        output.flush = logging_flush
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
211
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
212
        # this call is here to ensure we only flush once, not on every
213
        # _accept_bytes call.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
214
        client_medium._accept_bytes('abc')
215
        client_medium._flush()
216
        client_medium.disconnect()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
217
        self.assertEqual(['flush'], flush_calls)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
218
219
    def test_construct_smart_ssh_client_medium(self):
220
        # the SSH client medium takes:
221
        # host, port, username, password, vendor
222
        # Constructing one should just save these and do nothing.
223
        # we test this by creating a empty bound socket and constructing
224
        # a medium.
225
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
226
        sock.bind(('127.0.0.1', 0))
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
227
        unopened_port = sock.getsockname()[1]
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
228
        # having vendor be invalid means that if it tries to connect via the
229
        # vendor it will blow up.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
230
        client_medium = medium.SmartSSHClientMedium('127.0.0.1', unopened_port,
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
231
            username=None, password=None, vendor="not a vendor")
232
        sock.close()
233
234
    def test_ssh_client_connects_on_first_use(self):
235
        # The only thing that initiates a connection from the medium is giving
236
        # it bytes.
237
        output = StringIO()
238
        vendor = StringIOSSHVendor(StringIO(), output)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
239
        client_medium = medium.SmartSSHClientMedium(
240
            'a hostname', 'a port', 'a username', 'a password', vendor)
241
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
242
        self.assertEqual('abc', output.getvalue())
243
        self.assertEqual([('connect_ssh', 'a username', 'a password',
244
            'a hostname', 'a port',
245
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes'])],
246
            vendor.calls)
247
    
248
    def test_ssh_client_changes_command_when_BZR_REMOTE_PATH_is_set(self):
249
        # The only thing that initiates a connection from the medium is giving
250
        # it bytes.
251
        output = StringIO()
252
        vendor = StringIOSSHVendor(StringIO(), output)
253
        orig_bzr_remote_path = os.environ.get('BZR_REMOTE_PATH')
254
        def cleanup_environ():
255
            osutils.set_or_unset_env('BZR_REMOTE_PATH', orig_bzr_remote_path)
256
        self.addCleanup(cleanup_environ)
257
        os.environ['BZR_REMOTE_PATH'] = 'fugly'
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
258
        client_medium = medium.SmartSSHClientMedium('a hostname', 'a port', 'a username',
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
259
            'a password', vendor)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
260
        client_medium._accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
261
        self.assertEqual('abc', output.getvalue())
262
        self.assertEqual([('connect_ssh', 'a username', 'a password',
263
            'a hostname', 'a port',
264
            ['fugly', 'serve', '--inet', '--directory=/', '--allow-writes'])],
265
            vendor.calls)
266
    
267
    def test_ssh_client_disconnect_does_so(self):
268
        # calling disconnect should disconnect both the read_from and write_to
269
        # file-like object it from the ssh connection.
270
        input = StringIO()
271
        output = StringIO()
272
        vendor = StringIOSSHVendor(input, output)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
273
        client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
274
        client_medium._accept_bytes('abc')
275
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
276
        self.assertTrue(input.closed)
277
        self.assertTrue(output.closed)
278
        self.assertEqual([
279
            ('connect_ssh', None, None, 'a hostname', None,
280
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
281
            ('close', ),
282
            ],
283
            vendor.calls)
284
285
    def test_ssh_client_disconnect_allows_reconnection(self):
286
        # calling disconnect on the client terminates the connection, but should
287
        # not prevent additional connections occuring.
288
        # we test this by initiating a second connection after doing a
289
        # disconnect.
290
        input = StringIO()
291
        output = StringIO()
292
        vendor = StringIOSSHVendor(input, output)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
293
        client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
294
        client_medium._accept_bytes('abc')
295
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
296
        # the disconnect has closed output, so we need a new output for the
297
        # new connection to write to.
298
        input2 = StringIO()
299
        output2 = StringIO()
300
        vendor.read_from = input2
301
        vendor.write_to = output2
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
302
        client_medium._accept_bytes('abc')
303
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
304
        self.assertTrue(input.closed)
305
        self.assertTrue(output.closed)
306
        self.assertTrue(input2.closed)
307
        self.assertTrue(output2.closed)
308
        self.assertEqual([
309
            ('connect_ssh', None, None, 'a hostname', None,
310
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
311
            ('close', ),
312
            ('connect_ssh', None, None, 'a hostname', None,
313
            ['bzr', 'serve', '--inet', '--directory=/', '--allow-writes']),
314
            ('close', ),
315
            ],
316
            vendor.calls)
317
    
318
    def test_ssh_client_ignores_disconnect_when_not_connected(self):
319
        # Doing a disconnect on a new (and thus unconnected) SSH medium
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
320
        # does not fail.  It's ok to disconnect an unconnected medium.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
321
        client_medium = medium.SmartSSHClientMedium(None)
322
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
323
324
    def test_ssh_client_raises_on_read_when_not_connected(self):
325
        # Doing a read on a new (and thus unconnected) SSH medium raises
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
326
        # MediumNotConnected.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
327
        client_medium = medium.SmartSSHClientMedium(None)
328
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
329
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 1)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
330
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
331
    def test_ssh_client_supports__flush(self):
332
        # invoking _flush on a SSHClientMedium should flush the output 
333
        # pipe. We test this by creating an output pipe that records
334
        # flush calls made to it.
335
        from StringIO import StringIO # get regular StringIO
336
        input = StringIO()
337
        output = StringIO()
338
        flush_calls = []
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
339
        def logging_flush(): flush_calls.append('flush')
340
        output.flush = logging_flush
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
341
        vendor = StringIOSSHVendor(input, output)
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
342
        client_medium = medium.SmartSSHClientMedium('a hostname', vendor=vendor)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
343
        # this call is here to ensure we only flush once, not on every
344
        # _accept_bytes call.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
345
        client_medium._accept_bytes('abc')
346
        client_medium._flush()
347
        client_medium.disconnect()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
348
        self.assertEqual(['flush'], flush_calls)
349
        
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
350
    def test_construct_smart_tcp_client_medium(self):
351
        # the TCP client medium takes a host and a port.  Constructing it won't
352
        # connect to anything.
353
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
354
        sock.bind(('127.0.0.1', 0))
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
355
        unopened_port = sock.getsockname()[1]
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
356
        client_medium = medium.SmartTCPClientMedium('127.0.0.1', unopened_port)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
357
        sock.close()
358
359
    def test_tcp_client_connects_on_first_use(self):
360
        # The only thing that initiates a connection from the medium is giving
361
        # it bytes.
362
        sock, medium = self.make_loopsocket_and_medium()
363
        bytes = []
364
        t = self.receive_bytes_on_server(sock, bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
365
        medium.accept_bytes('abc')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
366
        t.join()
367
        sock.close()
368
        self.assertEqual(['abc'], bytes)
369
    
370
    def test_tcp_client_disconnect_does_so(self):
371
        # calling disconnect on the client terminates the connection.
372
        # we test this by forcing a short read during a socket.MSG_WAITALL
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
373
        # call: write 2 bytes, try to read 3, and then the client disconnects.
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
374
        sock, medium = self.make_loopsocket_and_medium()
375
        bytes = []
376
        t = self.receive_bytes_on_server(sock, bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
377
        medium.accept_bytes('ab')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
378
        medium.disconnect()
379
        t.join()
380
        sock.close()
381
        self.assertEqual(['ab'], bytes)
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
382
        # now disconnect again: this should not do anything, if disconnection
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
383
        # really did disconnect.
384
        medium.disconnect()
385
    
386
    def test_tcp_client_ignores_disconnect_when_not_connected(self):
387
        # Doing a disconnect on a new (and thus unconnected) TCP medium
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
388
        # does not fail.  It's ok to disconnect an unconnected medium.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
389
        client_medium = medium.SmartTCPClientMedium(None, None)
390
        client_medium.disconnect()
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
391
392
    def test_tcp_client_raises_on_read_when_not_connected(self):
393
        # Doing a read on a new (and thus unconnected) TCP medium raises
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
394
        # MediumNotConnected.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
395
        client_medium = medium.SmartTCPClientMedium(None, None)
396
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 0)
397
        self.assertRaises(errors.MediumNotConnected, client_medium.read_bytes, 1)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
398
399
    def test_tcp_client_supports__flush(self):
400
        # invoking _flush on a TCPClientMedium should do something useful.
401
        # RBC 20060922 not sure how to test/tell in this case.
402
        sock, medium = self.make_loopsocket_and_medium()
403
        bytes = []
404
        t = self.receive_bytes_on_server(sock, bytes)
405
        # try with nothing buffered
406
        medium._flush()
407
        medium._accept_bytes('ab')
408
        # and with something sent.
409
        medium._flush()
410
        medium.disconnect()
411
        t.join()
412
        sock.close()
413
        self.assertEqual(['ab'], bytes)
414
        # now disconnect again : this should not do anything, if disconnection
415
        # really did disconnect.
416
        medium.disconnect()
417
418
419
class TestSmartClientStreamMediumRequest(tests.TestCase):
420
    """Tests the for SmartClientStreamMediumRequest.
421
    
422
    SmartClientStreamMediumRequest is a helper for the three stream based 
423
    mediums: TCP, SSH, SimplePipes, so we only test it once, and then test that
424
    those three mediums implement the interface it expects.
425
    """
426
427
    def test_accept_bytes_after_finished_writing_errors(self):
428
        # calling accept_bytes after calling finished_writing raises 
429
        # WritingCompleted to prevent bad assumptions on stream environments
430
        # breaking the needs of message-based environments.
431
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
432
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
433
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
434
        request.finished_writing()
435
        self.assertRaises(errors.WritingCompleted, request.accept_bytes, None)
436
437
    def test_accept_bytes(self):
438
        # accept bytes should invoke _accept_bytes on the stream medium.
439
        # we test this by using the SimplePipes medium - the most trivial one
440
        # and checking that the pipes get the data.
441
        input = StringIO()
442
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
443
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
444
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
445
        request.accept_bytes('123')
446
        request.finished_writing()
447
        request.finished_reading()
448
        self.assertEqual('', input.getvalue())
449
        self.assertEqual('123', output.getvalue())
450
451
    def test_construct_sets_stream_request(self):
452
        # constructing a SmartClientStreamMediumRequest on a StreamMedium sets
453
        # the current request to the new SmartClientStreamMediumRequest
454
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
455
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
456
        request = medium.SmartClientStreamMediumRequest(client_medium)
457
        self.assertIs(client_medium._current_request, request)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
458
459
    def test_construct_while_another_request_active_throws(self):
460
        # constructing a SmartClientStreamMediumRequest on a StreamMedium with
461
        # a non-None _current_request raises TooManyConcurrentRequests.
462
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
463
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
464
        client_medium._current_request = "a"
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
465
        self.assertRaises(errors.TooManyConcurrentRequests,
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
466
            medium.SmartClientStreamMediumRequest, client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
467
468
    def test_finished_read_clears_current_request(self):
469
        # calling finished_reading clears the current request from the requests
470
        # medium
471
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
472
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
473
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
474
        request.finished_writing()
475
        request.finished_reading()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
476
        self.assertEqual(None, client_medium._current_request)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
477
478
    def test_finished_read_before_finished_write_errors(self):
479
        # calling finished_reading before calling finished_writing triggers a
480
        # WritingNotComplete error.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
481
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
482
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
483
        self.assertRaises(errors.WritingNotComplete, request.finished_reading)
484
        
485
    def test_read_bytes(self):
486
        # read bytes should invoke _read_bytes on the stream medium.
487
        # we test this by using the SimplePipes medium - the most trivial one
488
        # and checking that the data is supplied. Its possible that a 
489
        # faulty implementation could poke at the pipe variables them selves,
490
        # but we trust that this will be caught as it will break the integration
491
        # smoke tests.
492
        input = StringIO('321')
493
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
494
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
495
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
496
        request.finished_writing()
497
        self.assertEqual('321', request.read_bytes(3))
498
        request.finished_reading()
499
        self.assertEqual('', input.read())
500
        self.assertEqual('', output.getvalue())
501
502
    def test_read_bytes_before_finished_write_errors(self):
503
        # calling read_bytes before calling finished_writing triggers a
504
        # WritingNotComplete error because the Smart protocol is designed to be
505
        # compatible with strict message based protocols like HTTP where the
506
        # request cannot be submitted until the writing has completed.
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
507
        client_medium = medium.SmartSimplePipesClientMedium(None, None)
508
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
509
        self.assertRaises(errors.WritingNotComplete, request.read_bytes, None)
510
511
    def test_read_bytes_after_finished_reading_errors(self):
512
        # calling read_bytes after calling finished_reading raises 
513
        # ReadingCompleted to prevent bad assumptions on stream environments
514
        # breaking the needs of message-based environments.
515
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
516
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
517
        request = medium.SmartClientStreamMediumRequest(client_medium)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
518
        request.finished_writing()
519
        request.finished_reading()
520
        self.assertRaises(errors.ReadingCompleted, request.read_bytes, None)
521
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
522
523
class RemoteTransportTests(tests.TestCaseWithTransport):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
524
525
    def setUp(self):
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
526
        super(RemoteTransportTests, self).setUp()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
527
        # We're allowed to set  the transport class here, so that we don't use
528
        # the default or a parameterized class, but rather use the
529
        # TestCaseWithTransport infrastructure to set up a smart server and
530
        # transport.
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
531
        self.transport_server = server.SmartTCPServer_for_testing
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
532
533
    def test_plausible_url(self):
534
        self.assert_(self.get_url().startswith('bzr://'))
535
536
    def test_probe_transport(self):
537
        t = self.get_transport()
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
538
        self.assertIsInstance(t, remote.RemoteTransport)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
539
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
540
    def test_get_medium_from_transport(self):
541
        """Remote transport has a medium always, which it can return."""
542
        t = self.get_transport()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
543
        client_medium = t.get_smart_medium()
544
        self.assertIsInstance(client_medium, medium.SmartClientMedium)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
545
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
546
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
547
class ErrorRaisingProtocol(object):
548
549
    def __init__(self, exception):
550
        self.exception = exception
551
552
    def next_read_size(self):
553
        raise self.exception
554
555
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
556
class SampleRequest(object):
557
    
558
    def __init__(self, expected_bytes):
559
        self.accepted_bytes = ''
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
560
        self._finished_reading = False
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
561
        self.expected_bytes = expected_bytes
562
        self.excess_buffer = ''
563
564
    def accept_bytes(self, bytes):
565
        self.accepted_bytes += bytes
566
        if self.accepted_bytes.startswith(self.expected_bytes):
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
567
            self._finished_reading = True
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
568
            self.excess_buffer = self.accepted_bytes[len(self.expected_bytes):]
569
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
570
    def next_read_size(self):
571
        if self._finished_reading:
572
            return 0
573
        else:
574
            return 1
575
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
576
577
class TestSmartServerStreamMedium(tests.TestCase):
578
579
    def portable_socket_pair(self):
580
        """Return a pair of TCP sockets connected to each other.
581
        
582
        Unlike socket.socketpair, this should work on Windows.
583
        """
584
        listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
585
        listen_sock.bind(('127.0.0.1', 0))
586
        listen_sock.listen(1)
587
        client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
588
        client_sock.connect(listen_sock.getsockname())
589
        server_sock, addr = listen_sock.accept()
590
        listen_sock.close()
591
        return server_sock, client_sock
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
592
    
593
    def test_smart_query_version(self):
594
        """Feed a canned query version to a server"""
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
595
        # wire-to-wire, using the whole stack
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
596
        to_server = StringIO('hello\n')
597
        from_server = StringIO()
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
598
        transport = local.LocalTransport(urlutils.local_path_to_url('/'))
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
599
        server = medium.SmartServerPipeStreamMedium(
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
600
            to_server, from_server, transport)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
601
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
602
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
603
        server._serve_one_request(smart_protocol)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
604
        self.assertEqual('ok\0011\n',
605
                         from_server.getvalue())
606
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
607
    def test_response_to_canned_get(self):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
608
        transport = memory.MemoryTransport('memory:///')
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
609
        transport.put_bytes('testfile', 'contents\nof\nfile\n')
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
610
        to_server = StringIO('get\001./testfile\n')
611
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
612
        server = medium.SmartServerPipeStreamMedium(
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
613
            to_server, from_server, transport)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
614
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
615
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
616
        server._serve_one_request(smart_protocol)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
617
        self.assertEqual('ok\n'
618
                         '17\n'
619
                         'contents\nof\nfile\n'
620
                         'done\n',
621
                         from_server.getvalue())
622
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
623
    def test_response_to_canned_get_of_utf8(self):
624
        # wire-to-wire, using the whole stack, with a UTF-8 filename.
625
        transport = memory.MemoryTransport('memory:///')
626
        utf8_filename = u'testfile\N{INTERROBANG}'.encode('utf-8')
627
        transport.put_bytes(utf8_filename, 'contents\nof\nfile\n')
628
        to_server = StringIO('get\001' + utf8_filename + '\n')
629
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
630
        server = medium.SmartServerPipeStreamMedium(
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
631
            to_server, from_server, transport)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
632
        smart_protocol = protocol.SmartServerRequestProtocolOne(transport,
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
633
                from_server.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
634
        server._serve_one_request(smart_protocol)
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
635
        self.assertEqual('ok\n'
636
                         '17\n'
637
                         'contents\nof\nfile\n'
638
                         'done\n',
639
                         from_server.getvalue())
640
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
641
    def test_pipe_like_stream_with_bulk_data(self):
642
        sample_request_bytes = 'command\n9\nbulk datadone\n'
643
        to_server = StringIO(sample_request_bytes)
644
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
645
        server = medium.SmartServerPipeStreamMedium(
646
            to_server, from_server, None)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
647
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
648
        server._serve_one_request(sample_protocol)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
649
        self.assertEqual('', from_server.getvalue())
650
        self.assertEqual(sample_request_bytes, sample_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
651
        self.assertFalse(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
652
653
    def test_socket_stream_with_bulk_data(self):
654
        sample_request_bytes = 'command\n9\nbulk datadone\n'
655
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
656
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
657
            server_sock, None)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
658
        sample_protocol = SampleRequest(expected_bytes=sample_request_bytes)
659
        client_sock.sendall(sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
660
        server._serve_one_request(sample_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
661
        server_sock.close()
662
        self.assertEqual('', client_sock.recv(1))
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
663
        self.assertEqual(sample_request_bytes, sample_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
664
        self.assertFalse(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
665
666
    def test_pipe_like_stream_shutdown_detection(self):
667
        to_server = StringIO('')
668
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
669
        server = medium.SmartServerPipeStreamMedium(to_server, from_server, None)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
670
        server._serve_one_request(SampleRequest('x'))
671
        self.assertTrue(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
672
        
673
    def test_socket_stream_shutdown_detection(self):
674
        server_sock, client_sock = self.portable_socket_pair()
675
        client_sock.close()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
676
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
677
            server_sock, None)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
678
        server._serve_one_request(SampleRequest('x'))
679
        self.assertTrue(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
680
        
681
    def test_pipe_like_stream_with_two_requests(self):
682
        # If two requests are read in one go, then two calls to
683
        # _serve_one_request should still process both of them as if they had
684
        # been received seperately.
685
        sample_request_bytes = 'command\n'
686
        to_server = StringIO(sample_request_bytes * 2)
687
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
688
        server = medium.SmartServerPipeStreamMedium(
689
            to_server, from_server, None)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
690
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
691
        server._serve_one_request(first_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
692
        self.assertEqual(0, first_protocol.next_read_size())
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
693
        self.assertEqual('', from_server.getvalue())
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
694
        self.assertFalse(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
695
        # Make a new protocol, call _serve_one_request with it to collect the
696
        # second request.
697
        second_protocol = SampleRequest(expected_bytes=sample_request_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
698
        server._serve_one_request(second_protocol)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
699
        self.assertEqual('', from_server.getvalue())
700
        self.assertEqual(sample_request_bytes, second_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
701
        self.assertFalse(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
702
        
703
    def test_socket_stream_with_two_requests(self):
704
        # If two requests are read in one go, then two calls to
705
        # _serve_one_request should still process both of them as if they had
706
        # been received seperately.
707
        sample_request_bytes = 'command\n'
708
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
709
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
710
            server_sock, None)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
711
        first_protocol = SampleRequest(expected_bytes=sample_request_bytes)
712
        # Put two whole requests on the wire.
713
        client_sock.sendall(sample_request_bytes * 2)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
714
        server._serve_one_request(first_protocol)
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
715
        self.assertEqual(0, first_protocol.next_read_size())
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
716
        self.assertFalse(server.finished)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
717
        # Make a new protocol, call _serve_one_request with it to collect the
718
        # second request.
719
        second_protocol = SampleRequest(expected_bytes=sample_request_bytes)
720
        stream_still_open = server._serve_one_request(second_protocol)
721
        self.assertEqual(sample_request_bytes, second_protocol.accepted_bytes)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
722
        self.assertFalse(server.finished)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
723
        server_sock.close()
724
        self.assertEqual('', client_sock.recv(1))
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
725
726
    def test_pipe_like_stream_error_handling(self):
727
        # Use plain python StringIO so we can monkey-patch the close method to
728
        # not discard the contents.
729
        from StringIO import StringIO
730
        to_server = StringIO('')
731
        from_server = StringIO()
732
        self.closed = False
733
        def close():
734
            self.closed = True
735
        from_server.close = close
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
736
        server = medium.SmartServerPipeStreamMedium(
737
            to_server, from_server, None)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
738
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
739
        server._serve_one_request(fake_protocol)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
740
        self.assertEqual('', from_server.getvalue())
741
        self.assertTrue(self.closed)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
742
        self.assertTrue(server.finished)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
743
        
744
    def test_socket_stream_error_handling(self):
745
        # Use plain python StringIO so we can monkey-patch the close method to
746
        # not discard the contents.
747
        from StringIO import StringIO
748
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
749
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
750
            server_sock, None)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
751
        fake_protocol = ErrorRaisingProtocol(Exception('boom'))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
752
        server._serve_one_request(fake_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
753
        # recv should not block, because the other end of the socket has been
754
        # closed.
755
        self.assertEqual('', client_sock.recv(1))
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
756
        self.assertTrue(server.finished)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
757
        
758
    def test_pipe_like_stream_keyboard_interrupt_handling(self):
759
        # Use plain python StringIO so we can monkey-patch the close method to
760
        # not discard the contents.
761
        to_server = StringIO('')
762
        from_server = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
763
        server = medium.SmartServerPipeStreamMedium(
764
            to_server, from_server, None)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
765
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
766
        self.assertRaises(
767
            KeyboardInterrupt, server._serve_one_request, fake_protocol)
768
        self.assertEqual('', from_server.getvalue())
769
770
    def test_socket_stream_keyboard_interrupt_handling(self):
771
        server_sock, client_sock = self.portable_socket_pair()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
772
        server = medium.SmartServerSocketStreamMedium(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
773
            server_sock, None)
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
774
        fake_protocol = ErrorRaisingProtocol(KeyboardInterrupt('boom'))
775
        self.assertRaises(
776
            KeyboardInterrupt, server._serve_one_request, fake_protocol)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
777
        server_sock.close()
778
        self.assertEqual('', client_sock.recv(1))
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
779
        
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
780
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
781
class TestSmartTCPServer(tests.TestCase):
782
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
783
    def test_get_error_unexpected(self):
784
        """Error reported by server with no specific representation"""
785
        class FlakyTransport(object):
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
786
            def get_bytes(self, path):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
787
                raise Exception("some random exception from inside server")
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
788
        smart_server = server.SmartTCPServer(backing_transport=FlakyTransport())
789
        smart_server.start_background_thread()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
790
        try:
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
791
            transport = remote.SmartTCPTransport(smart_server.get_url())
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
792
            try:
793
                transport.get('something')
794
            except errors.TransportError, e:
795
                self.assertContainsRe(str(e), 'some random exception')
796
            else:
797
                self.fail("get did not raise expected error")
798
        finally:
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
799
            smart_server.stop_background_thread()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
800
801
802
class SmartTCPTests(tests.TestCase):
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
803
    """Tests for connection/end to end behaviour using the TCP server.
804
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
805
    All of these tests are run with a server running on another thread serving
806
    a MemoryTransport, and a connection to it already open.
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
807
808
    the server is obtained by calling self.setUpServer(readonly=False).
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
809
    """
810
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
811
    def setUpServer(self, readonly=False):
812
        """Setup the server.
813
814
        :param readonly: Create a readonly server.
815
        """
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
816
        self.backing_transport = memory.MemoryTransport()
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
817
        if readonly:
818
            self.real_backing_transport = self.backing_transport
819
            self.backing_transport = get_transport("readonly+" + self.backing_transport.abspath('.'))
2018.5.14 by Andrew Bennetts
Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py.
820
        self.server = server.SmartTCPServer(self.backing_transport)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
821
        self.server.start_background_thread()
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
822
        self.transport = remote.SmartTCPTransport(self.server.get_url())
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
823
824
    def tearDown(self):
825
        if getattr(self, 'transport', None):
826
            self.transport.disconnect()
827
        if getattr(self, 'server', None):
828
            self.server.stop_background_thread()
829
        super(SmartTCPTests, self).tearDown()
830
        
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
831
832
class WritableEndToEndTests(SmartTCPTests):
833
    """Client to server tests that require a writable transport."""
834
835
    def setUp(self):
836
        super(WritableEndToEndTests, self).setUp()
837
        self.setUpServer()
838
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
839
    def test_start_tcp_server(self):
840
        url = self.server.get_url()
841
        self.assertContainsRe(url, r'^bzr://127\.0\.0\.1:[0-9]{2,}/')
842
843
    def test_smart_transport_has(self):
844
        """Checking for file existence over smart."""
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
845
        self.backing_transport.put_bytes("foo", "contents of foo\n")
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
846
        self.assertTrue(self.transport.has("foo"))
847
        self.assertFalse(self.transport.has("non-foo"))
848
849
    def test_smart_transport_get(self):
850
        """Read back a file over smart."""
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
851
        self.backing_transport.put_bytes("foo", "contents\nof\nfoo\n")
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
852
        fp = self.transport.get("foo")
853
        self.assertEqual('contents\nof\nfoo\n', fp.read())
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
854
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
855
    def test_get_error_enoent(self):
856
        """Error reported from server getting nonexistent file."""
1752.2.81 by Andrew Bennetts
Merge cleaned up underlying dependencies for remote bzrdir.
857
        # The path in a raised NoSuchFile exception should be the precise path
858
        # asked for by the client. This gives meaningful and unsurprising errors
859
        # for users.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
860
        try:
1752.2.80 by Andrew Bennetts
Some urlquoting fixes, a double-slash fix, a fix for an incorrect test (still failing though) and remove an apparently obsolete comment.
861
            self.transport.get('not%20a%20file')
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
862
        except errors.NoSuchFile, e:
1752.2.80 by Andrew Bennetts
Some urlquoting fixes, a double-slash fix, a fix for an incorrect test (still failing though) and remove an apparently obsolete comment.
863
            self.assertEqual('not%20a%20file', e.path)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
864
        else:
865
            self.fail("get did not raise expected error")
866
867
    def test_simple_clone_conn(self):
868
        """Test that cloning reuses the same connection."""
869
        # we create a real connection not a loopback one, but it will use the
870
        # same server and pipes
1752.2.74 by Andrew Bennetts
Make SmartTransport.clone return the right class, and move connection sharing into clone from __init__.
871
        conn2 = self.transport.clone('.')
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
872
        self.assertIs(self.transport._medium, conn2._medium)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
873
1910.19.12 by Andrew Bennetts
Activate a disabled test, rename another test to be consistent with what it's testing. (Andrew Bennetts, Robert Collins)
874
    def test__remote_path(self):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
875
        self.assertEquals('/foo/bar',
876
                          self.transport._remote_path('foo/bar'))
877
878
    def test_clone_changes_base(self):
879
        """Cloning transport produces one with a new base location"""
880
        conn2 = self.transport.clone('subdir')
881
        self.assertEquals(self.transport.base + 'subdir/',
882
                          conn2.base)
883
884
    def test_open_dir(self):
885
        """Test changing directory"""
886
        transport = self.transport
887
        self.backing_transport.mkdir('toffee')
888
        self.backing_transport.mkdir('toffee/apple')
889
        self.assertEquals('/toffee', transport._remote_path('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
890
        toffee_trans = transport.clone('toffee')
891
        # Check that each transport has only the contents of its directory
892
        # directly visible. If state was being held in the wrong object, it's
893
        # conceivable that cloning a transport would alter the state of the
894
        # cloned-from transport.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
895
        self.assertTrue(transport.has('toffee'))
1910.19.13 by Andrew Bennetts
Address various review comments.
896
        self.assertFalse(toffee_trans.has('toffee'))
897
        self.assertFalse(transport.has('apple'))
898
        self.assertTrue(toffee_trans.has('apple'))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
899
900
    def test_open_bzrdir(self):
901
        """Open an existing bzrdir over smart transport"""
902
        transport = self.transport
903
        t = self.backing_transport
904
        bzrdir.BzrDirFormat.get_default_format().initialize_on_transport(t)
905
        result_dir = bzrdir.BzrDir.open_containing_from_transport(transport)
906
907
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
908
class ReadOnlyEndToEndTests(SmartTCPTests):
909
    """Tests from the client to the server using a readonly backing transport."""
910
911
    def test_mkdir_error_readonly(self):
912
        """TransportNotPossible should be preserved from the backing transport."""
913
        self.setUpServer(readonly=True)
914
        self.assertRaises(errors.TransportNotPossible, self.transport.mkdir,
915
            'foo')
916
        
917
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
918
class SmartServerCommandTests(tests.TestCaseWithTransport):
919
    """Tests that call directly into the command objects, bypassing the network
920
    and the request dispatching.
921
    """
922
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
923
    def test_hello(self):
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
924
        cmd = request.HelloRequest(None)
925
        response = cmd.do()
926
        self.assertEqual(('ok', '1'), response.args)
927
        self.assertEqual(None, response.body)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
928
        
929
    def test_get_bundle(self):
930
        from bzrlib.bundle import serializer
931
        wt = self.make_branch_and_tree('.')
1910.19.13 by Andrew Bennetts
Address various review comments.
932
        self.build_tree_contents([('hello', 'hello world')])
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
933
        wt.add('hello')
1910.19.13 by Andrew Bennetts
Address various review comments.
934
        rev_id = wt.commit('add hello')
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
935
        
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
936
        cmd = request.GetBundleRequest(self.get_transport())
937
        response = cmd.do('.', rev_id)
938
        bundle = serializer.read_bundle(StringIO(response.body))
939
        self.assertEqual((), response.args)
940
941
942
class SmartServerRequestHandlerTests(tests.TestCaseWithTransport):
943
    """Test that call directly into the handler logic, bypassing the network."""
944
945
    def build_handler(self, transport):
946
        """Returns a handler for the commands in protocol version one."""
947
        return smart.SmartServerRequestHandler(transport, vfs.vfs_commands)
948
949
    def test_construct_request_handler(self):
950
        """Constructing a request handler should be easy and set defaults."""
951
        handler = smart.SmartServerRequestHandler(None, None)
952
        self.assertFalse(handler.finished_reading)
953
954
    def test_hello(self):
955
        handler = self.build_handler(None)
956
        handler.dispatch_command('hello', ())
957
        self.assertEqual(('ok', '1'), handler.response.args)
958
        self.assertEqual(None, handler.response.body)
959
        
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
960
    def test_readonly_exception_becomes_transport_not_possible(self):
961
        """The response for a read-only error is ('ReadOnlyError')."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
962
        handler = self.build_handler(self.get_readonly_transport())
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
963
        # send a mkdir for foo, with no explicit mode - should fail.
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
964
        handler.dispatch_command('mkdir', ('foo', ''))
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
965
        # and the failure should be an explicit ReadOnlyError
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
966
        self.assertEqual(("ReadOnlyError", ), handler.response.args)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
967
        # XXX: TODO: test that other TransportNotPossible errors are
968
        # presented as TransportNotPossible - not possible to do that
969
        # until I figure out how to trigger that relatively cleanly via
970
        # the api. RBC 20060918
971
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
972
    def test_hello_has_finished_body_on_dispatch(self):
973
        """The 'hello' command should set finished_reading."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
974
        handler = self.build_handler(None)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
975
        handler.dispatch_command('hello', ())
976
        self.assertTrue(handler.finished_reading)
977
        self.assertNotEqual(None, handler.response)
978
979
    def test_put_bytes_non_atomic(self):
980
        """'put_...' should set finished_reading after reading the bytes."""
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
981
        handler = self.build_handler(self.get_transport())
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
982
        handler.dispatch_command('put_non_atomic', ('a-file', '', 'F', ''))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
983
        self.assertFalse(handler.finished_reading)
984
        handler.accept_body('1234')
985
        self.assertFalse(handler.finished_reading)
986
        handler.accept_body('5678')
987
        handler.end_of_body()
988
        self.assertTrue(handler.finished_reading)
989
        self.assertEqual(('ok', ), handler.response.args)
990
        self.assertEqual(None, handler.response.body)
991
        
992
    def test_readv_accept_body(self):
993
        """'readv' should set finished_reading after reading offsets."""
994
        self.build_tree(['a-file'])
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
995
        handler = self.build_handler(self.get_readonly_transport())
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
996
        handler.dispatch_command('readv', ('a-file', ))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
997
        self.assertFalse(handler.finished_reading)
998
        handler.accept_body('2,')
999
        self.assertFalse(handler.finished_reading)
1000
        handler.accept_body('3')
1001
        handler.end_of_body()
1002
        self.assertTrue(handler.finished_reading)
1003
        self.assertEqual(('readv', ), handler.response.args)
1004
        # co - nte - nt of a-file is the file contents we are extracting from.
1005
        self.assertEqual('nte', handler.response.body)
1006
1007
    def test_readv_short_read_response_contents(self):
1008
        """'readv' when a short read occurs sets the response appropriately."""
1009
        self.build_tree(['a-file'])
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1010
        handler = self.build_handler(self.get_readonly_transport())
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
1011
        handler.dispatch_command('readv', ('a-file', ))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1012
        # read beyond the end of the file.
1013
        handler.accept_body('100,1')
1014
        handler.end_of_body()
1015
        self.assertTrue(handler.finished_reading)
1016
        self.assertEqual(('ShortReadvError', 'a-file', '100', '1', '0'),
1017
            handler.response.args)
1018
        self.assertEqual(None, handler.response.body)
1019
1752.2.73 by Andrew Bennetts
Define (and register as bzr+ssh://) SmartSSHTransport, factor out an SSHSubprocess from SFTPSubprocess, and make SmartTransport connect lazily rather than in the constructor.
1020
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1021
class RemoteTransportRegistration(tests.TestCase):
1752.2.73 by Andrew Bennetts
Define (and register as bzr+ssh://) SmartSSHTransport, factor out an SSHSubprocess from SFTPSubprocess, and make SmartTransport connect lazily rather than in the constructor.
1022
1023
    def test_registration(self):
1024
        t = get_transport('bzr+ssh://example.com/path')
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1025
        self.assertIsInstance(t, remote.SmartSSHTransport)
1752.2.73 by Andrew Bennetts
Define (and register as bzr+ssh://) SmartSSHTransport, factor out an SSHSubprocess from SFTPSubprocess, and make SmartTransport connect lazily rather than in the constructor.
1026
        self.assertEqual('example.com', t._host)
1027
1028
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1029
class TestRemoteTransport(tests.TestCase):
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1030
        
1031
    def test_use_connection_factory(self):
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1032
        # We want to be able to pass a client as a parameter to RemoteTransport.
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1033
        input = StringIO("ok\n3\nbardone\n")
1034
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1035
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1036
        transport = remote.RemoteTransport(
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1037
            'bzr://localhost/', medium=client_medium)
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1038
1039
        # We want to make sure the client is used when the first remote
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1040
        # method is called.  No data should have been sent, or read.
1041
        self.assertEqual(0, input.tell())
1042
        self.assertEqual('', output.getvalue())
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1043
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1044
        # Now call a method that should result in a single request : as the
1045
        # transport makes its own protocol instances, we check on the wire.
1046
        # XXX: TODO: give the transport a protocol factory, which can make
1047
        # an instrumented protocol for us.
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1048
        self.assertEqual('bar', transport.get_bytes('foo'))
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1049
        # only the needed data should have been sent/received.
1050
        self.assertEqual(13, input.tell())
1051
        self.assertEqual('get\x01/foo\n', output.getvalue())
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1052
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1053
    def test__translate_error_readonly(self):
1054
        """Sending a ReadOnlyError to _translate_error raises TransportNotPossible."""
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1055
        client_medium = medium.SmartClientMedium()
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1056
        transport = remote.RemoteTransport(
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1057
            'bzr://localhost/', medium=client_medium)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1058
        self.assertRaises(errors.TransportNotPossible,
1059
            transport._translate_error, ("ReadOnlyError", ))
1060
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
1061
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1062
class InstrumentedServerProtocol(medium.SmartServerStreamMedium):
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1063
    """A smart server which is backed by memory and saves its write requests."""
1064
1065
    def __init__(self, write_output_list):
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1066
        medium.SmartServerStreamMedium.__init__(self, memory.MemoryTransport())
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1067
        self._write_output_list = write_output_list
1068
1069
1070
class TestSmartProtocol(tests.TestCase):
1071
    """Tests for the smart protocol.
1072
1073
    Each test case gets a smart_server and smart_client created during setUp().
1074
1075
    It is planned that the client can be called with self.call_client() giving
1076
    it an expected server response, which will be fed into it when it tries to
1077
    read. Likewise, self.call_server will call a servers method with a canned
1078
    serialised client request. Output done by the client or server for these
1079
    calls will be captured to self.to_server and self.to_client. Each element
1080
    in the list is a write call from the client or server respectively.
1081
    """
1082
1083
    def setUp(self):
1084
        super(TestSmartProtocol, self).setUp()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1085
        self.server_to_client = []
1086
        self.to_server = StringIO()
1087
        self.to_client = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1088
        self.client_medium = medium.SmartSimplePipesClientMedium(self.to_client,
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1089
            self.to_server)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1090
        self.client_protocol = protocol.SmartClientRequestProtocolOne(
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1091
            self.client_medium)
1092
        self.smart_server = InstrumentedServerProtocol(self.server_to_client)
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1093
        self.smart_server_request = smart.SmartServerRequestHandler(
1094
            None, vfs.vfs_commands)
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1095
1096
    def assertOffsetSerialisation(self, expected_offsets, expected_serialised,
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1097
        client):
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1098
        """Check that smart (de)serialises offsets as expected.
1099
        
1100
        We check both serialisation and deserialisation at the same time
1101
        to ensure that the round tripping cannot skew: both directions should
1102
        be as expected.
1103
        
1104
        :param expected_offsets: a readv offset list.
1105
        :param expected_seralised: an expected serial form of the offsets.
1106
        """
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1107
        # XXX: '_deserialise_offsets' should be a method of the
1108
        # SmartServerRequestProtocol in future.
1109
        readv_cmd = vfs.ReadvCommand(None)
1110
        offsets = readv_cmd._deserialise_offsets(expected_serialised)
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1111
        self.assertEqual(expected_offsets, offsets)
1112
        serialised = client._serialise_offsets(offsets)
1113
        self.assertEqual(expected_serialised, serialised)
1114
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1115
    def build_protocol_waiting_for_body(self):
1116
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1117
        smart_protocol = protocol.SmartServerRequestProtocolOne(None,
1118
                out_stream.write)
1119
        smart_protocol.has_dispatched = True
2018.5.17 by Andrew Bennetts
Paramaterise the commands handled by SmartServerRequestHandler.
1120
        smart_protocol.request = self.smart_server_request
2018.5.7 by Andrew Bennetts
Simplify dispatch_command.
1121
        class FakeCommand(object):
1122
            def do_body(cmd, body_bytes):
1123
                self.end_received = True
1124
                self.assertEqual('abcdefg', body_bytes)
2018.5.16 by Andrew Bennetts
Move SmartServerResponse to smart/request.py, untangling more import dependencies.
1125
                return request.SmartServerResponse(('ok', ))
2018.5.12 by Andrew Bennetts
Rename SmartServerRequestHandler's command attribute to _command; it's private.
1126
        smart_protocol.request._command = FakeCommand()
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1127
        # Call accept_bytes to make sure that internal state like _body_decoder
1128
        # is initialised.  This test should probably be given a clearer
1129
        # interface to work with that will not cause this inconsistency.
1130
        #   -- Andrew Bennetts, 2006-09-28
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1131
        smart_protocol.accept_bytes('')
1132
        return smart_protocol
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1133
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1134
    def test_construct_version_one_server_protocol(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1135
        smart_protocol = protocol.SmartServerRequestProtocolOne(None, None)
1136
        self.assertEqual('', smart_protocol.excess_buffer)
1137
        self.assertEqual('', smart_protocol.in_buffer)
1138
        self.assertFalse(smart_protocol.has_dispatched)
1139
        self.assertEqual(1, smart_protocol.next_read_size())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1140
1141
    def test_construct_version_one_client_protocol(self):
1142
        # we can construct a client protocol from a client medium request
1143
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1144
        client_medium = medium.SmartSimplePipesClientMedium(None, output)
1145
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1146
        client_protocol = protocol.SmartClientRequestProtocolOne(request)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1147
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1148
    def test_server_offset_serialisation(self):
1149
        """The Smart protocol serialises offsets as a comma and \n string.
1150
1151
        We check a number of boundary cases are as expected: empty, one offset,
1152
        one with the order of reads not increasing (an out of order read), and
1153
        one that should coalesce.
1154
        """
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1155
        self.assertOffsetSerialisation([], '', self.client_protocol)
1156
        self.assertOffsetSerialisation([(1,2)], '1,2', self.client_protocol)
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1157
        self.assertOffsetSerialisation([(10,40), (0,5)], '10,40\n0,5',
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1158
            self.client_protocol)
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
1159
        self.assertOffsetSerialisation([(1,2), (3,4), (100, 200)],
2018.5.4 by Andrew Bennetts
Split smart server VFS logic out into a new file, and start using the command pattern in the SmartServerRequestHandler.
1160
            '1,2\n3,4\n100,200', self.client_protocol)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1161
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
1162
    def test_accept_bytes_of_bad_request_to_protocol(self):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1163
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1164
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1165
            None, out_stream.write)
1166
        smart_protocol.accept_bytes('abc')
1167
        self.assertEqual('abc', smart_protocol.in_buffer)
1168
        smart_protocol.accept_bytes('\n')
1169
        self.assertEqual(
1170
            "error\x01Generic bzr smart protocol error: bad request 'abc'\n",
1171
            out_stream.getvalue())
1172
        self.assertTrue(smart_protocol.has_dispatched)
1173
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
1174
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1175
    def test_accept_body_bytes_to_protocol(self):
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1176
        protocol = self.build_protocol_waiting_for_body()
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1177
        self.assertEqual(6, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1178
        protocol.accept_bytes('7\nabc')
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1179
        self.assertEqual(9, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1180
        protocol.accept_bytes('defgd')
1181
        protocol.accept_bytes('one\n')
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1182
        self.assertEqual(0, protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1183
        self.assertTrue(self.end_received)
1184
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1185
    def test_accept_request_and_body_all_at_once(self):
1186
        mem_transport = memory.MemoryTransport()
1187
        mem_transport.put_bytes('foo', 'abcdefghij')
1188
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1189
        smart_protocol = protocol.SmartServerRequestProtocolOne(mem_transport,
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
1190
                out_stream.write)
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1191
        smart_protocol.accept_bytes('readv\x01foo\n3\n3,3done\n')
1192
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1193
        self.assertEqual('readv\n3\ndefdone\n', out_stream.getvalue())
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1194
        self.assertEqual('', smart_protocol.excess_buffer)
1195
        self.assertEqual('', smart_protocol.in_buffer)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1196
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1197
    def test_accept_excess_bytes_are_preserved(self):
1198
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1199
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1200
            None, out_stream.write)
1201
        smart_protocol.accept_bytes('hello\nhello\n')
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1202
        self.assertEqual("ok\x011\n", out_stream.getvalue())
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1203
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1204
        self.assertEqual("", smart_protocol.in_buffer)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1205
1206
    def test_accept_excess_bytes_after_body(self):
1207
        protocol = self.build_protocol_waiting_for_body()
1208
        protocol.accept_bytes('7\nabcdefgdone\nX')
1209
        self.assertTrue(self.end_received)
1210
        self.assertEqual("X", protocol.excess_buffer)
1211
        self.assertEqual("", protocol.in_buffer)
1212
        protocol.accept_bytes('Y')
1213
        self.assertEqual("XY", protocol.excess_buffer)
1214
        self.assertEqual("", protocol.in_buffer)
1215
1216
    def test_accept_excess_bytes_after_dispatch(self):
1217
        out_stream = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1218
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1219
            None, out_stream.write)
1220
        smart_protocol.accept_bytes('hello\n')
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1221
        self.assertEqual("ok\x011\n", out_stream.getvalue())
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1222
        smart_protocol.accept_bytes('hel')
1223
        self.assertEqual("hel", smart_protocol.excess_buffer)
1224
        smart_protocol.accept_bytes('lo\n')
1225
        self.assertEqual("hello\n", smart_protocol.excess_buffer)
1226
        self.assertEqual("", smart_protocol.in_buffer)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1227
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
1228
    def test__send_response_sets_finished_reading(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1229
        smart_protocol = protocol.SmartServerRequestProtocolOne(
1230
            None, lambda x: None)
1231
        self.assertEqual(1, smart_protocol.next_read_size())
1232
        smart_protocol._send_response(('x',))
1233
        self.assertEqual(0, smart_protocol.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1234
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1235
    def test_query_version(self):
1236
        """query_version on a SmartClientProtocolOne should return a number.
1237
        
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1238
        The protocol provides the query_version because the domain level clients
1239
        may all need to be able to probe for capabilities.
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1240
        """
1241
        # What we really want to test here is that SmartClientProtocolOne calls
1242
        # accept_bytes(tuple_based_encoding_of_hello) and reads and parses the
1243
        # response of tuple-encoded (ok, 1).  Also, seperately we should test
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1244
        # the error if the response is a non-understood version.
1245
        input = StringIO('ok\x011\n')
1246
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1247
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1248
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1249
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1250
        self.assertEqual(1, smart_protocol.query_version())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1251
1252
    def assertServerToClientEncoding(self, expected_bytes, expected_tuple,
1253
            input_tuples):
1254
        """Assert that each input_tuple serialises as expected_bytes, and the
1255
        bytes deserialise as expected_tuple.
1256
        """
1257
        # check the encoding of the server for all input_tuples matches
1258
        # expected bytes
1259
        for input_tuple in input_tuples:
1260
            server_output = StringIO()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1261
            server_protocol = protocol.SmartServerRequestProtocolOne(
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
1262
                None, server_output.write)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1263
            server_protocol._send_response(input_tuple)
1264
            self.assertEqual(expected_bytes, server_output.getvalue())
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1265
        # check the decoding of the client smart_protocol from expected_bytes:
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1266
        input = StringIO(expected_bytes)
1267
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1268
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1269
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1270
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1271
        smart_protocol.call('foo')
1272
        self.assertEqual(expected_tuple, smart_protocol.read_response_tuple())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1273
1274
    def test_client_call_empty_response(self):
1275
        # protocol.call() can get back an empty tuple as a response. This occurs
1276
        # when the parsed line is an empty line, and results in a tuple with
1277
        # one element - an empty string.
1278
        self.assertServerToClientEncoding('\n', ('', ), [(), ('', )])
1279
1280
    def test_client_call_three_element_response(self):
1281
        # protocol.call() can get back tuples of other lengths. A three element
1282
        # tuple should be unpacked as three strings.
1283
        self.assertServerToClientEncoding('a\x01b\x0134\n', ('a', 'b', '34'),
1284
            [('a', 'b', '34')])
1285
1286
    def test_client_call_with_body_bytes_uploads(self):
1287
        # protocol.call_with_upload should length-prefix the bytes onto the 
1288
        # wire.
1289
        expected_bytes = "foo\n7\nabcdefgdone\n"
1290
        input = StringIO("\n")
1291
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1292
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1293
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1294
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1295
        smart_protocol.call_with_body_bytes(('foo', ), "abcdefg")
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1296
        self.assertEqual(expected_bytes, output.getvalue())
1297
1298
    def test_client_call_with_body_readv_array(self):
1299
        # protocol.call_with_upload should encode the readv array and then
1300
        # length-prefix the bytes onto the wire.
1301
        expected_bytes = "foo\n7\n1,2\n5,6done\n"
1302
        input = StringIO("\n")
1303
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1304
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1305
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1306
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1307
        smart_protocol.call_with_body_readv_array(('foo', ), [(1,2),(5,6)])
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1308
        self.assertEqual(expected_bytes, output.getvalue())
1309
1310
    def test_client_read_body_bytes_all(self):
1311
        # read_body_bytes should decode the body bytes from the wire into
1312
        # a response.
1313
        expected_bytes = "1234567"
1314
        server_bytes = "ok\n7\n1234567done\n"
1315
        input = StringIO(server_bytes)
1316
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1317
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1318
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1319
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1320
        smart_protocol.call('foo')
1321
        smart_protocol.read_response_tuple(True)
1322
        self.assertEqual(expected_bytes, smart_protocol.read_body_bytes())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1323
1324
    def test_client_read_body_bytes_incremental(self):
1325
        # test reading a few bytes at a time from the body
1326
        # XXX: possibly we should test dribbling the bytes into the stringio
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1327
        # to make the state machine work harder: however, as we use the
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1328
        # LengthPrefixedBodyDecoder that is already well tested - we can skip
1329
        # that.
1330
        expected_bytes = "1234567"
1331
        server_bytes = "ok\n7\n1234567done\n"
1332
        input = StringIO(server_bytes)
1333
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1334
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1335
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1336
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1337
        smart_protocol.call('foo')
1338
        smart_protocol.read_response_tuple(True)
1339
        self.assertEqual(expected_bytes[0:2], smart_protocol.read_body_bytes(2))
1340
        self.assertEqual(expected_bytes[2:4], smart_protocol.read_body_bytes(2))
1341
        self.assertEqual(expected_bytes[4:6], smart_protocol.read_body_bytes(2))
1342
        self.assertEqual(expected_bytes[6], smart_protocol.read_body_bytes())
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1343
1344
    def test_client_cancel_read_body_does_not_eat_body_bytes(self):
1345
        # cancelling the expected body needs to finish the request, but not
1346
        # read any more bytes.
1347
        expected_bytes = "1234567"
1348
        server_bytes = "ok\n7\n1234567done\n"
1349
        input = StringIO(server_bytes)
1350
        output = StringIO()
2018.5.2 by Andrew Bennetts
Start splitting bzrlib/transport/smart.py into a package.
1351
        client_medium = medium.SmartSimplePipesClientMedium(input, output)
1352
        request = client_medium.get_request()
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1353
        smart_protocol = protocol.SmartClientRequestProtocolOne(request)
1354
        smart_protocol.call('foo')
1355
        smart_protocol.read_response_tuple(True)
1356
        smart_protocol.cancel_read_body()
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1357
        self.assertEqual(3, input.tell())
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1358
        self.assertRaises(
1359
            errors.ReadingCompleted, smart_protocol.read_body_bytes)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1360
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
1361
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1362
class LengthPrefixedBodyDecoder(tests.TestCase):
1363
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1364
    # XXX: TODO: make accept_reading_trailer invoke translate_response or 
1365
    # something similar to the ProtocolBase method.
1366
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1367
    def test_construct(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1368
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1369
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1370
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1371
        self.assertEqual('', decoder.read_pending_data())
1372
        self.assertEqual('', decoder.unused_data)
1373
1374
    def test_accept_bytes(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1375
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1376
        decoder.accept_bytes('')
1377
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1378
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1379
        self.assertEqual('', decoder.read_pending_data())
1380
        self.assertEqual('', decoder.unused_data)
1381
        decoder.accept_bytes('7')
1382
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1383
        self.assertEqual(6, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1384
        self.assertEqual('', decoder.read_pending_data())
1385
        self.assertEqual('', decoder.unused_data)
1386
        decoder.accept_bytes('\na')
1387
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1388
        self.assertEqual(11, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1389
        self.assertEqual('a', decoder.read_pending_data())
1390
        self.assertEqual('', decoder.unused_data)
1391
        decoder.accept_bytes('bcdefgd')
1392
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1393
        self.assertEqual(4, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1394
        self.assertEqual('bcdefg', decoder.read_pending_data())
1395
        self.assertEqual('', decoder.unused_data)
1396
        decoder.accept_bytes('one')
1397
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1398
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1399
        self.assertEqual('', decoder.read_pending_data())
1400
        self.assertEqual('', decoder.unused_data)
1401
        decoder.accept_bytes('\nblarg')
1402
        self.assertTrue(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1403
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1404
        self.assertEqual('', decoder.read_pending_data())
1405
        self.assertEqual('blarg', decoder.unused_data)
1406
        
1407
    def test_accept_bytes_all_at_once_with_excess(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1408
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1409
        decoder.accept_bytes('1\nadone\nunused')
1410
        self.assertTrue(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1411
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1412
        self.assertEqual('a', decoder.read_pending_data())
1413
        self.assertEqual('unused', decoder.unused_data)
1414
1415
    def test_accept_bytes_exact_end_of_body(self):
2018.5.3 by Andrew Bennetts
Split up more smart server code, this time into bzrlib/transport/smart/protocol.py
1416
        decoder = protocol.LengthPrefixedBodyDecoder()
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1417
        decoder.accept_bytes('1\na')
1418
        self.assertFalse(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1419
        self.assertEqual(5, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1420
        self.assertEqual('a', decoder.read_pending_data())
1421
        self.assertEqual('', decoder.unused_data)
1422
        decoder.accept_bytes('done\n')
1423
        self.assertTrue(decoder.finished_reading)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1424
        self.assertEqual(1, decoder.next_read_size())
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1425
        self.assertEqual('', decoder.read_pending_data())
1426
        self.assertEqual('', decoder.unused_data)
1427
1428
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1429
class FakeHTTPMedium(object):
1430
    def __init__(self):
1431
        self.written_request = None
1432
        self._current_request = None
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
1433
    def send_http_smart_request(self, bytes):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1434
        self.written_request = bytes
1435
        return None
1436
1437
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1438
class HTTPTunnellingSmokeTest(tests.TestCaseWithTransport):
1439
    
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1440
    def _test_bulk_data(self, url_protocol):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1441
        # We should be able to send and receive bulk data in a single message.
1442
        # The 'readv' command in the smart protocol both sends and receives bulk
1443
        # data, so we use that.
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1444
        self.build_tree(['data-file'])
1445
        http_server = HTTPServerWithSmarts()
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1446
        http_server._url_protocol = url_protocol
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1447
        http_server.setUp()
1448
        self.addCleanup(http_server.tearDown)
1449
1450
        http_transport = get_transport(http_server.get_url())
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1451
1452
        medium = http_transport.get_smart_medium()
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1453
        #remote_transport = RemoteTransport('fake_url', medium)
2018.5.20 by Andrew Bennetts
Move bzrlib/transport/smart/_smart.py to bzrlib/transport/remote.py and rename SmartTransport to RemoteTransport (Robert Collins, Andrew Bennetts)
1454
        remote_transport = remote.RemoteTransport('/', medium=medium)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1455
        self.assertEqual(
1456
            [(0, "c")], list(remote_transport.readv("data-file", [(0,1)])))
1457
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1458
    def test_bulk_data_pycurl(self):
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
1459
        try:
1460
            self._test_bulk_data('http+pycurl')
1461
        except errors.UnsupportedProtocol, e:
1462
            raise tests.TestSkipped(str(e))
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1463
    
1464
    def test_bulk_data_urllib(self):
1465
        self._test_bulk_data('http+urllib')
1466
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1467
    def test_smart_http_medium_request_accept_bytes(self):
1468
        medium = FakeHTTPMedium()
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
1469
        request = SmartClientHTTPMediumRequest(medium)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1470
        request.accept_bytes('abc')
1471
        request.accept_bytes('def')
1472
        self.assertEqual(None, medium.written_request)
1473
        request.finished_writing()
1474
        self.assertEqual('abcdef', medium.written_request)
1475
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1476
    def _test_http_send_smart_request(self, url_protocol):
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1477
        http_server = HTTPServerWithSmarts()
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1478
        http_server._url_protocol = url_protocol
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1479
        http_server.setUp()
1480
        self.addCleanup(http_server.tearDown)
1481
1482
        post_body = 'hello\n'
1483
        expected_reply_body = 'ok\x011\n'
1484
1485
        http_transport = get_transport(http_server.get_url())
1486
        medium = http_transport.get_smart_medium()
2018.2.8 by Andrew Bennetts
Make HttpTransportBase.get_smart_client return self again.
1487
        response = medium.send_http_smart_request(post_body)
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1488
        reply_body = response.read()
1489
        self.assertEqual(expected_reply_body, reply_body)
1490
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1491
    def test_http_send_smart_request_pycurl(self):
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
1492
        try:
1493
            self._test_http_send_smart_request('http+pycurl')
1494
        except errors.UnsupportedProtocol, e:
1495
            raise tests.TestSkipped(str(e))
2018.2.7 by Andrew Bennetts
Implement _post on HttpTransport_urllib.
1496
1497
    def test_http_send_smart_request_urllib(self):
1498
        self._test_http_send_smart_request('http+urllib')
1499
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1500
    def test_http_server_with_smarts(self):
1501
        http_server = HTTPServerWithSmarts()
1502
        http_server.setUp()
1503
        self.addCleanup(http_server.tearDown)
1504
1505
        post_body = 'hello\n'
1506
        expected_reply_body = 'ok\x011\n'
1507
1508
        smart_server_url = http_server.get_url() + '.bzr/smart'
1509
        reply = urllib2.urlopen(smart_server_url, post_body).read()
1510
1511
        self.assertEqual(expected_reply_body, reply)
1512
2018.2.6 by Andrew Bennetts
HTTP client starting to work (pycurl for the moment).
1513
    def test_smart_http_server_post_request_handler(self):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
1514
        http_server = HTTPServerWithSmarts()
1515
        http_server.setUp()
1516
        self.addCleanup(http_server.tearDown)
1517
        httpd = http_server._get_httpd()
1518
1519
        socket = SampleSocket(
1520
            'POST /.bzr/smart HTTP/1.0\r\n'
1521
            # HTTP/1.0 posts must have a Content-Length.
1522
            'Content-Length: 6\r\n'
1523
            '\r\n'
1524
            'hello\n')
1525
        request_handler = SmartRequestHandler(
1526
            socket, ('localhost', 80), httpd)
1527
        response = socket.writefile.getvalue()
1528
        self.assertStartsWith(response, 'HTTP/1.0 200 ')
1529
        # This includes the end of the HTTP headers, and all the body.
1530
        expected_end_of_response = '\r\n\r\nok\x011\n'
1531
        self.assertEndsWith(response, expected_end_of_response)
1532
1533
1534
class SampleSocket(object):
1535
    """A socket-like object for use in testing the HTTP request handler."""
1536
    
1537
    def __init__(self, socket_read_content):
1538
        """Constructs a sample socket.
1539
1540
        :param socket_read_content: a byte sequence
1541
        """
1542
        # Use plain python StringIO so we can monkey-patch the close method to
1543
        # not discard the contents.
1544
        from StringIO import StringIO
1545
        self.readfile = StringIO(socket_read_content)
1546
        self.writefile = StringIO()
1547
        self.writefile.close = lambda: None
1548
        
1549
    def makefile(self, mode='r', bufsize=None):
1550
        if 'r' in mode:
1551
            return self.readfile
1552
        else:
1553
            return self.writefile
1554
1555
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1556
# TODO: Client feature that does get_bundle and then installs that into a
1557
# branch; this can be used in place of the regular pull/fetch operation when
1558
# coming from a smart server.
1559
#
1560
# TODO: Eventually, want to do a 'branch' command by fetching the whole
1561
# history as one big bundle.  How?  
1562
#
1563
# The branch command does 'br_from.sprout', which tries to preserve the same
1564
# format.  We don't necessarily even want that.  
1565
#
1566
# It might be simpler to handle cmd_pull first, which does a simpler fetch()
1567
# operation from one branch into another.  It already has some code for
1568
# pulling from a bundle, which it does by trying to see if the destination is
1569
# a bundle file.  So it seems the logic for pull ought to be:
1570
# 
1571
#  - if it's a smart server, get a bundle from there and install that
1572
#  - if it's a bundle, install that
1573
#  - if it's a branch, pull from there
1574
#
1575
# Getting a bundle from a smart server is a bit different from reading a
1576
# bundle from a URL:
1577
#
1578
#  - we can reasonably remember the URL we last read from 
1579
#  - you can specify a revision number to pull, and we need to pass it across
1580
#    to the server as a limit on what will be requested
1581
#
1582
# TODO: Given a URL, determine whether it is a smart server or not (or perhaps
1583
# otherwise whether it's a bundle?)  Should this be a property or method of
1584
# the transport?  For the ssh protocol, we always know it's a smart server.
1585
# For http, we potentially need to probe.  But if we're explicitly given
1586
# bzr+http:// then we can skip that for now.